Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
7c291bb
fix(webview-message-handler): enforce workspace containment for markd…
easonLiangWorldedtech Sep 26, 2026
673ab1f
feat: inline chat model selector (split from chat-input-ux)
Sep 26, 2026
2f6b25d
test(webview): cover model selector provider/transform/clear/early-re…
Sep 27, 2026
f36f9a7
test(e2e): regenerate electron chat-dark sidebar snapshot from pr/cha…
Sep 27, 2026
3c09f17
refactor(code-index): separate service factories and embedder validat…
WebMad Sep 28, 2026
d0dec4b
fix(task): keep the first abort reason (RSK-19) (#1811)
edelauna Sep 28, 2026
2225856
fix: reset didFinishAbortingStream for each API request (#1801) (#1812)
edelauna Sep 28, 2026
d351a15
refactor(code-index): introduce workspace scope behind registry (#1766)
WebMad Sep 28, 2026
8bec7c1
refactor(code-index): extract scan execution without behavior changes…
WebMad Sep 28, 2026
778ad3e
refactor(code-index): scope state ownership and workspace status deli…
WebMad Sep 28, 2026
34779f9
Merge branch 'main' into pr/chat-model-selector
seeones Sep 29, 2026
91f50a4
fix(chat): harden inline model selector (allow-list, a11y, Z.AI line,…
Sep 29, 2026
4d29244
test(chat): strengthen inline model selector coverage
Sep 29, 2026
78ca407
i18n: localize selectModel across locales
Sep 29, 2026
3d4b1e8
fix(webview): unify profile validation boundary and harden model requ…
Sep 29, 2026
240c521
refactor(chat): trim executable lines under the mutation-diff gate
Sep 29, 2026
8a6170b
test(webview): cover profile validation, request identity and cleanup
Sep 29, 2026
07f0de2
fix(webview): map zoo-gateway model id for allow-list and clarify sig…
Sep 29, 2026
e277ab9
refactor(code-index): extract single-file preparation (#1836)
WebMad Sep 29, 2026
a312415
fix(webview): close allow-list fail-open and make profile activation …
Sep 29, 2026
2da6ea2
fix: streaming tool-call argument loss in NativeToolCallParser (#695)…
awschmeder Sep 30, 2026
ce8a1ba
fix(webview): never delete an existing profile on an unconfirmed read…
Sep 30, 2026
0f75a60
fix(vscode-lm): window-safe middle-out truncation of tool_result cont…
simurg79 Sep 30, 2026
056509a
Merge branch 'main' into pr/chat-model-selector
seeones Sep 30, 2026
bf3bc78
refactor(code-index): route workspace actions through scopes (#1778)
WebMad Sep 30, 2026
6daf6e7
Merge branch 'main' into pr/chat-model-selector
seeones Sep 30, 2026
0b7cd10
refactor(code-index): use immutable configuration snapshots (#1815)
WebMad Sep 30, 2026
ceceb08
feat(openai): add GPT-6.1 Sol support (#1864)
WebMad Sep 30, 2026
9a2c3fb
[Feat] Add community-approved label for community code approvals (#1873)
zoomote[bot] Oct 1, 2026
5bb5191
chore(deps): update dependency lru-cache to v11.5.3 (#1583)
renovate[bot] Oct 1, 2026
0b6c418
fix(dev): use 127.0.0.1 instead of localhost for dev server (IPv6 res…
hnbdr Oct 1, 2026
c6e6ee3
fix(webview): batch repeated tool preambles (#1658)
PierrunoYT Oct 1, 2026
918e77e
Merge branch 'main' into pr/chat-model-selector
seeones Oct 1, 2026
82e3f21
fix(mcp): validate native mcp_tool_use dispatch through the shared to…
DaubnerF Oct 1, 2026
0aa636c
fix(webview): stop flushing throttled state for partial say messages …
daewoongoh Oct 1, 2026
c38db3e
fix(anthropic): correct Opus 5.5 output budget (#1854)
jaszhix Oct 2, 2026
be2de34
chore(deps): update dependency mocha to v11.8.0 (#1585)
renovate[bot] Oct 2, 2026
feb3386
fix(opencode-go): add DeepSeek V4.1 Flash metadata (#1659)
PierrunoYT Oct 2, 2026
91bf671
Merge branch 'main' into pr/chat-model-selector
seeones Oct 2, 2026
0bbbc5f
fix: restore profile mappings on activation failure, clean up failed …
coderabbitai[bot] Oct 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 127 additions & 3 deletions .github/workflows/label-pr-review-state.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ on:
# This workflow only reads PR metadata and never checks out or executes PR code.
# pull_request_target gives fork PRs a token that can update labels and comments.
pull_request_target:
types: [opened, reopened, ready_for_review, synchronize, review_requested, review_request_removed, labeled, unlabeled]
types: [opened, reopened, ready_for_review, converted_to_draft, synchronize, review_requested, review_request_removed, labeled, unlabeled]
pull_request_review:
types: [submitted, dismissed]
# Fork review events have a read-only token. CodeRabbit's status-comment update
Expand Down Expand Up @@ -81,13 +81,21 @@ jobs:
color: '5319e7',
description: 'Required CI passed; CodeRabbit review is active',
},
{
name: 'community-approved',
color: 'c2e0c6',
description: 'Fresh community approval on the current head; maintainer review still required',
},
];
const guideMarker = '<!-- zoo-code-pr-review-process -->';
const codeRabbitLabelMarkerPrefix = '<!-- coderabbit-review-label:';
const codeRabbitLogin = 'coderabbitai[bot]';
const codeRabbitLogins = new Set([codeRabbitLogin, 'coderabbitai']);
const codeRabbitActiveLabel = 'coderabbit-review-active';
const codeRabbitEligibleBotLogins = new Set(['zoomote[bot]']);
// Advisory priority signal only — kept outside stateLabels so it never
// replaces or blocks the review-state queue labels.
const communityApprovedLabel = 'community-approved';
const reviewGateName = 'Zoo Code / PR review gate';
const reconciliationCheckName = 'Zoo Code / reconcile PR review state';

Expand Down Expand Up @@ -310,6 +318,48 @@ jobs:
}
}

// Reconciles the advisory community-approved label. The label means
// "fresh community code approval for the current head; maintainer
// review still required" — it is a priority signal only and never
// grants merge authority. Every reconciliation branch routes through
// this one helper with a single decision:
// 'add' — stable head, maintainer queue, qualifying fresh approval
// 'remove' — known non-maintainer phase, known head change, or
// invalidated approval (known invalidation beats uncertainty)
// 'preserve' — unknown mergeability, or a permission lookup that
// fails only in the community check, with no known
// invalidation. A lookup failure that also blocks
// maintainer classification fails the PR and removes
// the label through the outer catch instead.
// A failed community label update is advisory: it must not clear the
// queue label or fail the review gate, so errors are logged, not thrown.
async function reconcileCommunityApproval(pr, decision) {
if (isReadOnlyRun && isForkPR(pr)) return;
if (decision === 'preserve') return;
const hasLabel = pr.labels.some(label => label.name === communityApprovedLabel);
try {
if (decision === 'add' && !hasLabel) {
await github.rest.issues.addLabels({
owner, repo, issue_number: pr.number, labels: [communityApprovedLabel],
});
pr.labels.push({ name: communityApprovedLabel });
} else if (decision === 'remove' && hasLabel) {
await github.rest.issues.removeLabel({
owner, repo, issue_number: pr.number, name: communityApprovedLabel,
});
pr.labels = pr.labels.filter(label => label.name !== communityApprovedLabel);
}
} catch (error) {
if (decision === 'remove' && error.status === 404) {
pr.labels = pr.labels.filter(label => label.name !== communityApprovedLabel);
return;
}
core.warning(
`PR #${pr.number}: could not ${decision} ${communityApprovedLabel}: ${error.message}`
);
}
}

function codeRabbitLabelHead(comment) {
const markerPattern = new RegExp(`${codeRabbitLabelMarkerPrefix}([a-f0-9]{40}) -->`);
const match = comment?.body?.match(markerPattern);
Expand Down Expand Up @@ -419,7 +469,10 @@ jobs:

return `${guideMarker}\n### Review status\n\n${authorNote}\n\n` +
`**Current step:** ${phaseMessage(phase)}${phaseHelp}\n\n` +
'Review-state labels are managed by this workflow; do not edit them manually.' +
'Review-state labels are managed by this workflow; do not edit them manually. ' +
'`community-approved` is managed the same way — do not add or remove it manually. ' +
'It signals a fresh community code approval for the current head as an advisory ' +
'priority only; maintainer review is still required.' +
labelMarker;
}

Expand Down Expand Up @@ -497,6 +550,7 @@ jobs:
const existingGuide = await findReviewGuide(pr);
await setCodeRabbitReviewActive(pr, false);
await reconcileLabels(pr, null);
await reconcileCommunityApproval(pr, 'remove');
await updateReviewGuide(pr, 'configuration-error', existingGuide);
continue;
}
Expand All @@ -518,6 +572,7 @@ jobs:
await updateReviewGate(pr, 'conflict', false);
await setCodeRabbitReviewActive(pr, false);
await reconcileLabels(pr, 'has-conflicts');
await reconcileCommunityApproval(pr, 'remove');
await updateReviewGuide(pr, 'conflict', existingGuide);
continue;
}
Expand Down Expand Up @@ -623,6 +678,7 @@ jobs:
await updateReviewGate(pr, ciPending ? 'ci-pending' : 'ci-failed', false);
await setCodeRabbitReviewActive(pr, false);
await reconcileLabels(pr, null);
await reconcileCommunityApproval(pr, 'remove');
await updateReviewGuide(
pr,
ciPending ? 'ci-pending' : 'ci-failed',
Expand All @@ -642,7 +698,10 @@ jobs:
// block the PR or indicate the author needs to act.
const latest = new Map();
for (const r of reviews) {
const reviewer = r.user.login.toLowerCase();
// Ghost accounts have no usable identity; their reviews carry
// no actionable state for either review loop.
const reviewer = r.user?.login?.toLowerCase();
if (!reviewer) continue;
const reviewerKey = codeRabbitLogins.has(reviewer) ? codeRabbitLogin : reviewer;
if (r.state === 'DISMISSED') {
latest.delete(reviewerKey);
Expand Down Expand Up @@ -742,6 +801,43 @@ jobs:
freshMaintainerReviews.push(review);
}
}
// Community approval eligibility reuses the decisive per-reviewer
// reduction above: the reviewer's latest decisive review (selected
// by review id, not array order) must be an approval on the current
// head. Ghost accounts, bots, CodeRabbit, and the PR author are
// already excluded there; comments are neutral. Permission results
// are shared with maintainer classification through permissionFor:
// a 404 qualifies an otherwise valid reviewer, while a lookup
// failure is unknown permission and never counts as community
// eligibility. One eligible approval is enough; bot-authored PRs
// follow the same rule. A known invalidation — an approval that
// was dismissed, replaced, or left behind by a head change —
// takes priority over any lookup uncertainty.
const approvedByReviewer = new Set();
for (const r of reviews) {
const reviewer = r.user?.login?.toLowerCase();
if (reviewer && r.state === 'APPROVED') approvedByReviewer.add(reviewer);
}
let communityApprovalFresh = false;
let communityPermissionUncertain = false;
let communityApprovalInvalidated = false;
for (const [reviewer, review] of latestHumanReview) {
if (review.state === 'APPROVED' && review.commit_id === pr.head.sha) {
let permission;
try {
permission = await permissionFor(review.user.login);
} catch (error) {
communityPermissionUncertain = true;
continue;
}
if (!['admin', 'maintain', 'write'].includes(permission)) {
communityApprovalFresh = true;
break;
}
} else if (approvedByReviewer.has(reviewer)) {
communityApprovalInvalidated = true;
}
}
const automatedAuthor = pr.user?.type === 'Bot';
const codeRabbitEligibleAuthor = !automatedAuthor ||
codeRabbitEligibleBotLogins.has(pr.user?.login.toLowerCase());
Expand Down Expand Up @@ -793,15 +889,24 @@ jobs:
phase = 'approved';
}

// Advisory community label decision for the branches below.
// Non-maintainer phases always remove it; the maintainer queue
// refines the decision after the head/mergeability refetch.
let communityDecision = 'remove';
if (desiredLabel === 'awaiting-maintainer') {
const { data: latestPrDetail } = await github.rest.pulls.get({
owner, repo, pull_number: pr.number,
});
// A head change since the event fetch invalidates every
// approval, even if another check is uncertain.
const headChanged = Boolean(latestPrDetail.head?.sha) &&
latestPrDetail.head.sha !== pr.head.sha;
if (latestPrDetail.mergeable === false && latestPrDetail.mergeable_state === 'dirty') {
core.info(`PR #${pr.number}: has merge conflicts — labeling has-conflicts`);
await updateReviewGate(pr, 'conflict', false);
await setCodeRabbitReviewActive(pr, false);
await reconcileLabels(pr, 'has-conflicts');
await reconcileCommunityApproval(pr, 'remove');
await updateReviewGuide(pr, 'conflict', existingGuide);
continue;
}
Expand All @@ -815,8 +920,23 @@ jobs:
error.preserveStateLabels = true;
throw error;
}
// Unknown mergeability alone, with an unchanged head and no
// known invalidation, preserves an existing label and never adds it.
await reconcileCommunityApproval(
pr,
headChanged || communityApprovalInvalidated ? 'remove' : 'preserve'
);
continue;
}
communityDecision = headChanged
? 'remove'
: communityApprovalFresh
? 'add'
: communityApprovalInvalidated
? 'remove'
: communityPermissionUncertain
? 'preserve'
: 'remove';
}

core.info(
Expand All @@ -842,6 +962,7 @@ jobs:
await updateReviewGuide(pr, phase, existingGuide);
}
await reconcileLabels(pr, desiredLabel);
await reconcileCommunityApproval(pr, communityDecision);
if (readyForMaintainer) {
await updateReviewGate(pr, phase, true);
}
Expand All @@ -864,6 +985,9 @@ jobs:
} catch (cleanupError) {
metadataErrors.push(cleanupError);
}
// A global failure that clears the maintainer queue clears the
// advisory community signal too.
await reconcileCommunityApproval(pr, 'remove');
}
const detail = error.status
? `${error.message} (HTTP ${error.status}${error.response?.data?.message ? `: ${error.response.data.message}` : ''})`
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ pnpm install

### Review Process

Ready-for-review PRs must pass required CI checks, address actionable review feedback, and receive fresh maintainer or CODEOWNER approval after automated review completes for the latest commit. Automated review may add a guidance comment and managed state labels; contributors should follow the indicated next step rather than editing those labels directly. New commits reset review state for the updated code.
Ready-for-review PRs must pass required CI checks, address actionable review feedback, and receive fresh maintainer or CODEOWNER approval after automated review completes for the latest commit. Automated review may add a guidance comment and managed state labels; contributors should follow the indicated next step rather than editing those labels directly. New commits reset review state for the updated code. A `community-approved` label marks a fresh community approval on the latest commit while the PR waits for maintainer review. It is an advisory priority signal for maintainers; it does not replace maintainer review or any required check, and it is managed by the review workflow.

Automated review supports maintainers but does not replace human approval. Outside contributors cannot direct CodeRabbit through comments; eligible reviews restart automatically after required CI passes, and maintainers handle any exceptional restart. Warnings are advisory unless repository policy says otherwise, and native GitHub required-check, CODEOWNER, and review protections remain authoritative for merging.

Expand Down
2 changes: 1 addition & 1 deletion apps/vscode-e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"@vscode/test-electron": "2.5.2",
"dotenv-cli": "11.0.0",
"glob": "11.1.0",
"mocha": "11.2.2",
"mocha": "11.8.0",
"rimraf": "6.0.1"
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
],
"overrides": {
"tar-fs": ">=3.1.1",
"mocha>diff": "8.0.4",
"esbuild": "0.28.1",
"rollup": "4.60.4",
"vite": "8.1.0",
Expand Down
64 changes: 63 additions & 1 deletion packages/types/src/__tests__/openai-models.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,69 @@
import { openAiCodexModels } from "../providers/openai-codex.js"
import { openAiCodexDefaultModelId, openAiCodexModels } from "../providers/openai-codex.js"
import { openAiNativeDefaultModelId, openAiNativeModels } from "../providers/openai.js"

describe("OpenAI native models", () => {
it("describes GPT-6.1 Sol pricing and required reasoning without changing defaults", () => {
expect(openAiNativeDefaultModelId).toBe("gpt-5.6-sol")
expect(openAiCodexDefaultModelId).toBe("gpt-5.6-sol")
expect(openAiNativeModels["gpt-6.1-sol"]).toMatchObject({
maxTokens: 128_000,
contextWindow: 1_050_000,
includedTools: ["apply_patch"],
excludedTools: ["apply_diff", "write_to_file"],
supportsImages: true,
supportsPromptCache: true,
supportsReasoningEffort: ["low", "medium", "high", "xhigh", "max"],
requiredReasoningEffort: true,
reasoningEffort: "medium",
supportsTemperature: false,
supportsVerbosity: true,
inputPrice: 2,
outputPrice: 10,
cacheWritesPrice: 2.5,
cacheReadsPrice: 0.1,
longContextPricing: {
thresholdTokens: 272_000,
inputPriceMultiplier: 2,
outputPriceMultiplier: 1.5,
cacheWritesPriceMultiplier: 2,
cacheReadsPriceMultiplier: 2,
appliesToServiceTiers: ["default", "flex", "priority"],
},
})
expect(openAiNativeModels["gpt-6.1-sol"].tiers).toEqual([
{
name: "flex",
contextWindow: 1_050_000,
inputPrice: 1,
outputPrice: 5,
cacheWritesPrice: 1.25,
cacheReadsPrice: 0.05,
},
{
name: "priority",
contextWindow: 1_050_000,
inputPrice: 4,
outputPrice: 20,
cacheWritesPrice: 5,
cacheReadsPrice: 0.2,
},
])
expect(openAiCodexModels["gpt-6.1-sol"]).toMatchObject({
maxTokens: 128_000,
contextWindow: 872_000,
includedTools: ["apply_patch"],
excludedTools: ["apply_diff", "write_to_file"],
supportsImages: true,
supportsPromptCache: true,
supportsReasoningEffort: ["low", "medium", "high", "xhigh", "max"],
requiredReasoningEffort: true,
reasoningEffort: "low",
supportsTemperature: false,
inputPrice: 0,
outputPrice: 0,
})
})

it("describes GPT-6 Astra without changing the provider default", () => {
expect(openAiNativeDefaultModelId).toBe("gpt-5.6-sol")
expect(openAiNativeModels["gpt-6-astra"]).toMatchObject({
Expand Down
15 changes: 15 additions & 0 deletions packages/types/src/__tests__/opencode-go.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ describe("opencode-go registry", () => {
"mimo-v2.5-pro",
"deepseek-v4-pro",
"deepseek-v4-flash",
"deepseek-v4.1-flash",
"omen-alpha",
"grok-4.5",
]
Expand Down Expand Up @@ -224,6 +225,19 @@ describe("opencode-go registry", () => {
cacheReadsPrice: 0.19,
},
},
{
id: "deepseek-v4.1-flash",
expected: {
maxTokens: 384_000,
contextWindow: 1_000_000,
supportsImages: true,
supportsPromptCache: true,
supportsMaxTokens: true,
inputPrice: 0.3,
outputPrice: 1.2,
cacheReadsPrice: 0.006,
},
},
{
id: "longcat-2.0",
expected: {
Expand Down Expand Up @@ -444,6 +458,7 @@ describe("opencode-go registry", () => {
it("DeepSeek entries expose supportsMaxTokens so the max-output slider is available", () => {
expect(getOpencodeGoModelInfo("deepseek-v4-pro")?.supportsMaxTokens).toBe(true)
expect(getOpencodeGoModelInfo("deepseek-v4-flash")?.supportsMaxTokens).toBe(true)
expect(getOpencodeGoModelInfo("deepseek-v4.1-flash")?.supportsMaxTokens).toBe(true)
})
})

Expand Down
7 changes: 2 additions & 5 deletions packages/types/src/providers/anthropic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,18 +165,15 @@ export const anthropicModels = {
description: "Claude Opus 5 is Anthropic's most capable model for complex agentic coding and enterprise work.",
},
"claude-opus-5-5": {
maxTokens: 128_000, // Overridden to 8k if `enableReasoningEffort` is false.
maxTokens: 128_000,
contextWindow: 1_000_000, // 1M context window native (no beta header required)
supportsImages: true,
supportsPromptCache: true,
inputPrice: 4.0, // $4 per million input tokens
outputPrice: 20.0, // $20 per million output tokens
cacheWritesPrice: 5.0, // $5 per million tokens
cacheReadsPrice: 0.2, // $0.20 per million tokens
// Opus 5.5 keeps the Opus 5 adaptive-thinking / binary-toggle convention on
// the direct Anthropic provider path: manual budget_tokens and non-default
// sampling parameters return a 400.
supportsReasoningBudget: true,
// Opus 5.5 always uses adaptive thinking and rejects manual budget_tokens.
supportsReasoningBinary: true,
supportsTemperature: false,
description:
Expand Down
Loading
Loading