Skip to content

Commit 4370709

Browse files
Merge upstream/main 73931fe into vps2-f2c
Cascade refresh for #1921. The merged sha is what git ls-remote reported for refs/heads/main on the upstream (Zoo-Code-Org) remote at fetch time; the fork's own main, 9b6d742, is four weeks stale and an ancestor of the commit being merged, so it is not the refresh target. 105 files in from main on top of 5789700; upstream was merged directly into this branch, not chained through vps2/f1b-getstate-merge or vps2/f1c-webview-identity. Survivor pass over the 56 spec paths either parent touched: 16 titles are absent from the merged tree relative to one parent and every one has a named substitute, so nothing was restored in this commit. No file present at either parent is missing from the merged tree. src/core/webview/__tests__/ClineProvider.spec.ts merged three ways with 504 tests green, and apps/vscode-e2e/src/suite/restart-persistence.test.ts merged against this branch's edits (that suite needs a real extension host, so it is compile-checked here and executed by CI, not run locally). Upstream restructures (merged blob byte-identical to upstream's blob, so coverage moved with the file): - src/utils/__tests__/safeWriteJson.test.ts: 4 per-rename failure titles, replaced by upstream's it.each failure-injection table plus the new safeWriteJson.interruption.test.ts (3 files / 125 tests green with DiffViewProvider). - src/integrations/editor/__tests__/DiffViewProvider.spec.ts: 'should handle showTextDocument failure', replaced by upstream's it.each('records failure metadata for each showTextDocument failure mode: %s'). - src/core/task-persistence/__tests__/TaskHistoryStore.reconciliation.spec.ts: 'replays a both-at-target intent without writing task files', replaced by upstream's it.each (13 files / 208 tests green in that directory). Held by this branch under different words, with the assertion in the branch copy: - src/activate/__tests__/registerCommands.spec.ts (8 main-side titles): 'creates diagnostics from all provider instances without requiring a visible provider' is here as 'creates the diagnostics report from every provider instance, visible or not' (line 289, which mocks getAllInstances to two doubles, getVisibleInstance to undefined, and asserts the payload); the settings/history/marketplace posts are here as the '... on the registered provider' trio (lines 357, 373, 384); 'focusInput does not post when no sidebar panel is active' is here as 'focusInput does not post when no sidebar panel is tracked' (line 611); 'plusButtonClicked calls evictCurrentTask on the visible provider' is here as 'plus ButtonClicked calls evictCurrentTask on the registered sidebar provider'; and main's two 'no-op when no visible provider' rows describe the visible-instance contract this branch replaced with the registered-provider contract, and the 'no target yet' behaviour is pinned here by the sidebar-view-wait table (rows for zoo-code.settingsButtonClicked at line 403 and the plusButtonClicked cases at lines 1049-1135) together with the zoo-code.*InTab no-op tables at lines 521 and 529. - src/core/webview/__tests__/ClineProvider.sticky-mode.spec.ts: main's 'should handle errors during mode switch gracefully' (a task whose emit throws on the second emit, i.e. on taskModeSwitched) is here as 'keeps the history entry on the mode the conversation runs when a TaskModeSwitched listener throws' (line 574), which drives the same throwing listener and asserts rejects.toThrow('listener failed'), that the history entry equals the task's own mode and is the requested mode, that the shared store keeps the old mode, and that the event was emitted; 'leaves no mode pin behind when a view without a pin fails the same way' (line 505) covers the pin side. The reordering that made main's case wrong is documented at src/core/webview/ClineProvider.ts:2882-2895 (emit moved after the mode assignment and outside the persistence try). - src/core/webview/__tests__/ClineProvider.apiHandlerRebuild.spec.ts: 'timed-out mutations abort before writing state and advance the queue' is here as five split titles including 'holds the next profile mutation until a timed-out model update settles' and 'timed-out activations run to completion and hold the queue'. Gates on the merged tree before this commit: tsc --noEmit 0 errors; eslint --max-warnings=0 clean over the 56 staged JS/TS paths in the root that owns each (src 52, apps/vscode-e2e 3, webview-ui 1); src/eslint-suppressions.json only loses entries (288 entries / 3297 counts to 265 / 2866, zero increases); vitest lanes green: registerCommands 81, safeWriteJson test + interruption + DiffViewProvider 3 files / 125, task-persistence 13 files / 208, sticky-mode + sticky-profile + parallelMode 3 files / 70, apiHandlerRebuild + webviewMessageHandler + ClineProviderFactory 3 files / 231, i18n locale-bundles + SkillsManager + extension.spec 3 files / 137, ClineProvider.spec 504, core/config 11 files / 323, extension 10 files / 72, packages/types 2 files / 25, webview-ui 2 files / 62. The pre-commit hook ran lint-staged on this merge (PATH prepended with C:/Users/eason/AppData/Roaming/npm so pnpm 10.8.1 resolves instead of falling through to npx). No --no-verify was used.
2 parents 5789700 + 73931fe commit 4370709

