Skip to content

Commit a6c428b

Browse files
committed
Merge remote-tracking branch 'origin/main' into fix/architect-plans-relative-path-3qskmqrd412q9
2 parents f2f5e62 + e479b7e commit a6c428b

253 files changed

Lines changed: 10046 additions & 1351 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/itchy-moles-thank.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"zoo-code": patch
3+
---
4+
5+
Fix bedrock DNS resolution when behind corporate proxy

.github/actions/setup-node-pnpm/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ inputs:
66
node-version:
77
description: "Node.js version to use"
88
required: false
9-
default: "20.20.2"
9+
default: "22.23.1"
1010
pnpm-version:
1111
description: "pnpm version to use"
1212
required: false

.nvmrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
20.20.2
1+
22.23.1

.tool-versions

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
pnpm 10.8.1
2-
nodejs 20.20.2
2+
nodejs 22.23.1

CHANGELOG.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,32 @@
11
# Zoo Code Changelog
22

3+
## [3.72.0]
4+
5+
### Minor Changes
6+
7+
- Add the Moonshot provider with live model discovery, streaming, model metadata, and a model picker (PR #857 by @grizmin)
8+
- Add the Kimi Code provider with OAuth device-flow authentication (PR #945 by @taltas)
9+
- Add Claude Opus 5 support across all providers (PR #1010 by @app/zoomote)
10+
- Add Kimi K3 to the Moonshot and OpenCode Go providers (#932 by @navedmerchant, PR #996 by @app/zoomote)
11+
- Add Gemini 3.6 Flash model support (PR #975 by @app/zoomote)
12+
- Add MiniMax-M3 model support (#888 by @RayWinter0816, PR #946 by @app/zoomote)
13+
- Add a safe way to abandon interrupted subtasks by severing stale parent-child links and surfacing delegation status (#559 by @edelauna, PR #935 by @edelauna)
14+
- Add Dart support to codebase indexing (#940 by @WebMad, PR #941 by @WebMad)
15+
- Fix codebase indexing for plain-text files (#931 by @tool-buddy, PR #938 by @WebMad)
16+
- Enable image input for DeepSeek V4 models (#964 by @grizmin, PR #963 by @grizmin)
17+
- Fix ChatGPT OAuth requests for GPT-5.6 Luna being rejected by the Codex backend (PR #889 by @taltas)
18+
- Preserve `reasoning_content` for known reasoning model families when using LiteLLM (#891 by @daewoongoh, PR #899 by @daewoongoh)
19+
- Fix task-history cache invalidation races by routing `invalidate()` and `invalidateAll()` through the task-history lock (#698 by @edelauna, PR #912 by @morgan-coded)
20+
- Fix Settings mode changes by synchronizing the local `cachedState` editing buffer (#914 by @easonLiangWorldedtech, PR #925 by @easonLiangWorldedtech)
21+
- Dismiss the welcome screen after successful Zoo Gateway sign-in (#961 by @JohnCanty, PR #962 by @JamesRobert20)
22+
- Add `CompletePromptOptions` to the `completePrompt` API so callers can configure prompt completion (#615 by @edelauna, PR #901 by @easonLiangWorldedtech)
23+
- Centralize provider identifiers into canonical shared types (#951 by @WebMad, PR #952 by @WebMad)
24+
- Refactor provider categories to use canonical provider identifiers (PR #989 by @WebMad)
25+
- Remove obsolete MCP server-creation translations (#895 by @edelauna, PR #943 by @WebMad)
26+
- Add regression coverage for resuming interrupted subtasks (#566 by @myk1yt, PR #911 by @edelauna)
27+
- Upload coverage reports as GitHub Actions artifacts to simplify CI debugging (PR #939 by @app/zoomote)
28+
- Update `esbuild-wasm` to v0.28.1 (PR #829 by @app/renovate)
29+
330
## [3.70.0]
431

532
### Minor Changes

README.md

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,15 @@
3535
You can find a quick guide for migrating from Roo Code to Zoo Code in the [Roo→Zoo migration guide](https://docs.zoocode.dev/roo-to-zoo-migration). We plan to try and help users as they transition over, we have our [Reddit](https://www.reddit.com/r/ZooCode) and [Discord](https://discord.gg/VxfP4Vx3gX)
3636
for this exact support, so if you are having problems or if you have question, jump on and ask.
3737

38-
## What's New in v3.70.0
38+
## What's New in v3.72.0
3939

40-
- **OpenAI GPT-5.6 family**`Sol`, `Terra`, and `Luna` are now available across both the OpenAI Codex and OpenAI Native provider paths.
41-
- **Grok 4.5 support**xAI's new flagship model, plus a reasoning-effort format fix that also benefits Grok 4 Mini.
42-
- **Kenari provider support**a first-class, OpenAI-compatible AI gateway billed in Rupiah covering Claude, GPT, DeepSeek, GLM, Kimi and more.
43-
- Surface the context-compaction button and context window progress bar in the collapsed task header.
44-
- Fix: terminal output loss and premature task completion on cold terminals.
45-
- Fix: image attach for Zoo Gateway and Vercel AI Gateway models now follows live vision-capability tags instead of a static allowlist.
46-
- Dependency and tooling updates.
40+
- **Moonshot and Kimi Code providers**discover Moonshot models dynamically or sign in to Kimi Code through its OAuth device flow.
41+
- **Latest model support**use Claude Opus 5 across providers, plus Kimi K3, Gemini 3.6 Flash, and MiniMax-M3.
42+
- **Improved subtask workflows**abandon interrupted subtasks cleanly, with safer task-history invalidation and better resume coverage.
43+
- **Expanded codebase indexing** — index Dart and plain-text files.
44+
- **Provider reliability** — fixes for GPT-5.6 Luna with ChatGPT OAuth, LiteLLM reasoning content, and DeepSeek V4 image input.
45+
- **Smoother setup and settings** — improved Zoo Gateway sign-in and mode-switch behavior.
46+
- API, provider-type, dependency, localization, and CI improvements.
4747

4848
<details>
4949
<summary>🌐 Available languages</summary>
@@ -66,8 +66,7 @@ for this exact support, so if you are having problems or if you have question, j
6666
- [Tiếng Việt](locales/vi/README.md)
6767
- [简体中文](locales/zh-CN/README.md)
6868
- [繁體中文](locales/zh-TW/README.md)
69-
- ...
70-
</details>
69+
</details>
7170

7271
---
7372

apps/cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
"@roo-code/config-eslint": "workspace:^",
4242
"@trpc/server": "^11.18.0",
4343
"@roo-code/config-typescript": "workspace:^",
44-
"@types/node": "20.19.43",
44+
"@types/node": "22.20.1",
4545
"@types/react": "18.3.31",
4646
"@vitest/coverage-v8": "4.1.9",
4747
"ink-testing-library": "4.0.0",

apps/vscode-e2e/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"@roo-code/types": "workspace:^",
1818
"@copilotkit/aimock": "1.35.0",
1919
"@types/mocha": "10.0.10",
20-
"@types/node": "20.19.43",
20+
"@types/node": "22.20.1",
2121
"@types/vscode": "1.100.0",
2222
"@vscode/test-electron": "2.5.2",
2323
"dotenv-cli": "11.0.0",

apps/vscode-e2e/src/fixtures/subtasks.ts

Lines changed: 125 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ const SUBTASK_XPROFILE_DIFFERENT_CHILD_MARKER = "SUBTASK_CHILD_DIFFERENT_PROFILE
1818
const SUBTASK_CHILD_PROMPT = `${SUBTASK_CHILD_MARKER}: Ask the user exactly this follow-up question: What is the square root of 81? After the user answers, complete with only the answer.`
1919
export const SUBTASK_PARENT_PROMPT = `${SUBTASK_PARENT_MARKER}: Use the new_task tool exactly once. Create an ask-mode subtask with this exact message: "${SUBTASK_CHILD_PROMPT}" Do not answer directly.`
2020
export const SUBTASK_CHILD_FOLLOWUP_ANSWER = "9"
21-
const SUBTASK_FAST_CHILD_PROMPT = `${SUBTASK_FAST_CHILD_MARKER}: Complete immediately with the exact result "Fast child completed".`
21+
export const SUBTASK_FAST_CHILD_RESULT = "Fast child completed"
22+
const SUBTASK_FAST_CHILD_PROMPT = `${SUBTASK_FAST_CHILD_MARKER}: Complete immediately with the exact result "${SUBTASK_FAST_CHILD_RESULT}".`
2223
export const SUBTASK_FAST_PARENT_PROMPT = `${SUBTASK_FAST_PARENT_MARKER}: Use the new_task tool exactly once. Create an ask-mode subtask with this exact message: "${SUBTASK_FAST_CHILD_PROMPT}" Do not answer directly.`
2324

2425
const SUBTASK_INTERRUPT_CHILD_PROMPT = `${SUBTASK_INTERRUPT_CHILD_MARKER}: Ask the user exactly this follow-up question: What is the square root of 81? After the user answers, complete with only the answer.`
@@ -32,6 +33,14 @@ export const SUBTASK_API_HANG_RESUME_MESSAGE = "Continue after provider hang."
3233
export const SUBTASK_API_HANG_CHILD_RESULT = "Hung child completed"
3334
export const SUBTASK_API_HANG_PARENT_RESULT = "API hang parent resumed"
3435

36+
// Abandon-subtask scenario (#559) — separate markers to avoid sequenceIndex collisions with the
37+
// interrupted-child-resumes tests above, which exhaust the sequence count for INTERRUPT markers.
38+
const SUBTASK_ABANDON_PARENT_MARKER = "SUBTASK_PARENT_ABANDON_SEVER"
39+
const SUBTASK_ABANDON_CHILD_MARKER = "SUBTASK_CHILD_ABANDON_SEVER"
40+
const SUBTASK_ABANDON_CHILD_PROMPT = `${SUBTASK_ABANDON_CHILD_MARKER}: Ask the user exactly this follow-up question: What is the square root of 81? After the user answers, complete with only the answer.`
41+
export const SUBTASK_ABANDON_PARENT_PROMPT = `${SUBTASK_ABANDON_PARENT_MARKER}: Use the new_task tool exactly once. Create an ask-mode subtask with this exact message: "${SUBTASK_ABANDON_CHILD_PROMPT}" Do not answer directly.`
42+
export const SUBTASK_ABANDON_CHILD_FOLLOWUP_ANSWER = "9"
43+
3544
const SUBTASK_XPROFILE_SAME_CHILD_PROMPT = `${SUBTASK_XPROFILE_SAME_CHILD_MARKER}: Complete immediately with the exact result "Same-profile child completed".`
3645
const SUBTASK_XPROFILE_DIFFERENT_CHILD_PROMPT = `${SUBTASK_XPROFILE_DIFFERENT_CHILD_MARKER}: Complete immediately with the exact result "Different-profile child completed".`
3746
export const SUBTASK_XPROFILE_PARENT_PROMPT = `${SUBTASK_XPROFILE_PARENT_MARKER}: First use new_task to create a code-mode subtask with this exact message: "${SUBTASK_XPROFILE_SAME_CHILD_PROMPT}" After it returns, create an ask-mode subtask with the next instructions you receive.`
@@ -46,6 +55,31 @@ const requestContains = (req: ChatCompletionRequest, expected: string[]) => {
4655
return expected.every((text) => rawRequest.includes(text))
4756
}
4857

58+
// aimock's `userMessage` matcher only inspects the LAST user message and joins only the
59+
// `type: "text"` content parts (see getTextContent in aimock's router). Fixtures that need
60+
// whole-request exclusions must replicate that scoping inside a predicate so they keep the
61+
// same matching semantics as the bare-regex fixtures they replace.
62+
const lastUserMessageContains = (req: ChatCompletionRequest, text: string) => {
63+
const userMessages = req.messages?.filter((message) => message.role === "user") ?? []
64+
const last = userMessages.at(-1)
65+
if (!last) return false
66+
const content =
67+
typeof last.content === "string"
68+
? last.content
69+
: (last.content ?? [])
70+
.filter((part): part is { type: "text"; text: string } => part?.type === "text")
71+
.map((part) => part.text)
72+
.join("")
73+
return content.includes(text)
74+
}
75+
76+
// reopenParentFromDelegation injects the child result into the resumed parent's history as
77+
// `Subtask <childId> completed.\n\nResult:\n<summary>`. Matching on this injected prefix (in
78+
// its JSON-serialized form) keeps parent-resume fixtures robust when
79+
// validateAndFixToolResultIds rewrites tool-use ids on resume — matching on the new_task
80+
// tool-call id directly proved flaky (the id can be rewritten, the fixture then misses, and
81+
// a looser child fixture wins and serves the child's response to the parent).
82+
const SUBTASK_RESULT_INJECTION = "completed.\\n\\nResult:"
4983
const completionAfterAnswer = (followupId: string, completionId: string) => ({
5084
match: {
5185
predicate: (req: ChatCompletionRequest) =>
@@ -92,25 +126,33 @@ export function addSubtaskFixtures(mock: InstanceType<typeof LLMock>) {
92126
},
93127
})
94128

129+
// The parent prompt embeds SUBTASK_FAST_CHILD_MARKER verbatim, so parent-resume turns
130+
// can also match a bare substring check (same collision class as #561). Exclude the
131+
// parent marker so those turns fall through to the parent-resume fixture below.
95132
mock.addFixture({
96133
match: {
97-
userMessage: new RegExp(SUBTASK_FAST_CHILD_MARKER),
134+
predicate: (req: ChatCompletionRequest) =>
135+
lastUserMessageContains(req, SUBTASK_FAST_CHILD_MARKER) &&
136+
!requestContains(req, [SUBTASK_FAST_PARENT_MARKER]),
98137
},
99138
response: {
100139
toolCalls: [
101140
{
102141
name: "attempt_completion",
103-
arguments: JSON.stringify({ result: "Fast child completed" }),
142+
arguments: JSON.stringify({ result: SUBTASK_FAST_CHILD_RESULT }),
104143
id: "call_subtasks_fast_child_completion_002",
105144
},
106145
],
107146
},
108147
})
109148

149+
// Guard on SUBTASK_RESULT_INJECTION (not the child result text): the child result is
150+
// embedded verbatim in SUBTASK_FAST_PARENT_PROMPT, so it cannot distinguish the parent's
151+
// initial request from its resume turn.
110152
mock.addFixture({
111153
match: {
112154
predicate: (req: ChatCompletionRequest) =>
113-
requestContains(req, [SUBTASK_FAST_PARENT_MARKER, "call_subtasks_fast_parent_new_task_001"]),
155+
requestContains(req, [SUBTASK_FAST_PARENT_MARKER, SUBTASK_RESULT_INJECTION]),
114156
},
115157
response: {
116158
toolCalls: [
@@ -123,9 +165,15 @@ export function addSubtaskFixtures(mock: InstanceType<typeof LLMock>) {
123165
},
124166
})
125167

168+
// This fixture is shared by several tests, so sequenceIndex cannot guard it. Exclude
169+
// resume turns via the injected tool_result prefix instead: when the tool_result is
170+
// serialized as role:"tool", the original parent prompt is the last user message again
171+
// and would otherwise re-serve new_task on the parent's resume turn.
126172
mock.addFixture({
127173
match: {
128-
userMessage: new RegExp(SUBTASK_PARENT_MARKER),
174+
predicate: (req: ChatCompletionRequest) =>
175+
lastUserMessageContains(req, SUBTASK_PARENT_MARKER) &&
176+
!requestContains(req, [SUBTASK_RESULT_INJECTION]),
129177
},
130178
response: {
131179
toolCalls: [
@@ -141,9 +189,12 @@ export function addSubtaskFixtures(mock: InstanceType<typeof LLMock>) {
141189
},
142190
})
143191

192+
// Same collision guard as the fast-child fixture above: SUBTASK_PARENT_PROMPT embeds
193+
// SUBTASK_CHILD_MARKER verbatim, so parent-resume turns must not match this fixture.
144194
mock.addFixture({
145195
match: {
146-
userMessage: new RegExp(SUBTASK_CHILD_MARKER),
196+
predicate: (req: ChatCompletionRequest) =>
197+
lastUserMessageContains(req, SUBTASK_CHILD_MARKER) && !requestContains(req, [SUBTASK_PARENT_MARKER]),
147198
},
148199
response: {
149200
toolCalls: [
@@ -164,7 +215,7 @@ export function addSubtaskFixtures(mock: InstanceType<typeof LLMock>) {
164215
mock.addFixture({
165216
match: {
166217
predicate: (req: ChatCompletionRequest) =>
167-
requestContains(req, [SUBTASK_PARENT_MARKER, "call_subtasks_parent_new_task_001"]),
218+
requestContains(req, [SUBTASK_PARENT_MARKER, SUBTASK_RESULT_INJECTION]),
168219
},
169220
response: {
170221
toolCalls: [
@@ -230,14 +281,12 @@ export function addSubtaskFixtures(mock: InstanceType<typeof LLMock>) {
230281
},
231282
})
232283

284+
// Same as the fast parent-resume fixture: the child result is embedded verbatim in
285+
// SUBTASK_API_HANG_PARENT_PROMPT, so guard on the injected tool_result prefix instead.
233286
mock.addFixture({
234287
match: {
235288
predicate: (req: ChatCompletionRequest) =>
236-
requestContains(req, [
237-
SUBTASK_API_HANG_PARENT_MARKER,
238-
"call_api_hang_parent_new_task_001",
239-
SUBTASK_API_HANG_CHILD_RESULT,
240-
]),
289+
requestContains(req, [SUBTASK_API_HANG_PARENT_MARKER, SUBTASK_RESULT_INJECTION]),
241290
},
242291
response: {
243292
toolCalls: [
@@ -425,7 +474,7 @@ export function addSubtaskFixtures(mock: InstanceType<typeof LLMock>) {
425474
mock.addFixture({
426475
match: {
427476
predicate: (req: ChatCompletionRequest) =>
428-
requestContains(req, [SUBTASK_INTERRUPT_PARENT_MARKER, "call_interrupt_parent_new_task_001"]),
477+
requestContains(req, [SUBTASK_INTERRUPT_PARENT_MARKER, SUBTASK_RESULT_INJECTION]),
429478
},
430479
response: {
431480
toolCalls: [
@@ -437,4 +486,67 @@ export function addSubtaskFixtures(mock: InstanceType<typeof LLMock>) {
437486
],
438487
},
439488
})
489+
490+
// Abandon-subtask scenario (#559)
491+
mock.addFixture({
492+
match: {
493+
userMessage: new RegExp(SUBTASK_ABANDON_PARENT_MARKER),
494+
sequenceIndex: 0,
495+
},
496+
response: {
497+
toolCalls: [
498+
{
499+
name: "new_task",
500+
arguments: JSON.stringify({
501+
mode: "ask",
502+
message: SUBTASK_ABANDON_CHILD_PROMPT,
503+
}),
504+
id: "call_abandon_parent_new_task_001",
505+
},
506+
],
507+
},
508+
})
509+
510+
mock.addFixture({
511+
match: {
512+
predicate: (req: ChatCompletionRequest) =>
513+
requestContains(req, [SUBTASK_ABANDON_CHILD_MARKER]) &&
514+
!requestContains(req, [SUBTASK_ABANDON_PARENT_MARKER]) &&
515+
!requestContains(req, ["call_abandon_child_followup_001"]) &&
516+
!requestContains(req, [`<user_message>\\n${SUBTASK_ABANDON_CHILD_FOLLOWUP_ANSWER}\\n</user_message>`]),
517+
},
518+
response: {
519+
toolCalls: [
520+
{
521+
name: "ask_followup_question",
522+
arguments: JSON.stringify({
523+
question: "What is the square root of 81?",
524+
follow_up: [{ text: SUBTASK_ABANDON_CHILD_FOLLOWUP_ANSWER }],
525+
}),
526+
id: "call_abandon_child_followup_001",
527+
},
528+
],
529+
},
530+
})
531+
532+
mock.addFixture({
533+
match: {
534+
predicate: (req: ChatCompletionRequest) =>
535+
toolResultContains(req, "call_abandon_child_followup_001", [SUBTASK_ABANDON_CHILD_FOLLOWUP_ANSWER]) ||
536+
requestContains(req, ["call_abandon_child_followup_001", SUBTASK_ABANDON_CHILD_FOLLOWUP_ANSWER]) ||
537+
requestContains(req, [
538+
SUBTASK_ABANDON_CHILD_MARKER,
539+
`<user_message>\\n${SUBTASK_ABANDON_CHILD_FOLLOWUP_ANSWER}\\n</user_message>`,
540+
]),
541+
},
542+
response: {
543+
toolCalls: [
544+
{
545+
name: "attempt_completion",
546+
arguments: JSON.stringify({ result: SUBTASK_ABANDON_CHILD_FOLLOWUP_ANSWER }),
547+
id: "call_abandon_child_completion_002",
548+
},
549+
],
550+
},
551+
})
440552
}

0 commit comments

Comments
 (0)