105 files changed

Lines changed: 3907 additions & 1501 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.

‎.github/workflows/e2e.yml‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,18 @@ jobs:
162162
if: github.event_name != 'pull_request' || steps.e2e-marker.outputs.cache-hit != 'true'
163163
run: TEST_FILE=restart-persistence.test USE_MOCK=true xvfb-run -a pnpm --filter @roo-code/vscode-e2e exec node ./out/runTest.js
164164

165+
- name: Upload restart-persistence failure artifacts
166+
# runTest.js preserves a failed scenario workspace (phase results,
167+
# VS Code logs, task files) under apps/vscode-e2e/test-failures/
168+
# instead of deleting it, so the interrupted-write state from
169+
# issue #2130-style failures stays inspectable.
170+
if: failure()
171+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
172+
with:
173+
name: restart-persistence-failure
174+
path: apps/vscode-e2e/test-failures/
175+
if-no-files-found: ignore
176+
165177
- name: Explain skipped mocked E2E pass marker
166178
if: steps.e2e-marker.outputs.cache-hit != 'true' && steps.run-e2e.outcome == 'success' && steps.vscode-fallback.outputs.used == 'true'
167179
run: echo "Skipping mocked E2E pass marker because tests ran against a stale cached VS Code binary (VS Code download endpoints unreachable)."

‎.gitignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ AGENTS.local.md
2828
apps/vscode-e2e/playwright-report-electron/
2929
apps/vscode-e2e/test-results-electron/
3030
apps/vscode-e2e/heap-results/
31+
apps/vscode-e2e/test-failures/
3132

3233
# Docs
3334
docs/_site/

‎CHANGELOG.md‎

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

3+
## [3.96.0]
4+
5+
- ✨ **New chat settings and diagnostics:** Show message timestamps with configurable time and date formats, tune the prompt-cache TTL for Anthropic profiles, and generate privacy-safe diagnostics reports.
6+
- 💸 **More accurate provider costs and caching:** Keep Anthropic tool results intact, adopt cumulative output tokens in Anthropic and MiniMax costs, read DeepSeek prompt-cache counters, target the xAI Responses cache, and bill Mistral cache reads at 0.1x.
7+
- 🧭 **More reliable and faster chats:** Resume completed tasks after a restart, restore checkpoints when the saved commit is missing, auto-reload a dead webview renderer, throttle streaming updates, and load locales on demand.
8+
9+
### New Chat Settings and Diagnostics
10+
11+
- Add time and date format settings and message timestamps (#2221 by @KarthickMahalingam, PR #2223 by @KarthickMahalingam)
12+
- Add a user-facing prompt-cache TTL for Anthropic profiles (PR #2271 by @simurg79)
13+
- Add privacy-safe diagnostics reports (PR #2292 by @app/zoomote2)
14+
15+
### More Accurate Provider Costs and Caching
16+
17+
- Keep tool_result blocks contiguous in Anthropic requests by grouping image blocks separately (#1307 by @alaskanloosemoose-dotcom, PR #2243 by @myk1yt)
18+
- Adopt cumulative message_delta output tokens in Anthropic and MiniMax cost (#1727 by @myk1yt, PR #2242 by @myk1yt)
19+
- Read documented top-level prompt-cache counters for DeepSeek (PR #2252 by @myk1yt)
20+
- Send body prompt_cache_key for xAI Responses cache affinity (PR #2256 by @myk1yt)
21+
- Bill Mistral cache reads at 0.1x instead of the full input price (PR #2257 by @myk1yt)
22+
- Map xAI tool_choice to the Responses API shape with a typed request body (PR #2036 by @easonLiangWorldedtech)
23+
- Add MiMo V2.6 Flash and Pro model configurations for OpenCode Go (#1997 by @WebMad, PR #2246 by @myk1yt)
24+
25+
### More Reliable and Faster Chats
26+
27+
- Resume completed tasks after a restart when a message write is interrupted (#2130 by @edelauna, PR #2297 by @app/zoomote2)
28+
- Restore checkpoints when the saved commit is missing (PR #2269 by @simurg79)
29+
- Detect a dead webview renderer via heartbeat and auto-reload (PR #1715 by @myk1yt)
30+
- Report emergency condense like the automatic path (PR #1838 by @PierrunoYT)
31+
- Summarize only the API-visible history when condensing (#1342 by @RemainIndoors1, PR #2300 by @RytisLT)
32+
- Propagate beyond-EOF read-file errors instead of reporting the file as empty (PR #1734 by @myk1yt)
33+
- Throttle partial message updates while streaming (PR #2120 by @PierrunoYT)
34+
- Replace axios with fetch in key-info hooks (PR #2261 by @myk1yt)
35+
- Load locales on demand instead of eager-globbing all 18 (PR #2267 by @myk1yt)
36+
- Replace the debounce package with a local leading-edge helper (PR #2268 by @myk1yt)
37+
38+
### New Features and Integrations
39+
40+
- Add a dependency-free BM25 ranker tool (PR #1207 by @edelauna)
41+
- Add abort-signal wiring for lm-studio and qwen-code (PR #1309 by @easonLiangWorldedtech)
42+
- Add Keenable to the MCP marketplace (#2312 by @ilya-bogin-keenable, PR #2313 by @ilya-bogin-keenable)
43+
44+
### Testing, CI, and Repository Automation
45+
46+
- Cancel obsolete PR CI runs and remove duplicate builds (#2289 by @edelauna, PR #2294 by @app/zoomote2)
47+
- Post surviving mutants as a PR comment (#2287 by @edelauna, PR #2293 by @app/zoomote2)
48+
- Run webview CI tests with four workers (#2288 by @edelauna, PR #2308 by @app/zoomote2)
49+
- Use plugin-side related-test discovery for the extension package mutation gate (PR #2211 by @easonLiangWorldedtech)
50+
- Add test-utils guardrails and convert remaining stragglers (PR #2279 by @app/zoomote2)
51+
- Drop test-only tool-use interfaces from shared/tools (PR #2258 by @myk1yt)
52+
- Show the Zoo Code panel in the Solheim smoke video and bound the apt step (#2158 by @edelauna, PR #2195 by @app/zoomote2)
53+
54+
### Refactoring and Code Health
55+
56+
- Move default provider ID functionality into the provider definition (PR #2233 by @p12tic)
57+
- Resolve profile model IDs through provider definitions (PR #2239 by @p12tic)
58+
- Remove no-explicit-any suppressions in utils (PR #2281 by @app/zoomote2)
59+
- Remove no-explicit-any suppressions in small directories (PR #2286 by @app/zoomote2)
60+
- Close lint coverage and knip hygiene gaps (PR #2280 by @app/zoomote2)
61+
- Sweep dead locale keys behind an AST gate (PR #2263 by @myk1yt)
62+
- Remove dead CSS classes from the global stylesheet (PR #2251 by @myk1yt)
63+
64+
### Dependencies and Maintenance
65+
66+
- Update @tanstack/react-query to v5.105.0 (PR #2180 by @app/renovate)
67+
- Update ignore to v7.0.14 (PR #2181 by @app/renovate)
68+
- Update posthog-js to v1.440.0 (PR #2182 by @app/renovate)
69+
- Update posthog-node to v5.57.2 (PR #2183 by @app/renovate)
70+
- Update GitHub Actions (PR #2186 by @app/renovate)
71+
- Update qs to v6.16.1 [security] (PR #2214 by @app/renovate)
72+
73+
### Housekeeping
74+
75+
- Merge the **previous v3.94.0 release preparation** after its publication; this is prior-release housekeeping, not a v3.96.0 feature (PR #2272 by @app/zoomote2)
76+
377
## [3.94.0]
478

579
- ✨ **Broader model and provider support:** Use Claude Haiku 5.5 through Anthropic, send images to compatible VS Code LM models, tune temperature for OpenCode Go Responses models, and cancel requests cleanly across more providers.

‎README.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,11 @@ Zoo Code builds on the foundation created by Roo Code and continues to expand it
4646
- **More dependable terminal and editing workflows** — fixes for premature terminal completion, task-state races, context management, diff editing, and provider-specific tool use.
4747
- **More control over your workspace** — rules management, per-mode MCP restrictions, multi-root path controls, model reasoning options, and completion change review actions.
4848

49-
## What's New in v3.94.0
49+
## What's New in v3.96.0
5050

51-
- ✨ **Broader model and provider support:** Use Claude Haiku 5.5 through Anthropic, send images to compatible VS Code LM models, tune temperature for OpenCode Go Responses models, and cancel requests cleanly across more providers.
52-
- 🔐 **More flexible MCP OAuth:** Configure static OAuth client settings, use custom callback paths, discover authentication details from `WWW-Authenticate`, and see callback failures before retrying.
53-
- 🧭 **Smoother chats and long sessions:** Rename tasks in history, drag files into chat without holding Shift, reduce memory retained during long sessions, and keep subtask links and replaced webviews stable.
51+
- ✨ **New chat settings and diagnostics:** Show message timestamps with configurable time and date formats, tune the prompt-cache TTL for Anthropic profiles, and generate privacy-safe diagnostics reports.
52+
- 💸 **More accurate provider costs and caching:** Keep Anthropic tool results intact, adopt cumulative output tokens in Anthropic and MiniMax costs, read DeepSeek prompt-cache counters, target the xAI Responses cache, and bill Mistral cache reads at 0.1x.
53+
- 🧭 **More reliable and faster chats:** Resume completed tasks after a restart, restore checkpoints when the saved commit is missing, auto-reload a dead webview renderer, throttle streaming updates, and load locales on demand.
5454

5555
<details>
5656
<summary>🌐 Available languages</summary>

‎apps/vscode-e2e/src/restart/scenarioWorkspace.ts‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,56 @@ export async function removeScenarioWorkspace(scenarioWorkspace: ScenarioWorkspa
5656

5757
await fs.rm(root, { recursive: true, force: true })
5858
}
59+
60+
const FAILURE_ARTIFACTS_DIR = "test-failures"
61+
62+
/**
63+
* Copies the diagnosable parts of a failed scenario workspace into a stable
64+
* directory inside the checkout, then removes the temporary scenario root.
65+
* CI uploads that directory as a run artifact, so the failure that used to be
66+
* deleted in the cleanup path stays inspectable: phase results, VS Code logs,
67+
* and the extension's global storage with the task files.
68+
*
69+
* The directory is keyed by the unique scenario root name and cleared before
70+
* copying, so retried runs cannot mix artifacts from different failures.
71+
*/
72+
export async function preserveScenarioWorkspace(scenarioWorkspace: ScenarioWorkspace): Promise<string> {
73+
const destinationRoot = path.resolve(
74+
__dirname,
75+
"../../",
76+
FAILURE_ARTIFACTS_DIR,
77+
path.basename(scenarioWorkspace.root),
78+
)
79+
await fs.rm(destinationRoot, { recursive: true, force: true })
80+
await fs.mkdir(destinationRoot, { recursive: true })
81+
82+
const selections: Array<{ label: string; source: string; destination: string }> = [
83+
{
84+
label: "phase results",
85+
source: scenarioWorkspace.results,
86+
destination: path.join(destinationRoot, "phase-results"),
87+
},
88+
{
89+
label: "VS Code logs",
90+
source: path.join(scenarioWorkspace.userData, "logs"),
91+
destination: path.join(destinationRoot, "vscode-logs"),
92+
},
93+
{
94+
label: "task files",
95+
source: path.join(scenarioWorkspace.userData, "User", "globalStorage"),
96+
destination: path.join(destinationRoot, "global-storage"),
97+
},
98+
]
99+
100+
for (const selection of selections) {
101+
try {
102+
await fs.cp(selection.source, selection.destination, { recursive: true, force: true })
103+
} catch (error) {
104+
// A missing subdirectory must not hide the other artifacts.
105+
console.error(`Failed to preserve ${selection.label} from ${selection.source}:`, error)
106+
}
107+
}
108+
109+
await removeScenarioWorkspace(scenarioWorkspace)
110+
return destinationRoot
111+
}

‎apps/vscode-e2e/src/runTest.ts‎

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@ import { addSearchFilesResultFixtures } from "./fixtures/search-files"
2121
import { addSubtaskFixtures } from "./fixtures/subtasks"
2222
import { addUseMcpToolResultFixtures } from "./fixtures/use-mcp-tool"
2323
import { addWriteToFileResultFixtures } from "./fixtures/write-to-file"
24-
import { createScenarioWorkspace, removeScenarioWorkspace } from "./restart/scenarioWorkspace"
24+
import {
25+
createScenarioWorkspace,
26+
preserveScenarioWorkspace,
27+
removeScenarioWorkspace,
28+
} from "./restart/scenarioWorkspace"
2529
import { runRestartScenario } from "./restart/vscodeCoordinator"
2630

2731
function getCliFlagValue(flag: string) {
@@ -95,6 +99,7 @@ async function main() {
9599

96100
let testWorkspace: string | undefined
97101
let scenarioWorkspace: Awaited<ReturnType<typeof createScenarioWorkspace>> | undefined
102+
let runFailed = false
98103

99104
try {
100105
// Create a temporary workspace folder for regular tests. Restart scenarios own
@@ -218,14 +223,27 @@ async function main() {
218223
})
219224
}
220225
} catch (error) {
226+
runFailed = true
221227
console.error("Failed to run tests", error)
222228
process.exitCode = 1
223229
} finally {
224230
if (testWorkspace) {
225231
await fs.rm(testWorkspace, { recursive: true, force: true })
226232
}
227233
if (scenarioWorkspace) {
228-
await removeScenarioWorkspace(scenarioWorkspace)
234+
// A failed restart scenario must stay inspectable: the phase
235+
// results, VS Code logs, and task files are copied to a stable
236+
// directory for CI artifact upload before the temp root goes away.
237+
if (runFailed) {
238+
try {
239+
const preservedAt = await preserveScenarioWorkspace(scenarioWorkspace)
240+
console.error(`Restart scenario workspace preserved for failure inspection: ${preservedAt}`)
241+
} catch (preserveError) {
242+
console.error("Failed to preserve the restart scenario workspace:", preserveError)
243+
}
244+
} else {
245+
await removeScenarioWorkspace(scenarioWorkspace)
246+
}
229247
}
230248
await mock?.stop()
231249
}

‎apps/vscode-e2e/src/suite/restart-persistence.test.ts‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,13 @@ async function runCreate(api: RooCodeAPI): Promise<void> {
5454
await waitUntilCompleted({ api, taskId })
5555
assert.strictEqual(sawMarker, true, `Completion should include ${MARKER}`)
5656

57-
// TaskCompleted fires before the final saveClineMessages() write is
58-
// necessarily durable on disk. Wait until the completion marker is
59-
// persisted in ui_messages.json before quitting, so the verify phase
60-
// never reads a task directory whose final write has not landed yet.
57+
// TaskCompleted and the UI marker do not acknowledge all three files.
58+
// Check both persisted histories and the store's published history item.
59+
// TaskHistoryStore publishes newly created cache entries only after writing them.
6160
const createdTaskId: string = taskId
6261
await waitFor(() => api.hasTaskUiMessageText(createdTaskId, MARKER))
62+
await waitForMarkedCompletion(api, createdTaskId)
63+
await waitFor(async () => (await api.getTaskHistoryItem(createdTaskId)) !== undefined)
6364

6465
// history_item.json is written outside the ui_messages.json flush as well: wait for the
6566
// task to appear in history with its completed status stored rather than merely existing.

0 commit comments

Comments
 (0)