diff --git a/ai_plans/2026-06-27_zoo-225-knip-dead-code.md b/ai_plans/2026-06-27_zoo-225-knip-dead-code.md new file mode 100644 index 0000000000..c131bcef5f --- /dev/null +++ b/ai_plans/2026-06-27_zoo-225-knip-dead-code.md @@ -0,0 +1,77 @@ +# Port Zoo PR #225 — configure knip + remove dead dependencies + +## §0 Credit & provenance + +- Upstream: Zoo-Code-Org/Zoo-Code PR #225, commit `8ea077942`, merged 2026-06-08. +- Original author: Elliott de Launay (roomote[bot] / renovate[bot] dropped as bots). +- Commit trailer: + ``` + Co-authored-by: Elliott de Launay + ``` + +## §1 What & why + +Upgrade the knip config (schema v5, per-workspace `ignoreDependencies`, rules, +`ignoreExportsUsedInFile`) and remove dependencies knip flagged as dead. + +**Divergence guard (critical):** "dead in Zoo" ≠ "dead in our fork." Every removed +dependency was independently verified UNUSED in _our_ code before removal (grep +for imports + a scripts/config audit). The knip `ignoreDependencies` lists were +checked against our actual dep set (src 16/16, webview 10/11 — we lack `vscode`, +so it was dropped from our webview list). + +## §2 Edits (exact, verified) + +### Dead deps removed from `src/package.json` (all verified zero-usage here) + +- `@openrouter/ai-sdk-provider` — zero refs repo-wide; our `openrouter.ts` uses + `@anthropic-ai/sdk`, not this package. +- `@vscode/test-electron` — `apps/vscode-e2e` declares its own; src's is unused. +- `npm-run-all2` — no script uses `run-p`/`run-s`/`npm-run-all`. +- `tsup` — src builds via esbuild; only `apps/cli` uses tsup (has its own). +- `tsx` — only `apps/cli` scripts use it (was hoisted from src) → moved to cli. +- `zod-to-ts` — zero imports. + +### `apps/cli/package.json` + +- Add `"tsx": "^4.19.3"` (cli scripts `dev`/`test:integration` use it; previously + resolved via hoist from src). + +### `webview-ui/package.json` + delete `webview-ui/src/types.d.ts` + +- Remove `knuth-shuffle-seeded` (only referenced by the now-deleted `types.d.ts` + module declaration; no real import) and `identity-obj-proxy` (no config uses it). +- Delete `webview-ui/src/types.d.ts` (contained only the knuth-shuffle declaration). + +### `package.json` + +- `"knip": "knip --include files"` → `"knip": "knip"`. + +### `knip.json` (adapted to our fork) + +- Port Zoo's new structure verbatim where deps match; **dropped** `vscode` from the + webview `ignoreDependencies` (we don't declare it); **added** `self-hosted-cloudapi/**` + to top-level `ignore` (our fork has that extra Docker/Python sub-project Zoo lacks). + +## §3 Scope cuts (YAGNI / divergence) + +- Did NOT add `@vitest/coverage-v8` to src or cli (neither runs coverage here; Zoo + already had it — we don't, and don't need it). +- Did NOT re-order posthog-js/lucide-react in webview (cosmetic). +- Did NOT add `packages/evals` to knip workspaces (faithful to upstream's new config). +- Did NOT re-add Roo branding / TTS / router / cloud. + +## §4 Verify (binary acceptance) + +- `pnpm install` succeeds after removals. ✓ +- `pnpm --filter tumble-code check-types` / `@roo-code/vscode-webview` / + `@roo-code/cli` → all pass (tsc resolves every import; a removed-but-used dep + would error "cannot find module"). ✓ +- `tsx` bin resolves for `apps/cli`. ✓ +- Note: `pnpm knip` cannot complete on this machine — it crashes traversing the + root-owned (uid 70, mode 700) `self-hosted-cloudapi/.vol/postgres` Docker volume + with EACCES. Confirmed this happens with the OLD config too → pre-existing local + environment issue, independent of this change. The knip config itself parses + (knip reaches file traversal past config validation). + +## §5 Co-author — see §0. diff --git a/ai_plans/2026-06-27_zoo-324-gemma4-thought-tags.md b/ai_plans/2026-06-27_zoo-324-gemma4-thought-tags.md new file mode 100644 index 0000000000..92aa8558c5 --- /dev/null +++ b/ai_plans/2026-06-27_zoo-324-gemma4-thought-tags.md @@ -0,0 +1,48 @@ +# Port Zoo PR #324 — parse Gemma 4 `` reasoning tags alongside `` + +## §0 Credit & provenance + +- Upstream: Zoo-Code-Org/Zoo-Code PR #324, commit `0084cc899`, merged 2026-06-26. +- Authors: Sagid M / Sagid Magomedov, Elliott de Launay (edelauna). +- Commit trailers: + ``` + Co-authored-by: Sagid M + Co-authored-by: Sagid Magomedov + Co-authored-by: Elliott de Launay + ``` + +## §1 What & why (weak-model robustness) + +Gemma 4 emits reasoning inside `` rather than ``. +This generalizes `TagMatcher` to accept multiple tag names so reasoning extraction +works for both, without a closing `` wrongly terminating a `` block. +Aligns with [[feedback_design_for_weak_models]]. + +Our `src/utils/tag-matcher.ts` matched Zoo's pre-PR exactly; clean port. + +## §2 Edits + +- `src/utils/tag-matcher.ts`: constructor accepts `string | [string, ...string[]]`; + tracks `tagNames` / `activeTagNames` / `candidates` so any of the names opens a + block and only the matching name closes it. (Replaced wholesale with the upstream + post-PR version — our file was byte-identical to Zoo's pre-PR.) +- `base-openai-compatible-provider.ts`, `lm-studio.ts`, `native-ollama.ts`, + `openai.ts`: `new TagMatcher("think", …)` → `new TagMatcher(["think", "thought"], …)`. +- Tests: add `src/utils/__tests__/tag-matcher.spec.ts` (new upstream file, 18 cases); + add two `` integration tests + tighten one flush assertion in + `base-openai-compatible-provider.spec.ts`. + +## §3 Scope cuts (divergence) + +- **Skipped the `openai.spec.ts` +169-line additions** — our `openai.spec.ts` has + diverged ~495 lines from Zoo's (timeout work in #567 + other), so the additions + don't anchor cleanly. The behavior is already covered by `tag-matcher.spec.ts` + (the matcher unit) and the `base-openai-compatible-provider.spec.ts` `` + integration tests (the shared streaming path). + +## §4 Verify (binary acceptance) — all ✓ + +- `pnpm --filter tumble-code check-types` passes. +- `npx vitest run utils/__tests__/tag-matcher api/providers/__tests__/base-openai-compatible-provider` + → 38 pass (incl. the 2 new `` tests). +- 4 affected provider suites still green (121 tests). diff --git a/ai_plans/2026-06-27_zoo-351-mcp-sdk-1-26-0-security.md b/ai_plans/2026-06-27_zoo-351-mcp-sdk-1-26-0-security.md new file mode 100644 index 0000000000..454225e9d5 --- /dev/null +++ b/ai_plans/2026-06-27_zoo-351-mcp-sdk-1-26-0-security.md @@ -0,0 +1,62 @@ +# Port Zoo PR #351 — update @modelcontextprotocol/sdk to v1.26.0 [security] + MCP resource_link support + +## §0 Credit & provenance + +- Upstream: Zoo-Code-Org/Zoo-Code PR #351, commit `09fedd62c`, merged 2026-06-06. +- Original author(s): Elliott de Launay, edelauna (renovate[bot] dropped as a bot). +- Commit trailers to add: + ``` + Co-authored-by: Elliott de Launay + Co-authored-by: edelauna <54631123+edelauna@users.noreply.github.com> + ``` + +## §1 What & why + +Two coupled changes: + +1. **Security bump** of `@modelcontextprotocol/sdk` `1.12.0 → 1.26.0` in `src/package.json` (+ lockfile). +2. **MCP `resource_link` content type** (MCP spec 2025-06-18): add the `McpResourceLink` + type to `packages/types/src/mcp.ts` and render it in `UseMcpToolTool`. + +Our fork is at the exact pre-PR state (sdk 1.12.0; no `McpResourceLink`; no `resource_link` +branch in `UseMcpToolTool.processToolContent`). MCP code paths exist here (McpHub, UseMcpToolTool), +so this is in-scope. No Roo/TTS/router/cloud entanglement. + +## §2 Edits (exact, adapted to our code) + +### 2a. `packages/types/src/mcp.ts` — add types before `McpToolCallResponse`, add to union + +Insert the `McpResourceLinkAnnotations` + `McpResourceLink` types and add `| McpResourceLink` +to the `McpToolCallResponse.content` union. (Keep our existing multi-line union formatting; +only add the new arm — do NOT reflow the existing arms to one-liners like upstream did; that's +churn with no behavior change.) + +### 2b. `src/core/tools/UseMcpToolTool.ts` + +- Add `McpResourceLink` to the type import from `@roo-code/types`. +- In `processToolContent`'s `.map`, after the `image` branch and before the trailing `return ""`, + add a `resource_link` branch rendering a markdown link. + +### 2c. `src/package.json` + +- `"@modelcontextprotocol/sdk": "1.12.0"` → `"1.26.0"`. + +### 2d. Lockfile + +- `pnpm install --lockfile-only` (or filtered install) to refresh `pnpm-lock.yaml`. + +## §3 Scope cuts (YAGNI) + +- Do NOT reflow the existing `McpToolCallResponse` union arms (upstream cosmetic change). +- Do NOT re-add TTS / router / cloud / Roo branding. +- No new tests required by upstream; add a focused type/render check only if cheap. + +## §4 Verify (binary acceptance) + +- `pnpm --filter @roo-code/types check-types` → passes. +- `cd src && npx tsc --noEmit` (or repo typecheck) → passes; `McpResourceLink` resolves. +- `grep '@modelcontextprotocol/sdk' src/package.json` shows `1.26.0`. +- `grep '@modelcontextprotocol/sdk@1.26.0' pnpm-lock.yaml` present; no `@1.12.0` left. +- Existing MCP tests (`UseMcpToolTool`, `McpHub`) still pass. + +## §5 Co-authors — see §0. diff --git a/ai_plans/2026-06-27_zoo-423-pin-dependencies.md b/ai_plans/2026-06-27_zoo-423-pin-dependencies.md new file mode 100644 index 0000000000..67bc99d174 --- /dev/null +++ b/ai_plans/2026-06-27_zoo-423-pin-dependencies.md @@ -0,0 +1,51 @@ +# Port Zoo PR #423 — pin React type dependencies + +## §0 Credit & provenance + +- Upstream: Zoo-Code-Org/Zoo-Code PR #423, commit `5ad7aab6b`, merged 2026-06-07. +- Original author: Elliott de Launay (renovate[bot] dropped as a bot). +- Commit trailer: + ``` + Co-authored-by: Elliott de Launay + ``` + +## §1 What & why + +Renovate "pin dependencies" chore: replace caret ranges on `@types/react` / +`@types/react-dom` with exact versions across the three manifests that declare +them, and align `apps/cli` (which declared React **19** types while the root +override forces **18**) to the pinned v18. + +Pinning removes silent caret drift (reproducible installs) and fixes the +declared-vs-resolved React-types mismatch in the CLI. The lockfile already +resolves `@types/react@18.3.23` and `@types/react-dom@18.3.7`, so resolution is +unchanged — only the declared ranges tighten. + +Our fork is at the exact pre-PR state for these lines. + +## §2 Edits (exact) + +- `apps/cli/package.json`: `"@types/react": "^19.1.6"` → `"18.3.23"`. +- `package.json` (pnpm.overrides): `"@types/react": "^18.3.23"` → `"18.3.23"`; + `"@types/react-dom": "^18.3.5"` → `"18.3.7"`. +- `webview-ui/package.json`: `"@types/react": "^18.3.23"` → `"18.3.23"`; + `"@types/react-dom": "^18.3.5"` → `"18.3.7"`. +- Refresh `pnpm-lock.yaml` via `pnpm install --lockfile-only`. + +## §3 Scope cuts (YAGNI / divergence) + +- Do NOT touch `@types/node`, `glob`, or any other line the PR only showed as + diff context — our fork has its own values there (`glob: ">=11.1.0"`, + cli `@types/node: "^24.1.0"`). Only the React-type lines change. +- Do NOT re-add Roo branding / TTS / router / cloud. + +## §4 Verify (binary acceptance) + +- `grep '@types/react' apps/cli/package.json package.json webview-ui/package.json` + shows no caret on the react type lines; cli shows `18.3.23`. +- `pnpm install --lockfile-only` succeeds; lockfile still resolves + `@types/react@18.3.23` and `@types/react-dom@18.3.7`. +- `pnpm --filter @roo-code/vscode-webview check-types` → passes. +- `pnpm --filter @roo-code/cli check-types` → passes. + +## §5 Co-author — see §0. diff --git a/ai_plans/2026-06-27_zoo-470-diff-fuzzy-threshold-DEFERRED.md b/ai_plans/2026-06-27_zoo-470-diff-fuzzy-threshold-DEFERRED.md new file mode 100644 index 0000000000..11a7f9eefa --- /dev/null +++ b/ai_plans/2026-06-27_zoo-470-diff-fuzzy-threshold-DEFERRED.md @@ -0,0 +1,45 @@ +# Zoo PR #470 — configurable diffFuzzyThreshold — DEFERRED (needs dedicated session) + +Upstream: Zoo-Code-Org/Zoo-Code PR #470, commit `518bae479`, merged 2026-06-23. +Author: edelauna (+ coderabbitai bot, dropped). Fixes "Edit Unsuccessful" friction. + +## Why deferred (not unsafe, but too large + diverged for an autonomous loop tick) + +- **32 files / 588 insertions**: a new opt-in `diffFuzzyThreshold` setting threaded + through types → ClineProvider → Task → the multi-search-replace diff strategy, + plus a webview slider, SettingsView/ExtensionStateContext wiring, **18 locales**, + and **4 test files**. +- **Plumbing anchors diverge**: Zoo threads the setting next to `rateLimitClock` in + `Task` (TaskOptions + constructor) and at ClineProvider Task-creation sites — **our + fork has no `rateLimitClock`**, so each of ~10 sites needs individual re-anchoring. +- The core diff-strategy change itself is **low risk** (our `multi-search-replace.ts` + matches Zoo's pre-PR except trivial const→let; the `startLine !== undefined` → + `startLine` change is benign with 1-based line numbers). + +## Groundwork for the focused session (all diffs already analyzed) + +1. `packages/types/src/global-settings.ts`: add `export const DEFAULT_DIFF_FUZZY_THRESHOLD = 1.0` + and `diffFuzzyThreshold: z.number().min(0.5).max(1).optional()` to `globalSettingsSchema`. + **Skip** Zoo's unrelated import-reorder at the top of that file. +2. `packages/types/src/vscode-extension-host.ts`: add `diffFuzzyThreshold: number` to `ExtensionState`. +3. `src/core/diff/strategies/multi-search-replace.ts`: import `DEFAULT_DIFF_FUZZY_THRESHOLD`; + constructor clamps `Math.max(0.5, Math.min(1.0, fuzzyThreshold ?? DEFAULT_DIFF_FUZZY_THRESHOLD))`; + add Levenshtein-distance / search-length diagnostics to the no-match error; new + "Original Content" slice for the range branch. +4. `src/core/task/Task.ts`: add `diffFuzzyThreshold?: number` to `TaskOptions` and the + constructor destructuring (anchor on `initialStatus`, NOT `rateLimitClock`), then + `new MultiSearchReplaceDiffStrategy(diffFuzzyThreshold)`. +5. `src/core/webview/ClineProvider.ts`: destructure `diffFuzzyThreshold` from `getState()` + at the two `createTask`/rehydrate sites and pass to `new Task({...})`; add + `diffFuzzyThreshold: … ?? DEFAULT_DIFF_FUZZY_THRESHOLD` to getStateToPostToWebview and + getState return objects. Re-anchor away from `rateLimitClock` lines. +6. webview: `ContextManagementSettings.tsx` (Slider 0.5–1.0 step 0.01 under a new + `fileEdits.diffFuzzyThreshold` SearchableSetting), `SettingsView.tsx` (destructure + + pass prop + include in save payload), `ExtensionStateContext.tsx` (default). +7. i18n: add `contextManagement.fileEdits.diffFuzzyThreshold.{label,description}` to all + 18 `settings.json` locales (en text in the PR). +8. Tests: `multi-search-replace.spec.ts` (+218, diagnostics/clamp), `ClineProvider.spec.ts` + (+54, passthrough), `single-open-invariant.spec.ts` (+40), `ContextManagementSettings.spec.tsx` + (+21), `SettingsView.spec.tsx` (+38), `ExtensionStateContext.spec.tsx` (+3). + +Credit on port: `Co-authored-by: Elliott de Launay `. diff --git a/ai_plans/2026-06-27_zoo-567-apirequesttimeout-providers.md b/ai_plans/2026-06-27_zoo-567-apirequesttimeout-providers.md new file mode 100644 index 0000000000..84fa6df9f3 --- /dev/null +++ b/ai_plans/2026-06-27_zoo-567-apirequesttimeout-providers.md @@ -0,0 +1,60 @@ +# Port Zoo PR #567 — apply apiRequestTimeout consistently across providers + +## §0 Credit & provenance + +- Upstream: Zoo-Code-Org/Zoo-Code PR #567, commit `b747d56ba`, merged 2026-06-19. +- Authors: dw (Oh Daewoong), Naved Merchant. +- Commit trailers: + ``` + Co-authored-by: Oh Daewoong + Co-authored-by: Naved Merchant + ``` + +## §1 What & why + +Previously only `openai`/`lm-studio` and `BaseOpenAiCompatibleProvider` honored the +user's `apiRequestTimeout`. This wires it into **every** provider that builds an +OpenAI or Anthropic SDK client, via a single `timeoutMs` field on `BaseProvider`. + +## §2 Edits + +- `base-provider.ts`: add `protected readonly timeoutMs: number = getApiRequestTimeout()`. +- `timeout-config.ts`: refactor — `getApiRequestTimeout()` now returns `number` + (never `undefined`), clamps to the valid 1–3600s range (out-of-range/NaN/non-number + → 600s default), and `Math.round()`s the ms (avoids Anthropic SDK float-validation throw). +- `base-openai-compatible-provider.ts` + `openai.ts`: use `this.timeoutMs` instead of + calling `getApiRequestTimeout()` directly; drop the now-unused import. +- Add `timeout: this.timeoutMs` to client construction in: `anthropic`, `minimax`, + `anthropic-vertex` (3 sites), `openai-native`, `openai-codex`, `openrouter`, + `router-provider` (covers `lite-llm`/`vercel-ai-gateway` via inheritance), `requesty`, + `unbound`, `xai`, `qwen-code`. +- `src/package.json`: `tumble-code.apiRequestTimeout` → `type: integer`, `minimum: 1`. +- `package.nls*.json` (18 locales): updated description (drops the now-invalid + "0 = no timeout", lists unsupported providers). + +## §3 Divergence handled + +- **Skipped Zoo-only providers** `zoo-gateway` and `opencode-go` — they don't exist here. +- Kept our `roo-code`-branded request headers (`originator`, X-Unbound-Metadata) — out of + scope for this PR; only `timeout` was added. +- No changeset (our fork doesn't ship Zoo's changeset flow). + +## §4 Tests + +- Provider tests that mock `vscode` as `{}` (or via a global mock lacking + `getConfiguration`) threw once `BaseProvider`'s field initializer started calling + `getApiRequestTimeout()` at construction. Fixed by mocking `../utils/timeout-config` + to return a constant `600_000` in the 8 affected specs (anthropic-vertex, lite-llm, + openrouter, requesty, vercel-ai-gateway, vertex, vertex-credentials, vscode-lm). +- Added `timeout: 600_000` to the exact-args client-construction assertions in + anthropic-vertex, openrouter, requesty (×2), vercel-ai-gateway. +- `timeout-config.spec.ts`: replaced the obsolete "0/negative → undefined" tests with + out-of-range → default, plus min/max boundary and ms-rounding cases. + +## §5 Verify (binary acceptance) — all ✓ + +- `pnpm --filter tumble-code check-types` passes. +- `npx vitest run api/providers/__tests__ api/providers/utils/__tests__/timeout-config` + → 46 files, 904 pass, 1 skipped. + +## §6 Co-authors — see §0. diff --git a/ai_plans/2026-06-27_zoo-599-fireworks-kimi-k2p7-code.md b/ai_plans/2026-06-27_zoo-599-fireworks-kimi-k2p7-code.md new file mode 100644 index 0000000000..cf6b20da14 --- /dev/null +++ b/ai_plans/2026-06-27_zoo-599-fireworks-kimi-k2p7-code.md @@ -0,0 +1,27 @@ +# Port Zoo PR #599 — add Fireworks kimi-k2p7-code model + +## §0 Credit & provenance + +- Upstream: Zoo-Code-Org/Zoo-Code PR #599, commit `74583b55e`, merged 2026-06-20. +- Author: Povilas Kanapickas. + +## §1 What & why + +New model support: Fireworks `kimi-k2p7-code` (Moonshot AI coding model). Pure +additive change — no divergence concerns; our `fireworks.ts` matches the pre-PR +structure (type union, model map, neighboring deepseek-v4-pro / glm-5p1 anchors). + +## §2 Edits + +- `packages/types/src/providers/fireworks.ts`: + - add `"accounts/fireworks/models/kimi-k2p7-code"` to the `FireworksModelId` union + (after `kimi-k2p6`). + - add its entry to `fireworksModels` (maxTokens 16384, ctx 262144, images+cache+ + reasoning+temperature, in 0.95 / out 4.0 / cacheReads 0.19), inserted after + `deepseek-v4-pro`, before `glm-5p1` — matching upstream placement. +- `src/api/providers/__tests__/fireworks.spec.ts`: add the model-config test. + +## §3 Verify (binary acceptance) — all ✓ + +- `pnpm --filter @roo-code/types check-types` passes. +- `npx vitest run api/providers/__tests__/fireworks` → 32 pass. diff --git a/ai_plans/2026-06-27_zoo-619-apply-diff-prompt-gemini.md b/ai_plans/2026-06-27_zoo-619-apply-diff-prompt-gemini.md new file mode 100644 index 0000000000..275abd0336 --- /dev/null +++ b/ai_plans/2026-06-27_zoo-619-apply-diff-prompt-gemini.md @@ -0,0 +1,34 @@ +# Port Zoo PR #619 — enhance apply_diff prompt for weaker models (Gemini) + +## §0 Credit & provenance + +- Upstream: Zoo-Code-Org/Zoo-Code PR #619, merged 2026-06-24. +- Author: Andrew Schmeder. +- Commit trailer: + ``` + Co-authored-by: Andrew Schmeder <149117631+awschmeder@users.noreply.github.com> + ``` + +## §1 What & why (weak-model robustness) + +Improves the `apply_diff` native-tool parameter description to raise weak-model +(Gemini, etc.) success rates: + +- `:start_line:` reworded from "is required" → "strongly recommended" (matches our + diff strategy, which already tolerates a missing start line). +- Adds a CRITICAL section spelling out exact `:start_line:[integer]` syntax (no + `:220` / `:start_line=220` shorthands), the 100% whitespace-exact match rule, and + the `-------` separator placement. + +Our `apply_diff.ts` matched Zoo's pre-PR exactly; clean port. Aligns with +[[feedback_design_for_weak_models]]. + +## §2 Edits + +- `src/core/prompts/tools/native-tools/apply_diff.ts`: update `DIFF_PARAMETER_DESCRIPTION`. +- Skipped Zoo's changeset file (our fork doesn't use that flow). + +## §3 Verify (binary acceptance) — all ✓ + +- No snapshot/spec asserts the old wording. +- `pnpm --filter tumble-code check-types` passes. diff --git a/ai_plans/2026-06-27_zoo-642-vite-8-0-16-security.md b/ai_plans/2026-06-27_zoo-642-vite-8-0-16-security.md new file mode 100644 index 0000000000..2dba011335 --- /dev/null +++ b/ai_plans/2026-06-27_zoo-642-vite-8-0-16-security.md @@ -0,0 +1,37 @@ +# Port Zoo PR #642 — vite 8.0.16 security floor + +## §0 Credit & provenance + +- Upstream: Zoo-Code-Org/Zoo-Code PR #642, commit `4bd064553`, merged 2026-06-18. +- Author: renovate[bot] only (bot — no human co-author to credit). + +## §1 What & why + +Security update of `vite` to `8.0.16`. Zoo pins exact versions (their fork ran +#423's "pin dependencies"), and also carries a `pnpm.overrides.vite` entry — **we +have neither**: our webview declares `vite: "^8.0.13"` (caret) and our root +`pnpm.overrides` has no vite/rollup entry. + +Our lockfile **already resolves `vite@8.0.16`** (the caret allows it), so the +vulnerable versions are already not installed. The meaningful, convention- +consistent port for our fork is to raise the declared floor so the patched +version can't silently regress on a future resolution. + +## §2 Edits + +- `webview-ui/package.json`: `"vite": "^8.0.13"` → `"^8.0.16"` (keep our caret + style; do NOT pin exact or add a root override — that's Zoo's divergence). +- Refresh `pnpm-lock.yaml` (expected near no-op; 8.0.16 already resolved). + +## §3 Scope cuts (divergence) + +- Do NOT add `pnpm.overrides.vite` / `rollup` / `esbuild: 0.28.1` from Zoo's root + package.json — our overrides intentionally differ. +- Do NOT pin exact `8.0.16` (our fork uses carets for vite). + +## §4 Verify (binary acceptance) + +- `grep '"vite"' webview-ui/package.json` shows `^8.0.16`. +- `pnpm install --lockfile-only` succeeds; lockfile resolves `vite@8.0.16`, no + `vite@8.0.1[0-5]` for the webview workspace. +- `pnpm --filter @roo-code/vscode-webview check-types` → passes. diff --git a/ai_plans/2026-06-27_zoo-657-rules-management-ui-DEFERRED.md b/ai_plans/2026-06-27_zoo-657-rules-management-ui-DEFERRED.md new file mode 100644 index 0000000000..7ad58f365b --- /dev/null +++ b/ai_plans/2026-06-27_zoo-657-rules-management-ui-DEFERRED.md @@ -0,0 +1,35 @@ +# Zoo PR #657 — rules management UI — DEFERRED (dedicated feature session) + +Upstream: Zoo-Code-Org/Zoo-Code PR #657, commit `f75b64e28`, merged 2026-06-26. +Author: Ivan Ramadhan Arifin. Credit on port: +`Co-authored-by: Ivan Ramadhan Arifin <…>` (resolve email from `zoo-prs show 657`). + +## Why deferred + +- **34 files / 3217 insertions** — the largest item in the batch. A complete new + rules-management subsystem, far beyond a safe autonomous loop tick. +- **New backend**: `src/services/rules/rules.ts` (+408), `src/core/webview/rulesMessageHandler.ts` + (+170), `packages/types/src/rules.ts` (+34). Our fork has **no** `src/services/rules` + dir today — rules loading lives in `src/services/roo-config/index.ts` and + `src/core/prompts/sections/custom-instructions.ts`. The new service must be + reconciled with that existing loader (and our `useAgentRules` / AGENTS.md support) + to avoid two competing rules pipelines. +- **New UI**: `RulesSettings.tsx` (+207), `CreateRuleDialog.tsx` (+218), wired through + `SettingsView.tsx`, `ExtensionStateContext.tsx`, `vscode-extension-host.ts`. +- **~1466 lines of new tests** across 6 files (rules.spec, rulesMessageHandler.spec, + webviewMessageHandler.spec, CreateRuleDialog.spec, RulesSettings.spec, + ExtensionStateContext.spec) + 18 locale `settings.json` updates. + +## Dedicated-session checklist + +1. Add `packages/types/src/rules.ts` + export from `index.ts`; extend `ExtensionState` + in `vscode-extension-host.ts`. +2. Port `src/services/rules/rules.ts` — **first audit `src/services/roo-config/index.ts`** + for overlap; decide whether the new service wraps or replaces our loader, and keep + AGENTS.md / `.roo/rules` behavior intact (do not regress `useAgentRules`). +3. Port `rulesMessageHandler.ts` and wire its cases into `webviewMessageHandler.ts`. +4. Port `RulesSettings.tsx` + `CreateRuleDialog.tsx`; wire into `SettingsView.tsx` + and `ExtensionStateContext.tsx` (re-anchor against our divergence). +5. Port the 6 test files; add the 18 locale strings. +6. Verify: types + src + webview typecheck; run the 6 new specs; manual smoke of the + Rules settings tab. diff --git a/ai_plans/2026-06-27_zoo-659-undici-6-27-0-security.md b/ai_plans/2026-06-27_zoo-659-undici-6-27-0-security.md new file mode 100644 index 0000000000..baed50898e --- /dev/null +++ b/ai_plans/2026-06-27_zoo-659-undici-6-27-0-security.md @@ -0,0 +1,35 @@ +# Port Zoo PR #659 — undici 6.27.0 security update + +## §0 Credit & provenance + +- Upstream: Zoo-Code-Org/Zoo-Code PR #659, commit `f798462f4`, merged 2026-06-20. +- Author: Elliott de Launay (renovate[bot] dropped as a bot). +- Commit trailer: + ``` + Co-authored-by: Elliott de Launay + ``` + +## §1 What & why + +Security update of `undici` to `6.27.0`. **Unlike the vite bump (#642), this one is +NOT already satisfied here**: our lockfile resolved `undici@6.26.0` (below the +patched 6.27.0) via the `^6.21.3` floor. Bumping the floor pulls in the fix. + +## §2 Edits + +- `package.json` (pnpm.overrides): `"undici": "^6.21.3"` → `"^6.27.0"`. +- `src/package.json` (devDeps): `"undici": "^6.21.3"` → `"^6.27.0"`. +- Refresh `pnpm-lock.yaml` → now resolves `undici@6.27.0`. + +## §3 Scope cuts (divergence / noise) + +- Our root override was already `^6.21.3` (Zoo's pre-PR was `>=5.29.0`); kept our + caret convention rather than Zoo's exact `6.27.0` pin. +- **Did NOT port the `ClineProvider.flicker-free-cancel.spec.ts` changes** bundled in + this PR — they are an unrelated test refactor squashed into the renovate bump, not + part of the security fix, and our ClineProvider tests have diverged. + +## §4 Verify (binary acceptance) — all ✓ + +- `pnpm install --lockfile-only` succeeds; lockfile resolves `undici@6.27.0`, no 6.26.0. +- `pnpm --filter tumble-code check-types` passes. diff --git a/ai_plans/2026-06-27_zoo-662-followup-non-array-type-error.md b/ai_plans/2026-06-27_zoo-662-followup-non-array-type-error.md new file mode 100644 index 0000000000..5363f62e72 --- /dev/null +++ b/ai_plans/2026-06-27_zoo-662-followup-non-array-type-error.md @@ -0,0 +1,48 @@ +# Port Zoo PR #662 — ask_followup_question: report non-array follow_up as a type error + +## §0 Credit & provenance + +- Upstream: Zoo-Code-Org/Zoo-Code PR #662, commit `37008b7f6`, merged 2026-06-22. Fixes #511. +- Author: edelauna (Elliott de Launay). +- Commit trailer: + ``` + Co-authored-by: Elliott de Launay + ``` + +## §1 What & why (weak-model robustness) + +When a weak model emits `follow_up` as a non-array (a keyed object / string / number, +common from incremental JSON parsing), the tool reported the misleading +"Missing value for required parameter 'follow_up'" — so the model kept retrying the +same malformed payload. The fix distinguishes: + +- `follow_up` null/undefined → missing-parameter error (unchanged), vs +- `follow_up` present-but-not-an-array → a precise "must be an array" type error. + +Directly aligns with our weak-model design work ([[feedback_design_for_weak_models]]). +Our fork was at the exact pre-PR state (`if (!follow_up || !Array.isArray(follow_up))` +collapsed both cases into the missing-param branch). + +## §2 Edits + +- `src/core/tools/AskFollowupQuestionTool.ts`: + - add a `recordValidationError(message)` helper (increments mistake count, records + tool error, `say("error", …)`, pushes `formatResponse.toolError(message)`). + - split the validation: null/undefined → `recordMissingParamError`; non-array → + `recordValidationError("The 'follow_up' parameter must be an array … Retry with +'follow_up' as a JSON array.")`. + - interface comment documenting the runtime guard. +- `src/core/assistant-message/NativeToolCallParser.ts`: explanatory comment on the + `ask_followup_question` case (it already forwards the raw present-but-non-array + value so the tool can emit the precise error). +- `src/core/tools/__tests__/askFollowupQuestionTool.spec.ts`: strengthen the string + case to assert the type-error message (not "Missing value"), and add an object case + asserting `say("error", …)`, the pushed "must be an array" result, and that + `task.ask` is NOT called with an invalid payload. + +## §3 Verify (binary acceptance) — all ✓ + +- `npx vitest run core/tools/__tests__/askFollowupQuestionTool` → 36 pass. +- `pnpm --filter tumble-code check-types` passes. + +## §4 Co-author — see §0. diff --git a/ai_plans/2026-06-27_zoo-670-remove-unused-vscode-test-cli.md b/ai_plans/2026-06-27_zoo-670-remove-unused-vscode-test-cli.md new file mode 100644 index 0000000000..1824399b51 --- /dev/null +++ b/ai_plans/2026-06-27_zoo-670-remove-unused-vscode-test-cli.md @@ -0,0 +1,38 @@ +# Port Zoo PR #670 — remove unused @vscode/test-cli + .vscode-test.mjs + +## §0 Credit & provenance + +- Upstream: Zoo-Code-Org/Zoo-Code PR #670, commit `ad7dcfeab`, merged 2026-06-20. +- Author: Elliott de Launay (renovate[bot] dropped as a bot). +- Commit trailer: + ``` + Co-authored-by: Elliott de Launay + ``` + +## §1 What & why + +The PR title says "update @vscode/test-cli to v0.0.12", but its second commit +("removing unused dep") instead **deletes** the `@vscode/test-cli` runner and its +`.vscode-test.mjs` config — they were unused. + +Verified the same holds in our fork: `apps/vscode-e2e` runs e2e via +`runTest.js` (the `@vscode/test-electron` path), not the `vscode-test` CLI. No +script invokes `vscode-test`, and nothing imports `.vscode-test.mjs` except the +file itself. So both are dead here too. + +## §2 Edits + +- Delete `apps/vscode-e2e/.vscode-test.mjs` (only consumer of `@vscode/test-cli`, + via `defineConfig`; carried our rebranded `RooVeterinaryInc.roo-cline` launchArg). +- Remove `"@vscode/test-cli": "^0.0.11"` from `apps/vscode-e2e/package.json`. +- Refresh `pnpm-lock.yaml` (prunes the test-cli dep tree, -161 lines). + +## §3 Scope cuts + +- Ignored `icon-map.json` "vscode-test" entries — unrelated material-icon mappings. + +## §4 Verify (binary acceptance) — all ✓ + +- No remaining `@vscode/test-cli` / `.vscode-test` references in tracked source. +- `pnpm install --lockfile-only` succeeds. +- `pnpm --filter @roo-code/vscode-e2e check-types` passes (e2e doesn't use the CLI). diff --git a/ai_plans/2026-06-27_zoo-671-execa-9-6-1.md b/ai_plans/2026-06-27_zoo-671-execa-9-6-1.md new file mode 100644 index 0000000000..7b1d832a90 --- /dev/null +++ b/ai_plans/2026-06-27_zoo-671-execa-9-6-1.md @@ -0,0 +1,32 @@ +# Port Zoo PR #671 — bump execa to 9.6.1 + +## §0 Credit & provenance + +- Upstream: Zoo-Code-Org/Zoo-Code PR #671, merged 2026-06-20. +- Author: Elliott de Launay (renovate[bot] dropped as a bot). +- Commit trailer: + ``` + Co-authored-by: Elliott de Launay + ``` + +## §1 What & why + +Routine (non-security) dependency bump of `execa` 9.5.3 → 9.6.1 in the extension. +Our src lockfile resolved 9.5.3 via the `^9.5.2` floor; raising the floor pulls +in 9.6.1. + +## §2 Edits + +- `src/package.json`: `"execa": "^9.5.2"` → `"^9.6.1"` (caret convention). +- Refresh `pnpm-lock.yaml` → src now resolves `execa@9.6.1`. + +## §3 Scope cuts + +- PR only touched `src/package.json`; left `apps/cli`, `packages/core`, + `packages/evals` execa ranges untouched (faithful to upstream scope; pnpm already + carries multiple execa versions across the monorepo). + +## §4 Verify (binary acceptance) — all ✓ + +- `pnpm install --lockfile-only` succeeds; src resolves `execa@9.6.1`. +- `pnpm --filter tumble-code check-types` passes (execa API unchanged for our usage). diff --git a/ai_plans/2026-06-27_zoo-673-axios-1-18-0.md b/ai_plans/2026-06-27_zoo-673-axios-1-18-0.md new file mode 100644 index 0000000000..75aa03f758 --- /dev/null +++ b/ai_plans/2026-06-27_zoo-673-axios-1-18-0.md @@ -0,0 +1,39 @@ +# Port Zoo PR #673 — bump axios to ^1.18.0 + +## §0 Credit & provenance + +- Upstream: Zoo-Code-Org/Zoo-Code PR #673, commit `ccf07eba3`, merged 2026-06-20. +- Author: Elliott de Launay (renovate[bot] dropped as a bot). +- Commit trailer: + ``` + Co-authored-by: Elliott de Launay + ``` + +## §1 What & why + +Renovate update of `axios` to v1.18.0. Zoo's PR left `package.json` at `^1.16.1` +and only refreshed the lockfile (a large -550-line dedup). Our src lockfile pinned +`axios@1.16.1`. + +For our fork the intent-documenting port is to raise the declared floor to +`^1.18.0` so the update is explicit; `pnpm install` then resolves our direct dep +to `axios@1.18.1` (latest 1.18.x, ≥ the target). + +## §2 Edits + +- `src/package.json`: `"axios": "^1.16.1"` → `"^1.18.0"`. +- Refresh `pnpm-lock.yaml` (targeted; direct axios → 1.18.1). + +## §3 Scope cuts (divergence / noise) + +- **Did NOT port `renovate.json`** — Zoo's renovate/bot config; our own deps flow + owns that (rubric: skip CI/bot config). +- Did not chase the remaining transitive `axios@1.16.1` (pulled by another + package's subtree, not our direct dependency). +- Did not reproduce Zoo's full 712-line lockfile dedup — our `pnpm install` + produces our own minimal resolution. + +## §4 Verify (binary acceptance) — all ✓ + +- `pnpm install --lockfile-only` succeeds; our direct axios resolves `1.18.1`. +- `pnpm --filter tumble-code check-types` passes. diff --git a/ai_plans/2026-06-27_zoo-691-delegation-atomic-update.md b/ai_plans/2026-06-27_zoo-691-delegation-atomic-update.md new file mode 100644 index 0000000000..d6831f2597 --- /dev/null +++ b/ai_plans/2026-06-27_zoo-691-delegation-atomic-update.md @@ -0,0 +1,50 @@ +# Port Zoo PR #691 — serialize delegateParentAndOpenChild with atomicReadAndUpdate + +## §0 Credit & provenance + +- Upstream: Zoo-Code-Org/Zoo-Code PR #691, commit `667096238`, merged 2026-06-25. +- Authors: edelauna (Elliott de Launay), Naved Merchant. +- Commit trailers: + ``` + Co-authored-by: Elliott de Launay + Co-authored-by: Naved Merchant + ``` + +## §1 What & why (concurrency fix) + +`delegateParentAndOpenChild` persisted parent delegation metadata with a separate +read (`getTaskWithId`) then write (`updateTaskHistory`) — a concurrent writer could +interleave and clobber the read-modify-write. This adds +`TaskHistoryStore.atomicReadAndUpdate(taskId, updater)`, which reads from cache and +writes back within a single lock acquisition, and switches delegation to use it. +Builds on our existing delegation work. + +Our `TaskHistoryStore.ts` matched Zoo's pre-PR exactly, and our ClineProvider's +step-5 block matched, so the core port applied cleanly. + +## §2 Edits + +- `src/core/task-persistence/TaskHistoryStore.ts`: + - extract `_upsertUnlocked()` from `upsert()` (so it can run inside a held lock). + - add `atomicReadAndUpdate()` — deep-copies the cached item, runs the pure updater, + guards the id, and persists via `_upsertUnlocked` inside `withLock`. +- `src/core/webview/ClineProvider.ts`: step 5 of `delegateParentAndOpenChild` now + calls `taskHistoryStore.atomicReadAndUpdate(...)`; after the lock releases it + invalidates `recentTasksCache` and (when `isViewLaunched`) posts + `taskHistoryItemUpdated` to the webview. + +## §3 Tests + +- Added `src/__tests__/delegation-concurrent.spec.ts` (new upstream file, 4 cases + exercising the atomic serialization / no-interleave invariant). +- Adopted the upstream `provider-delegation.spec.ts` rewrite (makeStoreStub + + `atomicReadAndUpdate` assertions, incl. the new `isViewLaunched` postMessage cases) + **minus its rollback test** — our `delegateParentAndOpenChild` logs on persist + failure and continues (no rollback), so that upstream test does not apply here. + (Our fork had already dropped the corresponding pre-PR rollback test.) + +## §4 Verify (binary acceptance) — all ✓ + +- `pnpm --filter tumble-code check-types` passes. +- `npx vitest run __tests__/provider-delegation __tests__/delegation-concurrent` → 8 pass. +- `npx vitest run core/task-persistence/__tests__/TaskHistoryStore` → 28 pass. diff --git a/apps/cli/package.json b/apps/cli/package.json index 9276f17053..74be0a5bcc 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -41,10 +41,11 @@ "@roo-code/config-eslint": "workspace:^", "@roo-code/config-typescript": "workspace:^", "@types/node": "^24.1.0", - "@types/react": "^19.1.6", + "@types/react": "18.3.23", "ink-testing-library": "^4.0.0", "rimraf": "^6.0.1", "tsup": "^8.4.0", + "tsx": "^4.19.3", "vitest": "^3.2.3" } } diff --git a/apps/vscode-e2e/.vscode-test.mjs b/apps/vscode-e2e/.vscode-test.mjs deleted file mode 100644 index c83f12c4bb..0000000000 --- a/apps/vscode-e2e/.vscode-test.mjs +++ /dev/null @@ -1,16 +0,0 @@ -/** - * See: https://code.visualstudio.com/api/working-with-extensions/testing-extension - */ - -import { defineConfig } from "@vscode/test-cli" - -export default defineConfig({ - label: "integrationTest", - files: "out/suite/**/*.test.js", - workspaceFolder: ".", - mocha: { - ui: "tdd", - timeout: 60000, - }, - launchArgs: ["--enable-proposed-api=RooVeterinaryInc.roo-cline", "--disable-extensions"], -}) diff --git a/apps/vscode-e2e/package.json b/apps/vscode-e2e/package.json index db647a8737..e7962b9b48 100644 --- a/apps/vscode-e2e/package.json +++ b/apps/vscode-e2e/package.json @@ -16,7 +16,6 @@ "@types/mocha": "^10.0.10", "@types/node": "^20.19.25", "@types/vscode": "^1.95.0", - "@vscode/test-cli": "^0.0.11", "@vscode/test-electron": "^2.4.0", "glob": "^11.1.0", "mocha": "^11.1.0", diff --git a/knip.json b/knip.json index a3382bd2c1..7ffb7ef5c4 100644 --- a/knip.json +++ b/knip.json @@ -1,27 +1,82 @@ { - "$schema": "https://unpkg.com/knip@latest/schema.json", - "ignore": [ - "**/__tests__/**", - "apps/vscode-e2e/**", - "src/extension/api.ts", - "src/activate/**", - "src/workers/countTokens.ts", - "src/extension.ts", - "scripts/**", - "apps/cli/scripts/**", - "@vscode/ripgrep" - ], + "$schema": "https://unpkg.com/knip@5/schema.json", + "ignore": ["**/__tests__/**", "apps/vscode-e2e/**", "scripts/**", "apps/cli/scripts/**", "self-hosted-cloudapi/**"], + "ignoreDependencies": ["lint-staged", "only-allow", "ovsx"], + "ignoreExportsUsedInFile": true, "workspaces": { "src": { - "entry": ["extension.ts"], - "project": ["**/*.ts"] + "entry": ["extension.ts", "extension/api.ts", "workers/countTokens.ts"], + "project": ["**/*.ts", "!**/__tests__/**"], + "ignoreDependencies": [ + "@ai-sdk/amazon-bedrock", + "@ai-sdk/baseten", + "@ai-sdk/deepseek", + "@ai-sdk/fireworks", + "@ai-sdk/google", + "@ai-sdk/google-vertex", + "@ai-sdk/mistral", + "@ai-sdk/xai", + "@roo-code/config-typescript", + "@types/mocha", + "@types/node-cache", + "@types/vscode", + "@vscode/codicons", + "esbuild-wasm", + "sambanova-ai-provider", + "tree-sitter-wasms", + "vscode-material-icons", + "zhipu-ai-provider" + ] }, "webview-ui": { "entry": ["src/index.tsx"], - "project": ["src/**/*.{ts,tsx}", "../src/shared/*.ts"] + "project": ["src/**/*.{ts,tsx}", "../src/shared/*.ts"], + "ignoreDependencies": [ + "@roo-code/config-typescript", + "@types/katex", + "@types/stacktrace-js", + "babel-plugin-react-compiler", + "katex", + "react-compiler-runtime", + "rehype-highlight", + "source-map", + "tailwindcss", + "tailwindcss-animate" + ] }, - "packages/{build,cloud,evals,ipc,telemetry,types}": { + "apps/cli": { + "project": ["src/**/*.ts", "src/**/*.tsx"], + "ignoreDependencies": ["@vscode/ripgrep", "cross-spawn"] + }, + "packages/config-typescript": { + "ignoreUnresolved": ["vitest/globals"] + }, + "packages/core": { + "project": ["src/**/*.ts"], + "ignoreDependencies": ["esbuild"] + }, + "packages/vscode-shim": { + "project": ["src/**/*.ts"] + }, + "packages/cloud": { + "project": ["src/**/*.ts"], + "ignoreDependencies": ["@types/vscode", "ioredis", "p-wait-for"] + }, + "packages/telemetry": { + "project": ["src/**/*.ts"], + "ignoreDependencies": ["@types/vscode"] + }, + "packages/{build,ipc,types}": { "project": ["src/**/*.ts"] } + }, + "rules": { + "classMembers": "off", + "duplicates": "warn", + "enumMembers": "warn", + "exports": "warn", + "nsExports": "warn", + "types": "warn", + "nsTypes": "warn" } } diff --git a/package.json b/package.json index a61f905748..1aa72559ef 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "install:vsix:nightly": "pnpm install --frozen-lockfile && pnpm clean && pnpm vsix:nightly && node scripts/install-vsix.js --nightly", "code-server:install": "node scripts/code-server.js", "changeset:version": "cp CHANGELOG.md src/CHANGELOG.md && changeset version && cp -vf src/CHANGELOG.md .", - "knip": "knip --include files", + "knip": "knip", "evals": "dotenvx run -f packages/evals/.env.development packages/evals/.env.local -- docker compose -f packages/evals/docker-compose.yml --profile server --profile runner up --build --scale runner=0", "npm:publish:types": "pnpm --filter @roo-code/types npm:publish" }, @@ -61,13 +61,13 @@ "overrides": { "tar-fs": ">=3.1.1", "esbuild": ">=0.25.0", - "undici": "^6.21.3", + "undici": "^6.27.0", "brace-expansion": "^2.0.2", "form-data": ">=4.0.4", "bluebird": ">=3.7.2", "glob": ">=11.1.0", - "@types/react": "^18.3.23", - "@types/react-dom": "^18.3.5", + "@types/react": "18.3.23", + "@types/react-dom": "18.3.7", "zod": "3.25.76", "picomatch@>=4": "4.0.4" } diff --git a/packages/types/src/mcp.ts b/packages/types/src/mcp.ts index 7e9cd59745..8cbe0c5221 100644 --- a/packages/types/src/mcp.ts +++ b/packages/types/src/mcp.ts @@ -115,6 +115,24 @@ export type McpResourceResponse = { }> } +export type McpResourceLinkAnnotations = { + audience?: ("user" | "assistant")[] + priority?: number + lastModified?: string +} + +// Defined in MCP spec 2025-06-18: https://modelcontextprotocol.io/specification/2025-06-18/server/tools +export type McpResourceLink = { + type: "resource_link" + uri: string + name: string + description?: string + mimeType?: string + title?: string + size?: number + annotations?: McpResourceLinkAnnotations +} + export type McpToolCallResponse = { _meta?: Record // eslint-disable-line @typescript-eslint/no-explicit-any content: Array< @@ -141,6 +159,7 @@ export type McpToolCallResponse = { blob?: string } } + | McpResourceLink > isError?: boolean } diff --git a/packages/types/src/providers/fireworks.ts b/packages/types/src/providers/fireworks.ts index 12e24c161e..3b18ad50b8 100644 --- a/packages/types/src/providers/fireworks.ts +++ b/packages/types/src/providers/fireworks.ts @@ -6,6 +6,7 @@ export type FireworksModelId = | "accounts/fireworks/models/kimi-k2-thinking" | "accounts/fireworks/models/kimi-k2p5" | "accounts/fireworks/models/kimi-k2p6" + | "accounts/fireworks/models/kimi-k2p7-code" | "accounts/fireworks/models/minimax-m2" | "accounts/fireworks/models/minimax-m2p1" | "accounts/fireworks/models/qwen3-235b-a22b-instruct-2507" @@ -265,6 +266,20 @@ export const fireworksModels = { description: "DeepSeek V4 Pro is the latest iteration of the DeepSeek model family, with improved reasoning, code generation, and instruction following over the V3 series.", }, + "accounts/fireworks/models/kimi-k2p7-code": { + maxTokens: 16384, + contextWindow: 262144, + supportsImages: true, + supportsPromptCache: true, + preserveReasoning: true, + supportsTemperature: true, + defaultTemperature: 1.0, + inputPrice: 0.95, + outputPrice: 4.0, + cacheReadsPrice: 0.19, + description: + "Kimi K2.7 Code is Moonshot AI's latest coding-focused model, building on K2.6 with enhanced code generation, reasoning, and unified vision/text understanding for software development workflows.", + }, "accounts/fireworks/models/glm-5p1": { maxTokens: 25344, contextWindow: 202752, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ffd10ac8a8..821934b886 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,13 +7,13 @@ settings: overrides: tar-fs: '>=3.1.1' esbuild: '>=0.25.0' - undici: ^6.21.3 + undici: ^6.27.0 brace-expansion: ^2.0.2 form-data: '>=4.0.4' bluebird: '>=3.7.2' glob: '>=11.1.0' - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 zod: 3.25.76 picomatch@>=4: 4.0.4 @@ -140,7 +140,7 @@ importers: specifier: ^24.1.0 version: 24.2.1 '@types/react': - specifier: ^18.3.23 + specifier: 18.3.23 version: 18.3.23 ink-testing-library: specifier: ^4.0.0 @@ -151,6 +151,9 @@ importers: tsup: specifier: ^8.4.0 version: 8.5.0(jiti@2.4.2)(postcss@8.5.15)(tsx@4.19.4)(typescript@5.8.3)(yaml@2.9.0) + tsx: + specifier: ^4.19.3 + version: 4.19.4 vitest: specifier: ^3.2.3 version: 3.2.4(@types/debug@4.1.12)(@types/node@24.2.1)(@vitest/ui@3.2.4)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.32.0)(tsx@4.19.4)(yaml@2.9.0) @@ -175,9 +178,6 @@ importers: '@types/vscode': specifier: ^1.95.0 version: 1.100.0 - '@vscode/test-cli': - specifier: ^0.0.11 - version: 0.0.11 '@vscode/test-electron': specifier: ^2.4.0 version: 2.5.2 @@ -324,10 +324,10 @@ importers: specifier: ^1.1.6 version: 1.1.6 '@types/react': - specifier: ^18.3.23 + specifier: 18.3.23 version: 18.3.23 '@types/react-dom': - specifier: ^18.3.5 + specifier: 18.3.7 version: 18.3.7(@types/react@18.3.23) tailwindcss: specifier: ^4 @@ -673,7 +673,7 @@ importers: version: 3.922.0 '@google/genai': specifier: ^1.29.1 - version: 1.29.1(@modelcontextprotocol/sdk@1.12.0) + version: 1.29.1(@modelcontextprotocol/sdk@1.26.0(zod@3.25.76)) '@lmstudio/sdk': specifier: ^1.1.1 version: 1.2.0 @@ -681,8 +681,8 @@ importers: specifier: ^1.9.18 version: 1.9.18(zod@3.25.76) '@modelcontextprotocol/sdk': - specifier: 1.12.0 - version: 1.12.0 + specifier: 1.26.0 + version: 1.26.0(zod@3.25.76) '@qdrant/js-client-rest': specifier: ^1.14.0 version: 1.14.0(typescript@5.8.3) @@ -711,8 +711,8 @@ importers: specifier: ^0.5.0 version: 0.5.0 axios: - specifier: ^1.16.1 - version: 1.16.1 + specifier: ^1.18.0 + version: 1.18.1 cheerio: specifier: ^1.0.0 version: 1.0.0 @@ -879,8 +879,8 @@ importers: specifier: ^7.2.0 version: 7.2.0 undici: - specifier: ^6.21.3 - version: 6.26.0 + specifier: ^6.27.0 + version: 6.27.0 uuid: specifier: ^11.1.1 version: 11.1.1 @@ -906,9 +906,6 @@ importers: '@ai-sdk/openai-compatible': specifier: ^2.0.28 version: 2.0.28(zod@3.25.76) - '@openrouter/ai-sdk-provider': - specifier: ^2.1.1 - version: 2.1.1(ai@6.0.77(zod@3.25.76))(zod@3.25.76) '@roo-code/build': specifier: workspace:^ version: link:../packages/build @@ -975,9 +972,6 @@ importers: '@types/vscode': specifier: ^1.84.0 version: 1.100.0 - '@vscode/test-electron': - specifier: ^2.5.2 - version: 2.5.2 '@vscode/vsce': specifier: 3.3.2 version: 3.3.2 @@ -988,8 +982,8 @@ importers: specifier: ^0.25.0 version: 0.25.12 execa: - specifier: ^9.5.2 - version: 9.5.3 + specifier: ^9.6.1 + version: 9.6.1 glob: specifier: '>=11.1.0' version: 11.1.0 @@ -999,27 +993,15 @@ importers: nock: specifier: ^14.0.4 version: 14.0.10 - npm-run-all2: - specifier: ^8.0.1 - version: 8.0.3 ovsx: specifier: 0.10.4 version: 0.10.4 rimraf: specifier: ^6.0.1 version: 6.0.1 - tsup: - specifier: ^8.4.0 - version: 8.5.0(jiti@2.4.2)(postcss@8.5.15)(tsx@4.19.4)(typescript@5.8.3)(yaml@2.9.0) - tsx: - specifier: ^4.19.3 - version: 4.19.4 vitest: specifier: ^3.2.3 version: 3.2.4(@types/debug@4.1.12)(@types/node@20.17.50)(@vitest/ui@3.2.4)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.32.0)(tsx@4.19.4)(yaml@2.9.0) - zod-to-ts: - specifier: ^1.2.0 - version: 1.2.0(typescript@5.8.3)(zod@3.25.76) webview-ui: dependencies: @@ -1122,9 +1104,6 @@ importers: katex: specifier: ^0.16.11 version: 0.16.22 - knuth-shuffle-seeded: - specifier: ^1.0.6 - version: 1.0.6 lru-cache: specifier: ^11.1.0 version: 11.1.0 @@ -1253,10 +1232,10 @@ importers: specifier: ^20.19.25 version: 20.19.41 '@types/react': - specifier: ^18.3.23 + specifier: 18.3.23 version: 18.3.23 '@types/react-dom': - specifier: ^18.3.5 + specifier: 18.3.7 version: 18.3.7(@types/react@18.3.23) '@types/shell-quote': specifier: ^1.7.5 @@ -1276,14 +1255,11 @@ importers: babel-plugin-react-compiler: specifier: ^1.0.0 version: 1.0.0 - identity-obj-proxy: - specifier: ^3.0.0 - version: 3.0.0 jsdom: specifier: ^26.0.0 version: 26.1.0 vite: - specifier: ^8.0.13 + specifier: ^8.0.16 version: 8.0.16(@types/node@20.19.41)(esbuild@0.25.9)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.9.0) vitest: specifier: ^3.2.3 @@ -1856,9 +1832,6 @@ packages: resolution: {integrity: sha512-H6bpd1JcDbuJsOS2dNft+CCGLzBqHJO/ST/4mMKhLAW641J6PpVJUw1szYsk/dTetdedbWxHpMkvFObOKeP8nw==} engines: {node: '>= 10'} - '@bcoe/v8-coverage@0.2.3': - resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - '@braintree/sanitize-url@7.1.1': resolution: {integrity: sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw==} @@ -2227,6 +2200,12 @@ packages: '@modelcontextprotocol/sdk': optional: true + '@hono/node-server@1.19.14': + resolution: {integrity: sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==} + engines: {node: '>=18.14.1'} + peerDependencies: + hono: ^4 + '@hookform/resolvers@5.1.1': resolution: {integrity: sha512-J/NVING3LMAEvexJkyTLjruSm7aOFx7QX21pzkiJfMoNG0wl5aFEjLTl7ay7IQb9EWY6AkrBy7tHL2Alijpdcg==} peerDependencies: @@ -2429,10 +2408,6 @@ packages: resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} engines: {node: '>=18.0.0'} - '@istanbuljs/schema@0.1.3': - resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} - engines: {node: '>=8'} - '@jest/expect-utils@29.7.0': resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2580,9 +2555,15 @@ packages: '@mixmark-io/domino@2.2.0': resolution: {integrity: sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==} - '@modelcontextprotocol/sdk@1.12.0': - resolution: {integrity: sha512-m//7RlINx1F3sz3KqwY1WWzVgTcYX52HYk4bJ1hkBXV3zccAEth+jRvG8DBRrdaQuRsPAJOx2MH3zaHNCKL7Zg==} + '@modelcontextprotocol/sdk@1.26.0': + resolution: {integrity: sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg==} engines: {node: '>=18'} + peerDependencies: + '@cfworker/json-schema': ^4.1.1 + zod: 3.25.76 + peerDependenciesMeta: + '@cfworker/json-schema': + optional: true '@mswjs/interceptors@0.39.6': resolution: {integrity: sha512-bndDP83naYYkfayr/qhBHMhk0YGwS1iv6vaEGcr0SQbO0IZtbOPqjKjds/WcG+bJA+1T5vCx6kprKOzn5Bg+Vw==} @@ -2777,13 +2758,6 @@ packages: '@open-draft/until@2.1.0': resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} - '@openrouter/ai-sdk-provider@2.1.1': - resolution: {integrity: sha512-UypPbVnSExxmG/4Zg0usRiit3auvQVrjUXSyEhm0sZ9GQnW/d8p/bKgCk2neh1W5YyRSo7PNQvCrAEBHZnqQkQ==} - engines: {node: '>=18'} - peerDependencies: - ai: ^6.0.0 - zod: 3.25.76 - '@opentelemetry/api@1.9.0': resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} @@ -2894,8 +2868,8 @@ packages: '@radix-ui/react-alert-dialog@1.1.13': resolution: {integrity: sha512-/uPs78OwxGxslYOG5TKeUsv9fZC0vo376cXSADdKirTmsLJU2au6L3n34c3p6W26rFDDDze/hwy4fYeNd0qdGA==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -2907,8 +2881,8 @@ packages: '@radix-ui/react-arrow@1.1.6': resolution: {integrity: sha512-2JMfHJf/eVnwq+2dewT3C0acmCWD3XiVA1Da+jTDqo342UlU13WvXtqHhG+yJw5JeQmu4ue2eMy6gcEArLBlcw==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -2920,8 +2894,8 @@ packages: '@radix-ui/react-arrow@1.1.7': resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -2933,8 +2907,8 @@ packages: '@radix-ui/react-checkbox@1.3.1': resolution: {integrity: sha512-xTaLKAO+XXMPK/BpVTSaAAhlefmvMSACjIhK9mGsImvX2ljcTDm8VGR1CuS1uYcNdR5J+oiOhoJZc5un6bh3VQ==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -2946,8 +2920,8 @@ packages: '@radix-ui/react-collapsible@1.1.10': resolution: {integrity: sha512-O2mcG3gZNkJ/Ena34HurA3llPOEA/M4dJtIRMa6y/cknRDC8XY5UZBInKTsUwW5cUue9A4k0wi1XU5fKBzKe1w==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -2959,8 +2933,8 @@ packages: '@radix-ui/react-collection@1.1.6': resolution: {integrity: sha512-PbhRFK4lIEw9ADonj48tiYWzkllz81TM7KVYyyMMw2cwHO7D5h4XKEblL8NlaRisTK3QTe6tBEhDccFUryxHBQ==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -2972,8 +2946,8 @@ packages: '@radix-ui/react-collection@1.1.7': resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -2985,7 +2959,7 @@ packages: '@radix-ui/react-compose-refs@1.1.2': resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} peerDependencies: - '@types/react': ^18.3.23 + '@types/react': 18.3.23 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': @@ -2994,7 +2968,7 @@ packages: '@radix-ui/react-context@1.1.2': resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} peerDependencies: - '@types/react': ^18.3.23 + '@types/react': 18.3.23 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': @@ -3003,8 +2977,8 @@ packages: '@radix-ui/react-dialog@1.1.13': resolution: {integrity: sha512-ARFmqUyhIVS3+riWzwGTe7JLjqwqgnODBUZdqpWar/z1WFs9z76fuOs/2BOWCR+YboRn4/WN9aoaGVwqNRr8VA==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -3016,7 +2990,7 @@ packages: '@radix-ui/react-direction@1.1.1': resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} peerDependencies: - '@types/react': ^18.3.23 + '@types/react': 18.3.23 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': @@ -3025,8 +2999,8 @@ packages: '@radix-ui/react-dismissable-layer@1.1.11': resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -3038,8 +3012,8 @@ packages: '@radix-ui/react-dismissable-layer@1.1.9': resolution: {integrity: sha512-way197PiTvNp+WBP7svMJasHl+vibhWGQDb6Mgf5mhEWJkgb85z7Lfl9TUdkqpWsf8GRNmoopx9ZxCyDzmgRMQ==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -3051,8 +3025,8 @@ packages: '@radix-ui/react-dropdown-menu@2.1.14': resolution: {integrity: sha512-lzuyNjoWOoaMFE/VC5FnAAYM16JmQA8ZmucOXtlhm2kKR5TSU95YLAueQ4JYuRmUJmBvSqXaVFGIfuukybwZJQ==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -3064,7 +3038,7 @@ packages: '@radix-ui/react-focus-guards@1.1.2': resolution: {integrity: sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==} peerDependencies: - '@types/react': ^18.3.23 + '@types/react': 18.3.23 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': @@ -3073,8 +3047,8 @@ packages: '@radix-ui/react-focus-scope@1.1.6': resolution: {integrity: sha512-r9zpYNUQY+2jWHWZGyddQLL9YHkM/XvSFHVcWs7bdVuxMAnCwTAuy6Pf47Z4nw7dYcUou1vg/VgjjrrH03VeBw==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -3091,7 +3065,7 @@ packages: '@radix-ui/react-id@1.1.1': resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} peerDependencies: - '@types/react': ^18.3.23 + '@types/react': 18.3.23 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': @@ -3100,8 +3074,8 @@ packages: '@radix-ui/react-label@2.1.7': resolution: {integrity: sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -3113,8 +3087,8 @@ packages: '@radix-ui/react-menu@2.1.14': resolution: {integrity: sha512-0zSiBAIFq9GSKoSH5PdEaQeRB3RnEGxC+H2P0egtnKoKKLNBH8VBHyVO6/jskhjAezhOIplyRUj7U2lds9A+Yg==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -3126,8 +3100,8 @@ packages: '@radix-ui/react-popover@1.1.13': resolution: {integrity: sha512-84uqQV3omKDR076izYgcha6gdpN8m3z6w/AeJ83MSBJYVG/AbOHdLjAgsPZkeC/kt+k64moXFCnio8BbqXszlw==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -3139,8 +3113,8 @@ packages: '@radix-ui/react-popper@1.2.6': resolution: {integrity: sha512-7iqXaOWIjDBfIG7aq8CUEeCSsQMLFdn7VEE8TaFz704DtEzpPHR7w/uuzRflvKgltqSAImgcmxQ7fFX3X7wasg==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -3152,8 +3126,8 @@ packages: '@radix-ui/react-popper@1.2.8': resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -3165,8 +3139,8 @@ packages: '@radix-ui/react-portal@1.1.8': resolution: {integrity: sha512-hQsTUIn7p7fxCPvao/q6wpbxmCwgLrlz+nOrJgC+RwfZqWY/WN+UMqkXzrtKbPrF82P43eCTl3ekeKuyAQbFeg==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -3178,8 +3152,8 @@ packages: '@radix-ui/react-portal@1.1.9': resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -3191,8 +3165,8 @@ packages: '@radix-ui/react-presence@1.1.4': resolution: {integrity: sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -3204,8 +3178,8 @@ packages: '@radix-ui/react-presence@1.1.5': resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -3217,8 +3191,8 @@ packages: '@radix-ui/react-primitive@2.1.2': resolution: {integrity: sha512-uHa+l/lKfxuDD2zjN/0peM/RhhSmRjr5YWdk/37EnSv1nJ88uvG85DPexSm8HdFQROd2VdERJ6ynXbkCFi+APw==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -3230,8 +3204,8 @@ packages: '@radix-ui/react-primitive@2.1.3': resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -3243,8 +3217,8 @@ packages: '@radix-ui/react-progress@1.1.6': resolution: {integrity: sha512-QzN9a36nKk2eZKMf9EBCia35x3TT+SOgZuzQBVIHyRrmYYi73VYBRK3zKwdJ6az/F5IZ6QlacGJBg7zfB85liA==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -3256,8 +3230,8 @@ packages: '@radix-ui/react-radio-group@1.3.8': resolution: {integrity: sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -3269,8 +3243,8 @@ packages: '@radix-ui/react-roving-focus@1.1.10': resolution: {integrity: sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -3282,8 +3256,8 @@ packages: '@radix-ui/react-roving-focus@1.1.11': resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -3295,8 +3269,8 @@ packages: '@radix-ui/react-roving-focus@1.1.9': resolution: {integrity: sha512-ZzrIFnMYHHCNqSNCsuN6l7wlewBEq0O0BCSBkabJMFXVO51LRUTq71gLP1UxFvmrXElqmPjA5VX7IqC9VpazAQ==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -3308,8 +3282,8 @@ packages: '@radix-ui/react-scroll-area@1.2.9': resolution: {integrity: sha512-YSjEfBXnhUELsO2VzjdtYYD4CfQjvao+lhhrX5XsHD7/cyUNzljF1FHEbgTPN7LH2MClfwRMIsYlqTYpKTTe2A==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -3321,8 +3295,8 @@ packages: '@radix-ui/react-select@2.2.4': resolution: {integrity: sha512-/OOm58Gil4Ev5zT8LyVzqfBcij4dTHYdeyuF5lMHZ2bIp0Lk9oETocYiJ5QC0dHekEQnK6L/FNJCceeb4AkZ6Q==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -3334,8 +3308,8 @@ packages: '@radix-ui/react-separator@1.1.6': resolution: {integrity: sha512-Izof3lPpbCfTM7WDta+LRkz31jem890VjEvpVRoWQNKpDUMMVffuyq854XPGP1KYGWWmjmYvHvPFeocWhFCy1w==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -3347,8 +3321,8 @@ packages: '@radix-ui/react-slider@1.3.4': resolution: {integrity: sha512-Cp6hEmQtRJFci285vkdIJ+HCDLTRDk+25VhFwa1fcubywjMUE3PynBgtN5RLudOgSCYMlT4jizCXdmV+8J7Y2w==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -3360,7 +3334,7 @@ packages: '@radix-ui/react-slot@1.2.2': resolution: {integrity: sha512-y7TBO4xN4Y94FvcWIOIh18fM4R1A8S4q1jhoz4PNzOoHsFcN8pogcFmZrTYAm4F9VRUrWP/Mw7xSKybIeRI+CQ==} peerDependencies: - '@types/react': ^18.3.23 + '@types/react': 18.3.23 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': @@ -3369,7 +3343,7 @@ packages: '@radix-ui/react-slot@1.2.3': resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} peerDependencies: - '@types/react': ^18.3.23 + '@types/react': 18.3.23 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': @@ -3378,8 +3352,8 @@ packages: '@radix-ui/react-tabs@1.1.12': resolution: {integrity: sha512-GTVAlRVrQrSw3cEARM0nAx73ixrWDPNZAruETn3oHCNP6SbZ/hNxdxp+u7VkIEv3/sFoLq1PfcHrl7Pnp0CDpw==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -3391,8 +3365,8 @@ packages: '@radix-ui/react-tooltip@1.2.6': resolution: {integrity: sha512-zYb+9dc9tkoN2JjBDIIPLQtk3gGyz8FMKoqYTb8EMVQ5a5hBcdHPECrsZVI4NpPAUOixhkoqg7Hj5ry5USowfA==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -3404,8 +3378,8 @@ packages: '@radix-ui/react-tooltip@1.2.8': resolution: {integrity: sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -3417,7 +3391,7 @@ packages: '@radix-ui/react-use-callback-ref@1.1.1': resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} peerDependencies: - '@types/react': ^18.3.23 + '@types/react': 18.3.23 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': @@ -3426,7 +3400,7 @@ packages: '@radix-ui/react-use-controllable-state@1.2.2': resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} peerDependencies: - '@types/react': ^18.3.23 + '@types/react': 18.3.23 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': @@ -3435,7 +3409,7 @@ packages: '@radix-ui/react-use-effect-event@0.0.2': resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} peerDependencies: - '@types/react': ^18.3.23 + '@types/react': 18.3.23 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': @@ -3444,7 +3418,7 @@ packages: '@radix-ui/react-use-escape-keydown@1.1.1': resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} peerDependencies: - '@types/react': ^18.3.23 + '@types/react': 18.3.23 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': @@ -3453,7 +3427,7 @@ packages: '@radix-ui/react-use-layout-effect@1.1.1': resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} peerDependencies: - '@types/react': ^18.3.23 + '@types/react': 18.3.23 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': @@ -3462,7 +3436,7 @@ packages: '@radix-ui/react-use-previous@1.1.1': resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==} peerDependencies: - '@types/react': ^18.3.23 + '@types/react': 18.3.23 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': @@ -3471,7 +3445,7 @@ packages: '@radix-ui/react-use-rect@1.1.1': resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} peerDependencies: - '@types/react': ^18.3.23 + '@types/react': 18.3.23 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': @@ -3480,7 +3454,7 @@ packages: '@radix-ui/react-use-size@1.1.1': resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} peerDependencies: - '@types/react': ^18.3.23 + '@types/react': 18.3.23 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': @@ -3489,8 +3463,8 @@ packages: '@radix-ui/react-visually-hidden@1.2.2': resolution: {integrity: sha512-ORCmRUbNiZIv6uV5mhFrhsIKw4UX/N3syZtyqvry61tbGm4JlgQuSn0hk5TwCARsCjkcnuRkSdCE3xfb+ADHew==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -3502,8 +3476,8 @@ packages: '@radix-ui/react-visually-hidden@1.2.3': resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==} peerDependencies: - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: @@ -4197,8 +4171,8 @@ packages: engines: {node: '>=18'} peerDependencies: '@testing-library/dom': ^10.0.0 - '@types/react': ^18.3.23 - '@types/react-dom': ^18.3.5 + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7 react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 peerDependenciesMeta: @@ -4499,7 +4473,7 @@ packages: '@types/react-dom@18.3.7': resolution: {integrity: sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==} peerDependencies: - '@types/react': ^18.3.23 + '@types/react': 18.3.23 '@types/react@18.3.23': resolution: {integrity: sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==} @@ -4679,11 +4653,6 @@ packages: '@vscode/ripgrep@1.17.0': resolution: {integrity: sha512-mBRKm+ASPkUcw4o9aAgfbusIu6H4Sdhw09bjeP1YOBFTJEZAnrnk6WZwzv8NEjgC82f7ILvhmb1WIElSugea6g==} - '@vscode/test-cli@0.0.11': - resolution: {integrity: sha512-qO332yvzFqGhBMJrp6TdwbIydiHgCtxXc2Nl6M58mbH/Z+0CyLR76Jzv4YWPEthhrARprzCRJUqzFvTHFhTj7Q==} - engines: {node: '>=18'} - hasBin: true - '@vscode/test-electron@2.5.2': resolution: {integrity: sha512-8ukpxv4wYe0iWMRQU18jhzJOHkeGKbnw7xWRX3Zw1WJA4cEKbHcmmLPdPrPtL6rhDcrlCZN+xKRpv09n4gRHYg==} engines: {node: '>=16'} @@ -4801,6 +4770,14 @@ packages: peerDependencies: zod: 3.25.76 + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} @@ -4843,10 +4820,6 @@ packages: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} - ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} - engines: {node: '>=12'} - ansi-styles@6.2.3: resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} @@ -4859,10 +4832,6 @@ packages: any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} - anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - aproba@2.0.0: resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} @@ -4970,6 +4939,9 @@ packages: axios@1.16.1: resolution: {integrity: sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==} + axios@1.18.1: + resolution: {integrity: sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==} + azure-devops-node-api@12.5.0: resolution: {integrity: sha512-R5eFskGvOm3U/GzeAuxRkUsAl0hrAwGgWn6zAd2KrZmrEhWZVqLew4OOupbQlXUuojUzpGtq62SmdhJ06N88og==} @@ -5044,10 +5016,6 @@ packages: bignumber.js@9.3.0: resolution: {integrity: sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==} - binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} - engines: {node: '>=8'} - binary@0.3.0: resolution: {integrity: sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==} @@ -5060,8 +5028,8 @@ packages: bluebird@3.7.2: resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} - body-parser@2.2.0: - resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} + body-parser@2.3.0: + resolution: {integrity: sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==} engines: {node: '>=18'} boolbase@1.0.0: @@ -5130,11 +5098,6 @@ packages: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} - c8@9.1.0: - resolution: {integrity: sha512-mBWcT5iqNir1zIkzSPyI3NCR9EZCVI3WUD+AVO17MVWTSFNyUueXE82qTeampNtTr+ilN/5Ua3j24LgbCKjDVg==} - engines: {node: '>=14.14.0'} - hasBin: true - cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -5230,10 +5193,6 @@ packages: resolution: {integrity: sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==} engines: {node: '>=18.17'} - chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} - chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} @@ -5417,6 +5376,10 @@ packages: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} + content-type@2.0.0: + resolution: {integrity: sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==} + engines: {node: '>=18'} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -6327,14 +6290,14 @@ packages: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} - execa@9.5.3: - resolution: {integrity: sha512-QFNnTvU3UjgWFy8Ef9iDHvIdcgZ344ebkwYx4/KLbR+CKQA4xBaHzv+iRpp86QfMHP8faFQLh8iOc57215y4Rg==} - engines: {node: ^18.19.0 || >=20.5.0} - execa@9.6.0: resolution: {integrity: sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==} engines: {node: ^18.19.0 || >=20.5.0} + execa@9.6.1: + resolution: {integrity: sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==} + engines: {node: ^18.19.0 || >=20.5.0} + exenv-es6@1.1.1: resolution: {integrity: sha512-vlVu3N8d6yEMpMsEm+7sUBAI81aqYYuEvfK0jNqmdb/OPXzzH7QWDDnVjMvDSY47JdHEqx/dfC/q8WkfoTmpGQ==} @@ -6350,14 +6313,14 @@ packages: resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - express-rate-limit@7.5.0: - resolution: {integrity: sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==} + express-rate-limit@8.5.2: + resolution: {integrity: sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A==} engines: {node: '>= 16'} peerDependencies: - express: ^4.11 || 5 || ^5.0.0-beta.1 + express: '>= 4.11' - express@5.1.0: - resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==} + express@5.2.1: + resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} engines: {node: '>= 18'} extend-shallow@2.0.1: @@ -6766,9 +6729,6 @@ packages: hachure-fill@0.5.2: resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==} - harmony-reflect@1.6.2: - resolution: {integrity: sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==} - has-bigints@1.1.0: resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} engines: {node: '>= 0.4'} @@ -6847,6 +6807,10 @@ packages: resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} engines: {node: '>=12.0.0'} + hono@4.12.27: + resolution: {integrity: sha512-1yrb/+w6HWQJrUCLkJ2IF5jNIPvvFkblV5RNOYl6bV+OA6p9GLcMpHFFGTosSvHvcAUibuUukRqhlYI4z32C7Q==} + engines: {node: '>=16.9.0'} + hosted-git-info@4.1.0: resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} engines: {node: '>=10'} @@ -6858,9 +6822,6 @@ packages: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} engines: {node: '>=18'} - html-escaper@2.0.2: - resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - html-parse-stringify@3.0.1: resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==} @@ -6877,6 +6838,10 @@ packages: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + http-proxy-agent@7.0.2: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} @@ -6932,9 +6897,9 @@ packages: resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==} engines: {node: '>=0.10.0'} - identity-obj-proxy@3.0.0: - resolution: {integrity: sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==} - engines: {node: '>=4'} + iconv-lite@0.7.2: + resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} + engines: {node: '>=0.10.0'} ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -6983,7 +6948,7 @@ packages: resolution: {integrity: sha512-yF92kj3pmBvk7oKbSq5vEALO//o7Z9Ck/OaLNlkzXNeYdwfpxMQkSowGTFUCS5MSu9bWfSZMewGpp7bFc66D7Q==} engines: {node: '>=18'} peerDependencies: - '@types/react': ^18.3.23 + '@types/react': 18.3.23 peerDependenciesMeta: '@types/react': optional: true @@ -6992,7 +6957,7 @@ packages: resolution: {integrity: sha512-QDt6FgJxgmSxAelcOvOHUvFxbIUjVpCH5bx+Slvc5m7IEcpGt3dYwbz/L+oRnqEGeRvwy1tineKK4ect3nW1vQ==} engines: {node: '>=20'} peerDependencies: - '@types/react': ^18.3.23 + '@types/react': 18.3.23 react: '>=19.0.0' react-devtools-core: ^6.1.2 peerDependenciesMeta: @@ -7025,6 +6990,10 @@ packages: resolution: {integrity: sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA==} engines: {node: '>=12.22.0'} + ip-address@10.2.0: + resolution: {integrity: sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==} + engines: {node: '>= 12'} + ip-address@9.0.5: resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} engines: {node: '>= 12'} @@ -7057,10 +7026,6 @@ packages: resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} engines: {node: '>= 0.4'} - is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - is-boolean-object@1.2.2: resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} engines: {node: '>= 0.4'} @@ -7281,10 +7246,6 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - isexe@3.1.1: - resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} - engines: {node: '>=16'} - isexe@3.1.5: resolution: {integrity: sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==} engines: {node: '>=18'} @@ -7293,18 +7254,6 @@ packages: resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} engines: {node: '>=0.10.0'} - istanbul-lib-coverage@3.2.2: - resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} - engines: {node: '>=8'} - - istanbul-lib-report@3.0.1: - resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} - engines: {node: '>=10'} - - istanbul-reports@3.1.7: - resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} - engines: {node: '>=8'} - iterator.prototype@1.1.5: resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} engines: {node: '>= 0.4'} @@ -7337,6 +7286,9 @@ packages: resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} hasBin: true + jose@6.2.3: + resolution: {integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==} + joycon@3.1.1: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} @@ -7392,16 +7344,15 @@ packages: json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - json-parse-even-better-errors@4.0.0: - resolution: {integrity: sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==} - engines: {node: ^18.17.0 || >=20.5.0} - json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-schema-typed@8.0.2: + resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==} + json-schema@0.4.0: resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} @@ -7485,9 +7436,6 @@ packages: '@types/node': '>=18' typescript: '>=5.0.4' - knuth-shuffle-seeded@1.0.6: - resolution: {integrity: sha512-9pFH0SplrfyKyojCLxZfMcvkhf5hH0d+UwR9nTVJ/DDQJGuzcXjTwB7TP7sDfehSudlGGaOLblmEWqv04ERVWg==} - layout-base@1.0.2: resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==} @@ -7891,10 +7839,6 @@ packages: magic-string@0.30.17: resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} - make-dir@4.0.0: - resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} - engines: {node: '>=10'} - mammoth@1.12.0: resolution: {integrity: sha512-cwnK1RIcRdDMi2HRx2EXGYlxqIEh0Oo3bLhorgnsVJi2UkbX1+jKxuBNR9PC5+JaX7EkmJxFPmo6mjLpqShI2w==} engines: {node: '>=12.0.0'} @@ -7996,10 +7940,6 @@ packages: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} engines: {node: '>= 0.8'} - memorystream@0.3.1: - resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} - engines: {node: '>= 0.10.0'} - merge-descriptors@2.0.0: resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} engines: {node: '>=18'} @@ -8149,10 +8089,6 @@ packages: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} - minimatch@10.0.1: - resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==} - engines: {node: 20 || >=22} - minimatch@10.1.1: resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==} engines: {node: 20 || >=22} @@ -8337,15 +8273,6 @@ packages: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - npm-normalize-package-bin@4.0.0: - resolution: {integrity: sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==} - engines: {node: ^18.17.0 || >=20.5.0} - - npm-run-all2@8.0.3: - resolution: {integrity: sha512-0mAycidMUMThrLt8AT3LGtOMgfLaMg6/4oUKHTKMU0jDSIsdKBsKp98H8zBFcJylQC4CtOB140UUFbOlFyE9gA==} - engines: {node: ^20.5.0 || >=22.0.0, npm: '>= 10'} - hasBin: true - npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} @@ -8644,11 +8571,6 @@ packages: resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} - pidtree@0.6.0: - resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} - engines: {node: '>=0.10'} - hasBin: true - pify@4.0.1: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} @@ -8849,6 +8771,10 @@ packages: resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} engines: {node: '>=0.6'} + qs@6.15.3: + resolution: {integrity: sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==} + engines: {node: '>=0.6'} + quansync@0.2.11: resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} @@ -8866,6 +8792,10 @@ packages: resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} engines: {node: '>= 0.8'} + raw-body@3.0.2: + resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} + engines: {node: '>= 0.10'} + rc@1.2.8: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true @@ -8919,7 +8849,7 @@ packages: react-markdown@9.1.0: resolution: {integrity: sha512-xaijuJB0kzGiUdG7nc2MOMDUDBWPyGAjZtUrow9XxUeua8IqeP+VlIfAZ3bphpcLTnSZXz6z9jcVC/TCwbfgdw==} peerDependencies: - '@types/react': ^18.3.23 + '@types/react': 18.3.23 react: '>=18' react-reconciler@0.33.0: @@ -8942,7 +8872,7 @@ packages: resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} engines: {node: '>=10'} peerDependencies: - '@types/react': ^18.3.23 + '@types/react': 18.3.23 react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 peerDependenciesMeta: '@types/react': @@ -8952,7 +8882,7 @@ packages: resolution: {integrity: sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==} engines: {node: '>=10'} peerDependencies: - '@types/react': ^18.3.23 + '@types/react': 18.3.23 react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': @@ -8962,7 +8892,7 @@ packages: resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} engines: {node: '>=10'} peerDependencies: - '@types/react': ^18.3.23 + '@types/react': 18.3.23 react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': @@ -9000,10 +8930,6 @@ packages: resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==} engines: {node: '>=0.10.0'} - read-package-json-fast@4.0.0: - resolution: {integrity: sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==} - engines: {node: ^18.17.0 || >=20.5.0} - read-yaml-file@1.1.0: resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} engines: {node: '>=6'} @@ -9029,10 +8955,6 @@ packages: readdir-glob@1.1.3: resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} - readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} - readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} @@ -9262,9 +9184,6 @@ packages: resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==} engines: {node: '>=4'} - seed-random@2.2.0: - resolution: {integrity: sha512-34EQV6AAHQGhoc0tn/96a9Fsi6v2xdqe/dMUwljGRaFOzR3EgRmECvD0O8vi8X+/uQ50LGHfkNu/Eue5TPKZkQ==} - semver-compare@1.0.0: resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==} @@ -9370,6 +9289,10 @@ packages: resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} engines: {node: '>= 0.4'} + side-channel-list@1.0.1: + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} + engines: {node: '>= 0.4'} + side-channel-map@1.0.1: resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} engines: {node: '>= 0.4'} @@ -9382,6 +9305,10 @@ packages: resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} engines: {node: '>= 0.4'} + side-channel@1.1.1: + resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} + engines: {node: '>= 0.4'} + siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} @@ -9526,6 +9453,10 @@ packages: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + std-env@3.9.0: resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} @@ -9733,10 +9664,6 @@ packages: resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} engines: {node: '>=10'} - supports-color@9.4.0: - resolution: {integrity: sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==} - engines: {node: '>=12'} - supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -9784,10 +9711,6 @@ packages: resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} engines: {node: '>=8'} - test-exclude@6.0.0: - resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} - engines: {node: '>=8'} - text-decoder@1.2.3: resolution: {integrity: sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==} @@ -9986,6 +9909,10 @@ packages: resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} engines: {node: '>= 0.6'} + type-is@2.1.0: + resolution: {integrity: sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==} + engines: {node: '>= 18'} + typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} @@ -10048,8 +9975,8 @@ packages: undici-types@7.10.0: resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==} - undici@6.26.0: - resolution: {integrity: sha512-4yqz8a3n5HmGTlsbADNtr/dJlhkh/55Rq798G6ibiULcXbDtaLpTl1pvdqcbFfeoj3iSi52lePFM7h9H21cw/A==} + undici@6.27.0: + resolution: {integrity: sha512-YmfV3YnEDzXRC5lZ2jWtWWHKGUm1zIt8AhesR1tens+HTNv+YZlN/dp6G727LOvMJ8xjP9Be7Y2Sdr96LDm+pg==} engines: {node: '>=18.17'} unicorn-magic@0.3.0: @@ -10144,7 +10071,7 @@ packages: resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} engines: {node: '>=10'} peerDependencies: - '@types/react': ^18.3.23 + '@types/react': 18.3.23 react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': @@ -10181,7 +10108,7 @@ packages: resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} engines: {node: '>=10'} peerDependencies: - '@types/react': ^18.3.23 + '@types/react': 18.3.23 react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': @@ -10216,10 +10143,6 @@ packages: deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). hasBin: true - v8-to-istanbul@9.3.0: - resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} - engines: {node: '>=10.12.0'} - vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} @@ -10411,7 +10334,7 @@ packages: vscrui@0.2.2: resolution: {integrity: sha512-buw2OipqUl7GCBq1mxcAjUwoUsslGzVhdaxDPmEx27xzc3QAJJZHtT30QbakgZVJ1Jb3E6kcsguUIFEGxrgkyQ==} peerDependencies: - '@types/react': ^18.3.23 + '@types/react': 18.3.23 react: ^17 || ^18 || ^19 w3c-xmlserializer@5.0.0: @@ -10504,11 +10427,6 @@ packages: engines: {node: ^16.13.0 || >=18.0.0} hasBin: true - which@5.0.0: - resolution: {integrity: sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==} - engines: {node: ^18.17.0 || >=20.5.0} - hasBin: true - why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} @@ -10711,12 +10629,6 @@ packages: peerDependencies: zod: 3.25.76 - zod-to-ts@1.2.0: - resolution: {integrity: sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA==} - peerDependencies: - typescript: ^4.9.4 || ^5.0.2 - zod: 3.25.76 - zod-validation-error@3.4.1: resolution: {integrity: sha512-1KP64yqDPQ3rupxNv7oXhf7KdhHHgaqbKuspVoiN93TT0xrBjql+Svjkdjq/Qh/7GSMmgQs3AfvBT0heE35thw==} engines: {node: '>=18.0.0'} @@ -10730,7 +10642,7 @@ packages: resolution: {integrity: sha512-ALBtUj0AfjJt3uNRQoL1tL2tMvj6Gp/6e39dnfT6uzpelGru8v1tPOGBzayOWbPJvujM8JojDk3E1LxeFisBNg==} engines: {node: '>=12.20.0'} peerDependencies: - '@types/react': ^18.3.23 + '@types/react': 18.3.23 immer: '>=9.0.6' react: '>=18.0.0' use-sync-external-store: '>=1.2.0' @@ -10879,7 +10791,7 @@ snapshots: dependencies: '@ai-sdk/provider': 3.0.8 '@standard-schema/spec': 1.1.0 - eventsource-parser: 3.0.6 + eventsource-parser: 3.1.0 zod: 3.25.76 '@ai-sdk/provider-utils@4.0.27(zod@3.25.76)': @@ -11716,8 +11628,6 @@ snapshots: '@basetenlabs/performance-client-win32-ia32-msvc': 0.0.10 '@basetenlabs/performance-client-win32-x64-msvc': 0.0.10 - '@bcoe/v8-coverage@0.2.3': {} - '@braintree/sanitize-url@7.1.1': {} '@changesets/apply-release-plan@7.0.13': @@ -12125,17 +12035,21 @@ snapshots: '@floating-ui/utils@0.2.9': {} - '@google/genai@1.29.1(@modelcontextprotocol/sdk@1.12.0)': + '@google/genai@1.29.1(@modelcontextprotocol/sdk@1.26.0(zod@3.25.76))': dependencies: google-auth-library: 10.5.0 ws: 8.18.3 optionalDependencies: - '@modelcontextprotocol/sdk': 1.12.0 + '@modelcontextprotocol/sdk': 1.26.0(zod@3.25.76) transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate + '@hono/node-server@1.19.14(hono@4.12.27)': + dependencies: + hono: 4.12.27 + '@hookform/resolvers@5.1.1(react-hook-form@7.57.0(react@18.3.1))': dependencies: '@standard-schema/utils': 0.3.0 @@ -12295,8 +12209,6 @@ snapshots: dependencies: minipass: 7.1.2 - '@istanbuljs/schema@0.1.3': {} - '@jest/expect-utils@29.7.0': dependencies: jest-get-type: 29.6.3 @@ -12489,19 +12401,25 @@ snapshots: '@mixmark-io/domino@2.2.0': {} - '@modelcontextprotocol/sdk@1.12.0': + '@modelcontextprotocol/sdk@1.26.0(zod@3.25.76)': dependencies: - ajv: 6.12.6 + '@hono/node-server': 1.19.14(hono@4.12.27) + ajv: 8.18.0 + ajv-formats: 3.0.1(ajv@8.18.0) content-type: 1.0.5 cors: 2.8.5 cross-spawn: 7.0.6 eventsource: 3.0.7 - express: 5.1.0 - express-rate-limit: 7.5.0(express@5.1.0) + eventsource-parser: 3.1.0 + express: 5.2.1 + express-rate-limit: 8.5.2(express@5.2.1) + hono: 4.12.27 + jose: 6.2.3 + json-schema-typed: 8.0.2 pkce-challenge: 5.0.0 raw-body: 3.0.0 zod: 3.25.76 - zod-to-json-schema: 3.24.5(zod@3.25.76) + zod-to-json-schema: 3.25.1(zod@3.25.76) transitivePeerDependencies: - supports-color @@ -12658,11 +12576,6 @@ snapshots: '@open-draft/until@2.1.0': {} - '@openrouter/ai-sdk-provider@2.1.1(ai@6.0.77(zod@3.25.76))(zod@3.25.76)': - dependencies: - ai: 6.0.77(zod@3.25.76) - zod: 3.25.76 - '@opentelemetry/api@1.9.0': {} '@oxc-project/types@0.133.0': {} @@ -12745,7 +12658,7 @@ snapshots: '@qdrant/openapi-typescript-fetch': 1.2.6 '@sevinf/maybe': 0.5.0 typescript: 5.8.3 - undici: 6.26.0 + undici: 6.27.0 '@qdrant/openapi-typescript-fetch@1.2.6': {} @@ -14672,18 +14585,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@vscode/test-cli@0.0.11': - dependencies: - '@types/mocha': 10.0.10 - c8: 9.1.0 - chokidar: 3.6.0 - enhanced-resolve: 5.18.1 - glob: 11.1.0 - minimatch: 9.0.5 - mocha: 11.2.2 - supports-color: 9.4.0 - yargs: 17.7.2 - '@vscode/test-electron@2.5.2': dependencies: http-proxy-agent: 7.0.2 @@ -14829,6 +14730,10 @@ snapshots: '@opentelemetry/api': 1.9.0 zod: 3.25.76 + ajv-formats@3.0.1(ajv@8.18.0): + optionalDependencies: + ajv: 8.18.0 + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -14869,8 +14774,6 @@ snapshots: ansi-styles@5.2.0: {} - ansi-styles@6.2.1: {} - ansi-styles@6.2.3: {} ansi-to-html@0.7.2: @@ -14879,11 +14782,6 @@ snapshots: any-promise@1.3.0: {} - anymatch@3.1.3: - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.2 - aproba@2.0.0: {} archiver-utils@2.1.0: @@ -15049,6 +14947,16 @@ snapshots: - debug - supports-color + axios@1.18.1: + dependencies: + follow-redirects: 1.16.0 + form-data: 4.0.4 + https-proxy-agent: 5.0.1 + proxy-from-env: 2.1.0 + transitivePeerDependencies: + - debug + - supports-color + azure-devops-node-api@12.5.0: dependencies: tunnel: 0.0.6 @@ -15113,8 +15021,6 @@ snapshots: bignumber.js@9.3.0: {} - binary-extensions@2.3.0: {} - binary@0.3.0: dependencies: buffers: 0.1.1 @@ -15133,17 +15039,17 @@ snapshots: bluebird@3.7.2: {} - body-parser@2.2.0: + body-parser@2.3.0: dependencies: bytes: 3.1.2 - content-type: 1.0.5 + content-type: 2.0.0 debug: 4.4.3 - http-errors: 2.0.0 - iconv-lite: 0.6.3 + http-errors: 2.0.1 + iconv-lite: 0.7.2 on-finished: 2.4.1 - qs: 6.14.0 - raw-body: 3.0.0 - type-is: 2.0.1 + qs: 6.15.3 + raw-body: 3.0.2 + type-is: 2.1.0 transitivePeerDependencies: - supports-color @@ -15203,20 +15109,6 @@ snapshots: bytes@3.1.2: {} - c8@9.1.0: - dependencies: - '@bcoe/v8-coverage': 0.2.3 - '@istanbuljs/schema': 0.1.3 - find-up: 5.0.0 - foreground-child: 3.3.1 - istanbul-lib-coverage: 3.2.2 - istanbul-lib-report: 3.0.1 - istanbul-reports: 3.1.7 - test-exclude: 6.0.0 - v8-to-istanbul: 9.3.0 - yargs: 17.7.2 - yargs-parser: 21.1.1 - cac@6.7.14: {} call-bind-apply-helpers@1.0.2: @@ -15316,21 +15208,9 @@ snapshots: parse5: 7.3.0 parse5-htmlparser2-tree-adapter: 7.1.0 parse5-parser-stream: 7.1.2 - undici: 6.26.0 + undici: 6.27.0 whatwg-mimetype: 4.0.0 - chokidar@3.6.0: - dependencies: - anymatch: 3.1.3 - braces: 3.0.3 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.3 - chokidar@4.0.3: dependencies: readdirp: 4.1.2 @@ -15499,6 +15379,8 @@ snapshots: content-type@1.0.5: {} + content-type@2.0.0: {} + convert-source-map@2.0.0: {} convert-to-spaces@2.0.1: {} @@ -16442,7 +16324,7 @@ snapshots: eventsource@3.0.7: dependencies: - eventsource-parser: 3.0.6 + eventsource-parser: 3.1.0 exceljs@4.4.0: dependencies: @@ -16480,7 +16362,7 @@ snapshots: signal-exit: 4.1.0 strip-final-newline: 3.0.0 - execa@9.5.3: + execa@9.6.0: dependencies: '@sindresorhus/merge-streams': 4.0.0 cross-spawn: 7.0.6 @@ -16495,7 +16377,7 @@ snapshots: strip-final-newline: 4.0.0 yoctocolors: 2.1.1 - execa@9.6.0: + execa@9.6.1: dependencies: '@sindresorhus/merge-streams': 4.0.0 cross-spawn: 7.0.6 @@ -16508,7 +16390,7 @@ snapshots: pretty-ms: 9.2.0 signal-exit: 4.1.0 strip-final-newline: 4.0.0 - yoctocolors: 2.1.1 + yoctocolors: 2.1.2 exenv-es6@1.1.1: {} @@ -16525,19 +16407,21 @@ snapshots: jest-message-util: 29.7.0 jest-util: 29.7.0 - express-rate-limit@7.5.0(express@5.1.0): + express-rate-limit@8.5.2(express@5.2.1): dependencies: - express: 5.1.0 + express: 5.2.1 + ip-address: 10.2.0 - express@5.1.0: + express@5.2.1: dependencies: accepts: 2.0.0 - body-parser: 2.2.0 + body-parser: 2.3.0 content-disposition: 1.0.0 content-type: 1.0.5 cookie: 0.7.2 cookie-signature: 1.2.2 debug: 4.4.3 + depd: 2.0.0 encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 @@ -17019,8 +16903,6 @@ snapshots: hachure-fill@0.5.2: {} - harmony-reflect@1.6.2: {} - has-bigints@1.1.0: {} has-flag@3.0.0: {} @@ -17155,6 +17037,8 @@ snapshots: highlight.js@11.11.1: {} + hono@4.12.27: {} + hosted-git-info@4.1.0: dependencies: lru-cache: 6.0.0 @@ -17165,8 +17049,6 @@ snapshots: dependencies: whatwg-encoding: 3.1.1 - html-escaper@2.0.2: {} - html-parse-stringify@3.0.1: dependencies: void-elements: 3.1.0 @@ -17190,6 +17072,14 @@ snapshots: statuses: 2.0.1 toidentifier: 1.0.1 + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.3 @@ -17241,9 +17131,9 @@ snapshots: dependencies: safer-buffer: 2.1.2 - identity-obj-proxy@3.0.0: + iconv-lite@0.7.2: dependencies: - harmony-reflect: 1.6.2 + safer-buffer: 2.1.2 ieee754@1.2.1: {} @@ -17341,6 +17231,8 @@ snapshots: transitivePeerDependencies: - supports-color + ip-address@10.2.0: {} + ip-address@9.0.5: dependencies: jsbn: 1.1.0 @@ -17380,10 +17272,6 @@ snapshots: dependencies: has-bigints: 1.1.0 - is-binary-path@2.1.0: - dependencies: - binary-extensions: 2.3.0 - is-boolean-object@1.2.2: dependencies: call-bound: 1.0.4 @@ -17560,25 +17448,10 @@ snapshots: isexe@2.0.0: {} - isexe@3.1.1: {} - isexe@3.1.5: {} isobject@3.0.1: {} - istanbul-lib-coverage@3.2.2: {} - - istanbul-lib-report@3.0.1: - dependencies: - istanbul-lib-coverage: 3.2.2 - make-dir: 4.0.0 - supports-color: 7.2.0 - - istanbul-reports@3.1.7: - dependencies: - html-escaper: 2.0.2 - istanbul-lib-report: 3.0.1 - iterator.prototype@1.1.5: dependencies: define-data-property: 1.1.4 @@ -17631,6 +17504,8 @@ snapshots: jiti@2.4.2: {} + jose@6.2.3: {} + joycon@3.1.1: {} js-base64@3.7.8: @@ -17694,12 +17569,12 @@ snapshots: json-buffer@3.0.1: {} - json-parse-even-better-errors@4.0.0: {} - json-schema-traverse@0.4.1: {} json-schema-traverse@1.0.0: {} + json-schema-typed@8.0.2: {} + json-schema@0.4.0: {} json-stable-stringify-without-jsonify@1.0.1: {} @@ -17811,10 +17686,6 @@ snapshots: zod: 3.25.76 zod-validation-error: 3.4.1(zod@3.25.76) - knuth-shuffle-seeded@1.0.6: - dependencies: - seed-random: 2.2.0 - layout-base@1.0.2: {} layout-base@2.0.1: {} @@ -18145,10 +18016,6 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 - make-dir@4.0.0: - dependencies: - semver: 7.7.3 - mammoth@1.12.0: dependencies: '@xmldom/xmldom': 0.8.10 @@ -18383,8 +18250,6 @@ snapshots: media-typer@1.1.0: {} - memorystream@0.3.1: {} - merge-descriptors@2.0.0: {} merge-stream@2.0.0: {} @@ -18653,10 +18518,6 @@ snapshots: min-indent@1.0.1: {} - minimatch@10.0.1: - dependencies: - brace-expansion: 2.0.2 - minimatch@10.1.1: dependencies: '@isaacs/brace-expansion': 5.0.0 @@ -18849,19 +18710,6 @@ snapshots: normalize-path@3.0.0: {} - npm-normalize-package-bin@4.0.0: {} - - npm-run-all2@8.0.3: - dependencies: - ansi-styles: 6.2.1 - cross-spawn: 7.0.6 - memorystream: 0.3.1 - minimatch: 10.0.1 - pidtree: 0.6.0 - read-package-json-fast: 4.0.0 - shell-quote: 1.8.3 - which: 5.0.0 - npm-run-path@4.0.1: dependencies: path-key: 3.1.1 @@ -19197,8 +19045,6 @@ snapshots: picomatch@4.0.4: {} - pidtree@0.6.0: {} - pify@4.0.1: {} pirates@4.0.7: {} @@ -19424,6 +19270,11 @@ snapshots: dependencies: side-channel: 1.1.0 + qs@6.15.3: + dependencies: + es-define-property: 1.0.1 + side-channel: 1.1.1 + quansync@0.2.11: {} queue-microtask@1.2.3: {} @@ -19441,6 +19292,13 @@ snapshots: iconv-lite: 0.6.3 unpipe: 1.0.0 + raw-body@3.0.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.7.2 + unpipe: 1.0.0 + rc@1.2.8: dependencies: deep-extend: 0.6.0 @@ -19589,11 +19447,6 @@ snapshots: react@19.2.3: {} - read-package-json-fast@4.0.0: - dependencies: - json-parse-even-better-errors: 4.0.0 - npm-normalize-package-bin: 4.0.0 - read-yaml-file@1.1.0: dependencies: graceful-fs: 4.2.11 @@ -19640,10 +19493,6 @@ snapshots: dependencies: minimatch: 5.1.6 - readdirp@3.6.0: - dependencies: - picomatch: 2.3.2 - readdirp@4.1.2: {} reconnecting-eventsource@1.6.4: {} @@ -19974,8 +19823,6 @@ snapshots: extend-shallow: 2.0.1 kind-of: 6.0.3 - seed-random@2.2.0: {} - semver-compare@1.0.0: {} semver@5.7.2: {} @@ -20121,6 +19968,11 @@ snapshots: es-errors: 1.3.0 object-inspect: 1.13.4 + side-channel-list@1.0.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-map@1.0.1: dependencies: call-bound: 1.0.4 @@ -20144,6 +19996,14 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 + side-channel@1.1.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.1 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + siginfo@2.0.0: {} signal-exit@3.0.7: {} @@ -20298,6 +20158,8 @@ snapshots: statuses@2.0.1: {} + statuses@2.0.2: {} + std-env@3.9.0: {} stdin-discarder@0.2.2: {} @@ -20522,8 +20384,6 @@ snapshots: dependencies: has-flag: 4.0.0 - supports-color@9.4.0: {} - supports-preserve-symlinks-flag@1.0.0: {} symbol-tree@3.2.4: {} @@ -20577,12 +20437,6 @@ snapshots: term-size@2.2.1: {} - test-exclude@6.0.0: - dependencies: - '@istanbuljs/schema': 0.1.3 - glob: 11.1.0 - minimatch: 3.1.2 - text-decoder@1.2.3: dependencies: b4a: 1.6.7 @@ -20763,6 +20617,12 @@ snapshots: media-typer: 1.1.0 mime-types: 3.0.1 + type-is@2.1.0: + dependencies: + content-type: 2.0.0 + media-typer: 1.1.0 + mime-types: 3.0.1 + typed-array-buffer@1.0.3: dependencies: call-bound: 1.0.4 @@ -20842,7 +20702,7 @@ snapshots: undici-types@7.10.0: {} - undici@6.26.0: {} + undici@6.27.0: {} unicorn-magic@0.3.0: {} @@ -21014,12 +20874,6 @@ snapshots: uuid@9.0.1: {} - v8-to-istanbul@9.3.0: - dependencies: - '@jridgewell/trace-mapping': 0.3.25 - '@types/istanbul-lib-coverage': 2.0.6 - convert-source-map: 2.0.0 - vary@1.1.2: {} vaul@1.1.2(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): @@ -21211,9 +21065,9 @@ snapshots: esbuild: 0.25.9 fdir: 6.4.6(picomatch@4.0.4) picomatch: 4.0.4 - postcss: 8.5.4 + postcss: 8.5.15 rollup: 4.40.2 - tinyglobby: 0.2.14 + tinyglobby: 0.2.17 optionalDependencies: '@types/node': 20.17.50 fsevents: 2.3.3 @@ -21227,9 +21081,9 @@ snapshots: esbuild: 0.25.9 fdir: 6.4.6(picomatch@4.0.4) picomatch: 4.0.4 - postcss: 8.5.4 + postcss: 8.5.15 rollup: 4.40.2 - tinyglobby: 0.2.14 + tinyglobby: 0.2.17 optionalDependencies: '@types/node': 20.17.57 fsevents: 2.3.3 @@ -21243,9 +21097,9 @@ snapshots: esbuild: 0.25.9 fdir: 6.4.6(picomatch@4.0.4) picomatch: 4.0.4 - postcss: 8.5.4 + postcss: 8.5.15 rollup: 4.40.2 - tinyglobby: 0.2.14 + tinyglobby: 0.2.17 optionalDependencies: '@types/node': 20.19.41 fsevents: 2.3.3 @@ -21259,9 +21113,9 @@ snapshots: esbuild: 0.25.9 fdir: 6.4.6(picomatch@4.0.4) picomatch: 4.0.4 - postcss: 8.5.4 + postcss: 8.5.15 rollup: 4.40.2 - tinyglobby: 0.2.14 + tinyglobby: 0.2.17 optionalDependencies: '@types/node': 24.2.1 fsevents: 2.3.3 @@ -21569,10 +21423,6 @@ snapshots: dependencies: isexe: 3.1.5 - which@5.0.0: - dependencies: - isexe: 3.1.1 - why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 @@ -21742,11 +21592,6 @@ snapshots: dependencies: zod: 3.25.76 - zod-to-ts@1.2.0(typescript@5.8.3)(zod@3.25.76): - dependencies: - typescript: 5.8.3 - zod: 3.25.76 - zod-validation-error@3.4.1(zod@3.25.76): dependencies: zod: 3.25.76 diff --git a/src/__tests__/delegation-concurrent.spec.ts b/src/__tests__/delegation-concurrent.spec.ts new file mode 100644 index 0000000000..40d9b49ee5 --- /dev/null +++ b/src/__tests__/delegation-concurrent.spec.ts @@ -0,0 +1,147 @@ +// npx vitest run __tests__/delegation-concurrent.spec.ts + +import { describe, it, expect, vi, beforeEach } from "vitest" +import type { HistoryItem } from "@roo-code/types" + +vi.mock("fs/promises", () => ({ + mkdir: vi.fn().mockResolvedValue(undefined), + readFile: vi.fn().mockRejectedValue(Object.assign(new Error("ENOENT"), { code: "ENOENT" })), + readdir: vi.fn().mockResolvedValue([]), + unlink: vi.fn().mockResolvedValue(undefined), +})) + +vi.mock("fs", () => ({ + default: { + watch: vi.fn().mockReturnValue({ on: vi.fn(), close: vi.fn() }), + existsSync: vi.fn().mockReturnValue(false), + }, + watch: vi.fn().mockReturnValue({ on: vi.fn(), close: vi.fn() }), + existsSync: vi.fn().mockReturnValue(false), +})) + +vi.mock("../utils/safeWriteJson", () => ({ + safeWriteJson: vi.fn().mockResolvedValue(undefined), +})) + +vi.mock("../utils/storage", () => ({ + getStorageBasePath: vi.fn().mockResolvedValue("/tmp/test-storage"), +})) + +import { TaskHistoryStore } from "../core/task-persistence/TaskHistoryStore" + +const makeItem = (id: string, overrides: Partial = {}): HistoryItem => + ({ + id, + ts: Date.now(), + task: "test task", + tokensIn: 0, + tokensOut: 0, + totalCost: 0, + status: "active", + mode: "code", + workspace: "/tmp", + ...overrides, + }) as HistoryItem + +describe("TaskHistoryStore.atomicReadAndUpdate", () => { + let store: TaskHistoryStore + + beforeEach(() => { + vi.clearAllMocks() + store = new TaskHistoryStore("/tmp/test-storage") + }) + + it("serializes concurrent operations — second caller reads the state written by the first", async () => { + // Seed the cache with an item that has no childIds yet. + const item = makeItem("parent-task", { childIds: [] }) + ;(store as any).cache.set(item.id, item) + + // Two concurrent delegations each append their child ID. + // Because they are serialized by the lock, the second caller must + // read the cache state that the first caller wrote — not the original. + const delegation1 = store.atomicReadAndUpdate("parent-task", (current) => ({ + ...current, + childIds: [...(current.childIds ?? []), "child-A"], + })) + + const delegation2 = store.atomicReadAndUpdate("parent-task", (current) => ({ + ...current, + childIds: [...(current.childIds ?? []), "child-B"], + })) + + await Promise.all([delegation1, delegation2]) + + // Both child IDs must be present: delegation2 saw delegation1's write. + const final = (store as any).cache.get("parent-task") as HistoryItem + expect(final.childIds).toContain("child-A") + expect(final.childIds).toContain("child-B") + }) + + it("two concurrent delegations produce consistent HistoryItem state (full field set)", async () => { + const item = makeItem("parent-task", { status: "active", childIds: [] }) + ;(store as any).cache.set(item.id, item) + + // Each delegation sets awaitingChildId and appends to childIds. + const delegation1 = store.atomicReadAndUpdate("parent-task", (historyItem) => { + const childIds = Array.from(new Set([...(historyItem.childIds ?? []), "child-A"])) + return { + ...historyItem, + status: "delegated", + delegatedToId: "child-A", + awaitingChildId: "child-A", + childIds, + } + }) + + const delegation2 = store.atomicReadAndUpdate("parent-task", (historyItem) => { + const childIds = Array.from(new Set([...(historyItem.childIds ?? []), "child-B"])) + return { + ...historyItem, + status: "delegated", + delegatedToId: "child-B", + awaitingChildId: "child-B", + childIds, + } + }) + + await Promise.all([delegation1, delegation2]) + + const final = (store as any).cache.get("parent-task") as HistoryItem + // Both child IDs present — neither write clobbered the other's childIds. + expect(final.childIds).toContain("child-A") + expect(final.childIds).toContain("child-B") + // The last writer wins on scalar fields; whichever child ran second is authoritative. + expect(final.status).toBe("delegated") + expect(["child-A", "child-B"]).toContain(final.awaitingChildId) + expect(final.delegatedToId).toBe(final.awaitingChildId) + expect(final.childIds).toContain(final.awaitingChildId) + }) + + it("completes without deadlock — updater is pure and does not re-acquire the lock", async () => { + const item = makeItem("task-1", { childIds: [] }) + ;(store as any).cache.set(item.id, item) + + // With the typed (taskId, updater) API, the updater is synchronous and + // cannot call upsert/withLock — no re-entrancy, no deadlock. + const result = await Promise.race([ + store + .atomicReadAndUpdate("task-1", (current) => ({ + ...current, + childIds: [...(current.childIds ?? []), "child-1"], + })) + .then(() => "completed"), + new Promise((resolve) => setTimeout(() => resolve("deadlocked"), 100)), + ]) + + expect(result).toBe("completed") + + const final = (store as any).cache.get("task-1") as HistoryItem + expect(final.childIds).toContain("child-1") + }) + + it("throws if the task ID is not in the cache", async () => { + await expect( + store.atomicReadAndUpdate("nonexistent-task", (current) => ({ ...current, status: "delegated" })), + ).rejects.toThrow("nonexistent-task") + }) +}) diff --git a/src/__tests__/provider-delegation.spec.ts b/src/__tests__/provider-delegation.spec.ts index 4b04fb5bbb..eef495ea54 100644 --- a/src/__tests__/provider-delegation.spec.ts +++ b/src/__tests__/provider-delegation.spec.ts @@ -1,124 +1,193 @@ // npx vitest run __tests__/provider-delegation.spec.ts import { describe, it, expect, vi } from "vitest" +import type { HistoryItem } from "@roo-code/types" import { RooCodeEventName } from "@roo-code/types" import { ClineProvider } from "../core/webview/ClineProvider" +const parentHistoryItem: HistoryItem = { + id: "parent-1", + task: "Parent", + tokensIn: 0, + tokensOut: 0, + totalCost: 0, + childIds: [], +} as unknown as HistoryItem + +/** Minimal taskHistoryStore stub whose atomicReadAndUpdate calls the updater with the parent item. */ +function makeStoreStub( + overrides: Partial<{ atomicReadAndUpdate: ReturnType; get: ReturnType }> = {}, +) { + return { + atomicReadAndUpdate: vi.fn(async (_taskId: string, updater: (h: HistoryItem) => HistoryItem) => { + updater(parentHistoryItem) + return [] + }), + get: vi.fn().mockReturnValue(undefined), + ...overrides, + } +} + +/** + * Parent task double with the methods delegateParentAndOpenChild reads from + * `parent`. Without flushPendingToolResultsToHistory the method hits its + * non-fatal flush-error branch and never reaches the happy delegation path. + */ +const makeParentTask = () => + ({ + taskId: "parent-1", + emit: vi.fn(), + flushPendingToolResultsToHistory: vi.fn().mockResolvedValue(true), + retrySaveApiConversationHistory: vi.fn(), + }) as any + describe("ClineProvider.delegateParentAndOpenChild()", () => { - it("persists parent delegation metadata and emits TaskDelegated", async () => { + it("persists parent delegation metadata via atomicReadAndUpdate and emits TaskDelegated", async () => { const providerEmit = vi.fn() - const parentTask = { taskId: "parent-1", emit: vi.fn() } as any + const parentTask = makeParentTask() const childStart = vi.fn() - const updateTaskHistory = vi.fn() const removeClineFromStack = vi.fn().mockResolvedValue(undefined) const createTask = vi.fn().mockResolvedValue({ taskId: "child-1", start: childStart }) const handleModeSwitch = vi.fn().mockResolvedValue(undefined) - const getTaskWithId = vi.fn().mockImplementation(async (id: string) => { - if (id === "parent-1") { - return { - historyItem: { - id: "parent-1", - task: "Parent", - tokensIn: 0, - tokensOut: 0, - totalCost: 0, - childIds: [], - }, - } - } - // child-1 - return { - historyItem: { - id: "child-1", - task: "Do something", - tokensIn: 0, - tokensOut: 0, - totalCost: 0, - }, - } - }) + const taskHistoryStore = makeStoreStub() const provider = { emit: providerEmit, getCurrentTask: vi.fn(() => parentTask), removeClineFromStack, createTask, - getTaskWithId, - updateTaskHistory, handleModeSwitch, log: vi.fn(), + isViewLaunched: false, + recentTasksCache: undefined, + taskHistoryStore, } as unknown as ClineProvider - const params = { + const child = await (ClineProvider.prototype as any).delegateParentAndOpenChild.call(provider, { parentTaskId: "parent-1", message: "Do something", initialTodos: [], mode: "code", - } - - const child = await (ClineProvider.prototype as any).delegateParentAndOpenChild.call(provider, params) + }) expect(child.taskId).toBe("child-1") // Invariant: parent closed before child creation expect(removeClineFromStack).toHaveBeenCalledTimes(1) - // Child task is created with startTask: false and initialStatus: "active" + + // Child task created with startTask: false and initialStatus: "active" expect(createTask).toHaveBeenCalledWith("Do something", undefined, parentTask, { initialTodos: [], initialStatus: "active", startTask: false, }) - // Metadata persistence - parent gets "delegated" status (child status is set at creation via initialStatus) - expect(updateTaskHistory).toHaveBeenCalledTimes(1) - - // Parent set to "delegated" - const parentSaved = updateTaskHistory.mock.calls[0][0] - expect(parentSaved).toEqual( - expect.objectContaining({ - id: "parent-1", - status: "delegated", - delegatedToId: "child-1", - awaitingChildId: "child-1", - childIds: expect.arrayContaining(["child-1"]), - }), - ) + // Delegation metadata written via atomicReadAndUpdate with correct taskId + expect(taskHistoryStore.atomicReadAndUpdate).toHaveBeenCalledTimes(1) + const [calledTaskId, updater] = taskHistoryStore.atomicReadAndUpdate.mock.calls[0] + expect(calledTaskId).toBe("parent-1") + + // The updater must produce the correct delegation fields + const result = updater(parentHistoryItem) + expect(result).toMatchObject({ + id: "parent-1", + status: "delegated", + delegatedToId: "child-1", + awaitingChildId: "child-1", + childIds: expect.arrayContaining(["child-1"]), + }) - // child.start() must be called AFTER parent metadata is persisted + // child.start() called AFTER parent metadata is persisted expect(childStart).toHaveBeenCalledTimes(1) - // Event emission (provider-level) + // Provider-level event expect(providerEmit).toHaveBeenCalledWith(RooCodeEventName.TaskDelegated, "parent-1", "child-1") // Mode switch expect(handleModeSwitch).toHaveBeenCalledWith("code") }) - it("calls child.start() only after parent metadata is persisted (no race condition)", async () => { - const callOrder: string[] = [] + it("posts taskHistoryItemUpdated to the webview when isViewLaunched is true", async () => { + const updatedParent = { ...parentHistoryItem, status: "delegated" } as HistoryItem + const postMessageToWebview = vi.fn().mockResolvedValue(undefined) + const parentTask = makeParentTask() + const taskHistoryStore = makeStoreStub({ + get: vi.fn().mockReturnValue(updatedParent), + }) - const parentTask = { taskId: "parent-1", emit: vi.fn() } as any - const childStart = vi.fn(() => callOrder.push("child.start")) + const provider = { + emit: vi.fn(), + getCurrentTask: vi.fn(() => parentTask), + removeClineFromStack: vi.fn().mockResolvedValue(undefined), + createTask: vi.fn().mockResolvedValue({ taskId: "child-1", start: vi.fn() }), + handleModeSwitch: vi.fn().mockResolvedValue(undefined), + postMessageToWebview, + log: vi.fn(), + isViewLaunched: true, + recentTasksCache: undefined, + taskHistoryStore, + } as unknown as ClineProvider - const updateTaskHistory = vi.fn(async () => { - callOrder.push("updateTaskHistory") + await (ClineProvider.prototype as any).delegateParentAndOpenChild.call(provider, { + parentTaskId: "parent-1", + message: "Do something", + initialTodos: [], + mode: "code", }) + + expect(postMessageToWebview).toHaveBeenCalledWith({ + type: "taskHistoryItemUpdated", + taskHistoryItem: updatedParent, + }) + }) + + it("skips postMessageToWebview when isViewLaunched is true but store returns undefined", async () => { + const postMessageToWebview = vi.fn().mockResolvedValue(undefined) + const parentTask = makeParentTask() + const taskHistoryStore = makeStoreStub({ + get: vi.fn().mockReturnValue(undefined), + }) + + const provider = { + emit: vi.fn(), + getCurrentTask: vi.fn(() => parentTask), + removeClineFromStack: vi.fn().mockResolvedValue(undefined), + createTask: vi.fn().mockResolvedValue({ taskId: "child-1", start: vi.fn() }), + handleModeSwitch: vi.fn().mockResolvedValue(undefined), + postMessageToWebview, + log: vi.fn(), + isViewLaunched: true, + recentTasksCache: undefined, + taskHistoryStore, + } as unknown as ClineProvider + + await (ClineProvider.prototype as any).delegateParentAndOpenChild.call(provider, { + parentTaskId: "parent-1", + message: "Do something", + initialTodos: [], + mode: "code", + }) + + expect(postMessageToWebview).not.toHaveBeenCalled() + }) + + it("calls child.start() only after atomicReadAndUpdate completes (no race condition)", async () => { + const callOrder: string[] = [] + + const parentTask = makeParentTask() + const childStart = vi.fn(() => callOrder.push("child.start")) const removeClineFromStack = vi.fn().mockResolvedValue(undefined) const createTask = vi.fn(async () => { callOrder.push("createTask") return { taskId: "child-1", start: childStart } }) const handleModeSwitch = vi.fn().mockResolvedValue(undefined) - const getTaskWithId = vi.fn().mockResolvedValue({ - historyItem: { - id: "parent-1", - task: "Parent", - tokensIn: 0, - tokensOut: 0, - totalCost: 0, - childIds: [], - }, + const taskHistoryStore = makeStoreStub({ + atomicReadAndUpdate: vi.fn(async (_taskId: string, _updater: (h: HistoryItem) => HistoryItem) => { + callOrder.push("atomicReadAndUpdate") + return [] + }), }) const provider = { @@ -126,10 +195,11 @@ describe("ClineProvider.delegateParentAndOpenChild()", () => { getCurrentTask: vi.fn(() => parentTask), removeClineFromStack, createTask, - getTaskWithId, - updateTaskHistory, handleModeSwitch, log: vi.fn(), + isViewLaunched: false, + recentTasksCache: undefined, + taskHistoryStore, } as unknown as ClineProvider await (ClineProvider.prototype as any).delegateParentAndOpenChild.call(provider, { @@ -139,7 +209,7 @@ describe("ClineProvider.delegateParentAndOpenChild()", () => { mode: "code", }) - // Verify ordering: createTask → updateTaskHistory → child.start - expect(callOrder).toEqual(["createTask", "updateTaskHistory", "child.start"]) + // createTask → atomicReadAndUpdate → child.start: lock must release before start + expect(callOrder).toEqual(["createTask", "atomicReadAndUpdate", "child.start"]) }) }) diff --git a/src/api/providers/__tests__/anthropic-vertex.spec.ts b/src/api/providers/__tests__/anthropic-vertex.spec.ts index 2b64decdbc..690b4df748 100644 --- a/src/api/providers/__tests__/anthropic-vertex.spec.ts +++ b/src/api/providers/__tests__/anthropic-vertex.spec.ts @@ -1,5 +1,9 @@ // npx vitest run src/api/providers/__tests__/anthropic-vertex.spec.ts +vitest.mock("../utils/timeout-config", () => ({ + getApiRequestTimeout: vitest.fn().mockReturnValue(600_000), +})) + import { Anthropic } from "@anthropic-ai/sdk" import { AnthropicVertex } from "@anthropic-ai/vertex-sdk" @@ -64,6 +68,7 @@ describe("VertexHandler", () => { expect(AnthropicVertex).toHaveBeenCalledWith({ projectId: "test-project", region: "us-central1", + timeout: 600_000, }) }) }) diff --git a/src/api/providers/__tests__/base-openai-compatible-provider.spec.ts b/src/api/providers/__tests__/base-openai-compatible-provider.spec.ts index c1753bc458..f7cc5da4df 100644 --- a/src/api/providers/__tests__/base-openai-compatible-provider.spec.ts +++ b/src/api/providers/__tests__/base-openai-compatible-provider.spec.ts @@ -95,6 +95,75 @@ describe("BaseOpenAiCompatibleProvider", () => { ]) }) + it("should handle reasoning tags () from stream", async () => { + mockCreate.mockImplementationOnce(() => { + return { + [Symbol.asyncIterator]: () => ({ + next: vi + .fn() + .mockResolvedValueOnce({ + done: false, + value: { choices: [{ delta: { content: "Deep thought" } }] }, + }) + .mockResolvedValueOnce({ + done: false, + value: { choices: [{ delta: { content: " here" } }] }, + }) + .mockResolvedValueOnce({ + done: false, + value: { choices: [{ delta: { content: "Result: 42" } }] }, + }) + .mockResolvedValueOnce({ done: true }), + }), + } + }) + const stream = handler.createMessage("system prompt", []) + const chunks = [] + for await (const chunk of stream) { + chunks.push(chunk) + } + expect(chunks).toEqual([ + { type: "reasoning", text: "Deep thought" }, + { type: "reasoning", text: " here" }, + { type: "text", text: "Result: 42" }, + ]) + }) + + it("should not close tag with tag", async () => { + mockCreate.mockImplementationOnce(() => { + return { + [Symbol.asyncIterator]: () => ({ + next: vi + .fn() + .mockResolvedValueOnce({ + done: false, + value: { choices: [{ delta: { content: "Thinking" } }] }, + }) + .mockResolvedValueOnce({ + done: false, + value: { choices: [{ delta: { content: " but closing with wrong tag" } }] }, + }) + .mockResolvedValueOnce({ + done: false, + value: { choices: [{ delta: { content: " still thinking" } }] }, + }) + .mockResolvedValueOnce({ done: true }), + }), + } + }) + const stream = handler.createMessage("system prompt", []) + const chunks = [] + for await (const chunk of stream) { + chunks.push(chunk) + } + // The tag should be treated as text since it doesn't match the active tag + expect(chunks).toEqual([ + { type: "reasoning", text: "Thinking" }, + { type: "reasoning", text: " but closing with wrong tag" }, + { type: "reasoning", text: " still thinking" }, + ]) + }) + it("should handle complete tag in a single chunk", async () => { mockCreate.mockImplementationOnce(() => { return { @@ -151,13 +220,8 @@ describe("BaseOpenAiCompatibleProvider", () => { chunks.push(chunk) } - // TagMatcher should handle incomplete tags and flush remaining content - expect(chunks.length).toBeGreaterThan(0) - expect( - chunks.some( - (c) => (c.type === "text" || c.type === "reasoning") && c.text.includes("Incomplete thought"), - ), - ).toBe(true) + // TagMatcher should flush incomplete reasoning content on stream end + expect(chunks).toContainEqual({ type: "reasoning", text: "Incomplete thought" }) }) it("should handle text without any tags", async () => { diff --git a/src/api/providers/__tests__/fireworks.spec.ts b/src/api/providers/__tests__/fireworks.spec.ts index 871c989eac..04af92244d 100644 --- a/src/api/providers/__tests__/fireworks.spec.ts +++ b/src/api/providers/__tests__/fireworks.spec.ts @@ -181,6 +181,30 @@ describe("FireworksHandler", () => { ) }) + it("should return Kimi K2.7 code model with correct configuration", () => { + const testModelId: FireworksModelId = "accounts/fireworks/models/kimi-k2p7-code" + const handlerWithModel = new FireworksHandler({ + apiModelId: testModelId, + fireworksApiKey: "test-fireworks-api-key", + }) + const model = handlerWithModel.getModel() + expect(model.id).toBe(testModelId) + expect(model.info).toEqual( + expect.objectContaining({ + maxTokens: 16384, + contextWindow: 262144, + supportsImages: true, + supportsPromptCache: true, + supportsTemperature: true, + preserveReasoning: true, + defaultTemperature: 1.0, + inputPrice: 0.95, + outputPrice: 4.0, + cacheReadsPrice: 0.19, + }), + ) + }) + it("should return MiniMax M2 model with correct configuration", () => { const testModelId: FireworksModelId = "accounts/fireworks/models/minimax-m2" const handlerWithModel = new FireworksHandler({ diff --git a/src/api/providers/__tests__/lite-llm.spec.ts b/src/api/providers/__tests__/lite-llm.spec.ts index df0e8b152d..b1a30ac394 100644 --- a/src/api/providers/__tests__/lite-llm.spec.ts +++ b/src/api/providers/__tests__/lite-llm.spec.ts @@ -1,3 +1,7 @@ +vi.mock("../utils/timeout-config", () => ({ + getApiRequestTimeout: vi.fn().mockReturnValue(600_000), +})) + import OpenAI from "openai" import { Anthropic } from "@anthropic-ai/sdk" diff --git a/src/api/providers/__tests__/openrouter.spec.ts b/src/api/providers/__tests__/openrouter.spec.ts index e03abea635..a22f769a7a 100644 --- a/src/api/providers/__tests__/openrouter.spec.ts +++ b/src/api/providers/__tests__/openrouter.spec.ts @@ -1,5 +1,9 @@ // pnpm --filter roo-cline test api/providers/__tests__/openrouter.spec.ts +vitest.mock("../utils/timeout-config", () => ({ + getApiRequestTimeout: vitest.fn().mockReturnValue(600_000), +})) + vitest.mock("vscode", () => ({})) import { Anthropic } from "@anthropic-ai/sdk" @@ -104,6 +108,7 @@ describe("OpenRouterHandler", () => { "X-Title": "Roo Code", "User-Agent": `RooCode/${Package.version}`, }, + timeout: 600_000, }) }) diff --git a/src/api/providers/__tests__/requesty.spec.ts b/src/api/providers/__tests__/requesty.spec.ts index 88804553bd..2c53c89080 100644 --- a/src/api/providers/__tests__/requesty.spec.ts +++ b/src/api/providers/__tests__/requesty.spec.ts @@ -1,5 +1,9 @@ // npx vitest run api/providers/__tests__/requesty.spec.ts +vitest.mock("../utils/timeout-config", () => ({ + getApiRequestTimeout: vitest.fn().mockReturnValue(600_000), +})) + import { Anthropic } from "@anthropic-ai/sdk" import OpenAI from "openai" @@ -76,6 +80,7 @@ describe("RequestyHandler", () => { "X-Title": "Roo Code", "User-Agent": `RooCode/${Package.version}`, }, + timeout: 600_000, }) }) @@ -91,6 +96,7 @@ describe("RequestyHandler", () => { "X-Title": "Roo Code", "User-Agent": `RooCode/${Package.version}`, }, + timeout: 600_000, }) }) diff --git a/src/api/providers/__tests__/vercel-ai-gateway.spec.ts b/src/api/providers/__tests__/vercel-ai-gateway.spec.ts index 2cb6bb8e2e..fb4f1c9872 100644 --- a/src/api/providers/__tests__/vercel-ai-gateway.spec.ts +++ b/src/api/providers/__tests__/vercel-ai-gateway.spec.ts @@ -1,6 +1,10 @@ // npx vitest run src/api/providers/__tests__/vercel-ai-gateway.spec.ts // Mock vscode first to avoid import errors +vitest.mock("../utils/timeout-config", () => ({ + getApiRequestTimeout: vitest.fn().mockReturnValue(600_000), +})) + vitest.mock("vscode", () => ({})) import { Anthropic } from "@anthropic-ai/sdk" @@ -112,6 +116,7 @@ describe("VercelAiGatewayHandler", () => { "X-Title": "Roo Code", "User-Agent": expect.stringContaining("RooCode/"), }), + timeout: 600_000, }) }) diff --git a/src/api/providers/__tests__/vertex-credentials.spec.ts b/src/api/providers/__tests__/vertex-credentials.spec.ts index 98871c825a..e6c960ba57 100644 --- a/src/api/providers/__tests__/vertex-credentials.spec.ts +++ b/src/api/providers/__tests__/vertex-credentials.spec.ts @@ -2,6 +2,10 @@ // Mock vscode first to avoid import errors when the provider stack pulls // transitive vscode-dependent modules during construction. +vitest.mock("../utils/timeout-config", () => ({ + getApiRequestTimeout: vitest.fn().mockReturnValue(600_000), +})) + vitest.mock("vscode", () => ({})) vitest.mock("@roo-code/telemetry", () => ({ diff --git a/src/api/providers/__tests__/vertex.spec.ts b/src/api/providers/__tests__/vertex.spec.ts index ddf22bc34f..62835bba27 100644 --- a/src/api/providers/__tests__/vertex.spec.ts +++ b/src/api/providers/__tests__/vertex.spec.ts @@ -1,6 +1,10 @@ // npx vitest run src/api/providers/__tests__/vertex.spec.ts // Mock vscode first to avoid import errors +vitest.mock("../utils/timeout-config", () => ({ + getApiRequestTimeout: vitest.fn().mockReturnValue(600_000), +})) + vitest.mock("vscode", () => ({})) const mockCaptureException = vitest.fn() diff --git a/src/api/providers/__tests__/vscode-lm.spec.ts b/src/api/providers/__tests__/vscode-lm.spec.ts index 305305d228..120dc920e0 100644 --- a/src/api/providers/__tests__/vscode-lm.spec.ts +++ b/src/api/providers/__tests__/vscode-lm.spec.ts @@ -1,3 +1,7 @@ +vi.mock("../utils/timeout-config", () => ({ + getApiRequestTimeout: vi.fn().mockReturnValue(600_000), +})) + import type { Mock } from "vitest" // Mocks must come first, before imports diff --git a/src/api/providers/anthropic-vertex.ts b/src/api/providers/anthropic-vertex.ts index 306b26191f..3a1db8e290 100644 --- a/src/api/providers/anthropic-vertex.ts +++ b/src/api/providers/anthropic-vertex.ts @@ -51,6 +51,7 @@ export class AnthropicVertexHandler extends BaseProvider implements SingleComple scopes: ["https://www.googleapis.com/auth/cloud-platform"], credentials: parsedVertexCredentials, }), + timeout: this.timeoutMs, }) } else if (this.options.vertexKeyFile) { this.client = new AnthropicVertex({ @@ -60,9 +61,10 @@ export class AnthropicVertexHandler extends BaseProvider implements SingleComple scopes: ["https://www.googleapis.com/auth/cloud-platform"], keyFile: this.options.vertexKeyFile, }), + timeout: this.timeoutMs, }) } else { - this.client = new AnthropicVertex({ projectId, region }) + this.client = new AnthropicVertex({ projectId, region, timeout: this.timeoutMs }) } } diff --git a/src/api/providers/anthropic.ts b/src/api/providers/anthropic.ts index 03d14c168b..01d624d2d3 100644 --- a/src/api/providers/anthropic.ts +++ b/src/api/providers/anthropic.ts @@ -44,6 +44,7 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa this.client = new Anthropic({ baseURL: this.options.anthropicBaseUrl || undefined, [apiKeyFieldName]: this.options.apiKey, + timeout: this.timeoutMs, }) } diff --git a/src/api/providers/base-openai-compatible-provider.ts b/src/api/providers/base-openai-compatible-provider.ts index 9ae605f507..b6094f9cc4 100644 --- a/src/api/providers/base-openai-compatible-provider.ts +++ b/src/api/providers/base-openai-compatible-provider.ts @@ -13,7 +13,6 @@ import { DEFAULT_HEADERS } from "./constants" import { BaseProvider } from "./base-provider" import { handleOpenAIError } from "./utils/openai-error-handler" import { calculateApiCostOpenAI } from "../../shared/cost" -import { getApiRequestTimeout } from "./utils/timeout-config" import { extractReasoningFromDelta } from "./utils/extract-reasoning" type BaseOpenAiCompatibleProviderOptions = ApiHandlerOptions & { @@ -64,7 +63,7 @@ export abstract class BaseOpenAiCompatibleProvider baseURL, apiKey: this.options.apiKey, defaultHeaders: DEFAULT_HEADERS, - timeout: getApiRequestTimeout(), + timeout: this.timeoutMs, }) } @@ -119,7 +118,7 @@ export abstract class BaseOpenAiCompatibleProvider const stream = await this.createStream(systemPrompt, messages, metadata) const matcher = new TagMatcher( - "think", + ["think", "thought"], (chunk) => ({ type: chunk.matched ? "reasoning" : "text", diff --git a/src/api/providers/base-provider.ts b/src/api/providers/base-provider.ts index a6adeeadbd..89366fb619 100644 --- a/src/api/providers/base-provider.ts +++ b/src/api/providers/base-provider.ts @@ -6,11 +6,14 @@ import type { ApiHandler, ApiHandlerCreateMessageMetadata } from "../index" import { ApiStream } from "../transform/stream" import { countTokens } from "../../utils/countTokens" import { isMcpTool } from "../../utils/mcp-name" +import { getApiRequestTimeout } from "./utils/timeout-config" /** * Base class for API providers that implements common functionality. */ export abstract class BaseProvider implements ApiHandler { + protected readonly timeoutMs: number = getApiRequestTimeout() + abstract createMessage( systemPrompt: string, messages: Anthropic.Messages.MessageParam[], diff --git a/src/api/providers/lm-studio.ts b/src/api/providers/lm-studio.ts index fd9c25ba30..238ae0aeb3 100644 --- a/src/api/providers/lm-studio.ts +++ b/src/api/providers/lm-studio.ts @@ -105,7 +105,7 @@ export class LmStudioHandler extends BaseProvider implements SingleCompletionHan } const matcher = new TagMatcher( - "think", + ["think", "thought"], (chunk) => ({ type: chunk.matched ? "reasoning" : "text", diff --git a/src/api/providers/minimax.ts b/src/api/providers/minimax.ts index bfcf4e3be4..03e01d3207 100644 --- a/src/api/providers/minimax.ts +++ b/src/api/providers/minimax.ts @@ -73,6 +73,7 @@ export class MiniMaxHandler extends BaseProvider implements SingleCompletionHand this.client = new Anthropic({ baseURL, apiKey: options.minimaxApiKey, + timeout: this.timeoutMs, }) } diff --git a/src/api/providers/native-ollama.ts b/src/api/providers/native-ollama.ts index aa35d27a96..c392f6b5c4 100644 --- a/src/api/providers/native-ollama.ts +++ b/src/api/providers/native-ollama.ts @@ -215,7 +215,7 @@ export class NativeOllamaHandler extends BaseProvider implements SingleCompletio ] const matcher = new TagMatcher( - "think", + ["think", "thought"], (chunk) => ({ type: chunk.matched ? "reasoning" : "text", diff --git a/src/api/providers/openai-codex.ts b/src/api/providers/openai-codex.ts index 9dfb37bc72..ed4a44519b 100644 --- a/src/api/providers/openai-codex.ts +++ b/src/api/providers/openai-codex.ts @@ -371,6 +371,7 @@ export class OpenAiCodexHandler extends BaseProvider implements SingleCompletion apiKey: accessToken, baseURL: CODEX_API_BASE_URL, defaultHeaders: codexHeaders, + timeout: this.timeoutMs, }) const stream = (await (client as any).responses.create(requestBody, { diff --git a/src/api/providers/openai-native.ts b/src/api/providers/openai-native.ts index 6ce9382763..42fbc1c6b9 100644 --- a/src/api/providers/openai-native.ts +++ b/src/api/providers/openai-native.ts @@ -104,6 +104,7 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio session_id: this.sessionId, "User-Agent": userAgent, }, + timeout: this.timeoutMs, }) } diff --git a/src/api/providers/openai.ts b/src/api/providers/openai.ts index 8761cde599..91e9a4edab 100644 --- a/src/api/providers/openai.ts +++ b/src/api/providers/openai.ts @@ -22,7 +22,6 @@ import { getModelParams } from "../transform/model-params" import { DEFAULT_HEADERS } from "./constants" import { BaseProvider } from "./base-provider" import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" -import { getApiRequestTimeout } from "./utils/timeout-config" import { handleOpenAIError } from "./utils/openai-error-handler" import { extractReasoningFromDelta } from "./utils/extract-reasoning" @@ -86,7 +85,7 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl ...(this.options.openAiHeaders || {}), } - const timeout = getApiRequestTimeout() + const timeout = this.timeoutMs if (isAzureAiInference) { // Azure AI Inference Service (e.g., for DeepSeek) uses a different path structure @@ -259,7 +258,7 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl } const matcher = new TagMatcher( - "think", + ["think", "thought"], (chunk) => ({ type: chunk.matched ? "reasoning" : "text", diff --git a/src/api/providers/openrouter.ts b/src/api/providers/openrouter.ts index 7fcc24b15f..b67f0e136f 100644 --- a/src/api/providers/openrouter.ts +++ b/src/api/providers/openrouter.ts @@ -153,7 +153,7 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH const baseURL = this.options.openRouterBaseUrl || "https://openrouter.ai/api/v1" const apiKey = this.options.openRouterApiKey ?? "not-provided" - this.client = new OpenAI({ baseURL, apiKey, defaultHeaders: DEFAULT_HEADERS }) + this.client = new OpenAI({ baseURL, apiKey, defaultHeaders: DEFAULT_HEADERS, timeout: this.timeoutMs }) // Load models asynchronously to populate cache before getModel() is called this.loadDynamicModels().catch((error) => { diff --git a/src/api/providers/qwen-code.ts b/src/api/providers/qwen-code.ts index 0b7d7598af..cdf0f88e4b 100644 --- a/src/api/providers/qwen-code.ts +++ b/src/api/providers/qwen-code.ts @@ -76,6 +76,7 @@ export class QwenCodeHandler extends BaseProvider implements SingleCompletionHan "X-DashScope-UserAgent": `QwenCode/1.0.0 (${os.platform()}; ${os.arch()})`, "X-DashScope-AuthType": "qwen-oauth", }, + timeout: this.timeoutMs, }) } return this.client diff --git a/src/api/providers/requesty.ts b/src/api/providers/requesty.ts index a62e5444da..aacc5a8178 100644 --- a/src/api/providers/requesty.ts +++ b/src/api/providers/requesty.ts @@ -69,6 +69,7 @@ export class RequestyHandler extends BaseProvider implements SingleCompletionHan baseURL: this.baseURL, apiKey: apiKey, defaultHeaders: DEFAULT_HEADERS, + timeout: this.timeoutMs, }) } diff --git a/src/api/providers/router-provider.ts b/src/api/providers/router-provider.ts index 09b102d5b2..20419bb45a 100644 --- a/src/api/providers/router-provider.ts +++ b/src/api/providers/router-provider.ts @@ -52,6 +52,7 @@ export abstract class RouterProvider extends BaseProvider { ...DEFAULT_HEADERS, ...(options.openAiHeaders || {}), }, + timeout: this.timeoutMs, }) } diff --git a/src/api/providers/unbound.ts b/src/api/providers/unbound.ts index 921985deaf..1e7aedd1bd 100644 --- a/src/api/providers/unbound.ts +++ b/src/api/providers/unbound.ts @@ -63,6 +63,7 @@ export class UnboundHandler extends BaseProvider implements SingleCompletionHand ...DEFAULT_HEADERS, "X-Unbound-Metadata": JSON.stringify({ labels: [{ key: "app", value: "roo-code" }] }), }, + timeout: this.timeoutMs, }) } diff --git a/src/api/providers/utils/__tests__/timeout-config.spec.ts b/src/api/providers/utils/__tests__/timeout-config.spec.ts index f76c26480c..bc26faf360 100644 --- a/src/api/providers/utils/__tests__/timeout-config.spec.ts +++ b/src/api/providers/utils/__tests__/timeout-config.spec.ts @@ -41,23 +41,47 @@ describe("getApiRequestTimeout", () => { expect(timeout).toBe(1200000) // 1200 seconds in milliseconds }) - it("should return undefined for zero timeout (disables timeout)", () => { + it("should fall back to default for zero (below the minimum of 1s)", () => { mockGetConfig.mockReturnValue(0) const timeout = getApiRequestTimeout() - // Zero means "no timeout" - return undefined so SDK uses its default - // (OpenAI SDK interprets 0 as "abort immediately", so we avoid that) - expect(timeout).toBeUndefined() + // 0 is out of the valid 1-3600s range, so we fall back to the default. + expect(timeout).toBe(600000) }) - it("should return undefined for negative values (disables timeout)", () => { + it("should fall back to default for negative values (below the minimum)", () => { mockGetConfig.mockReturnValue(-100) const timeout = getApiRequestTimeout() - // Negative values also mean "no timeout" - return undefined - expect(timeout).toBeUndefined() + expect(timeout).toBe(600000) + }) + + it("should fall back to default for values above the 3600s maximum", () => { + mockGetConfig.mockReturnValue(5000) + + const timeout = getApiRequestTimeout() + + expect(timeout).toBe(600000) + }) + + it("should accept the minimum boundary (1s)", () => { + mockGetConfig.mockReturnValue(1) + + expect(getApiRequestTimeout()).toBe(1000) + }) + + it("should accept the maximum boundary (3600s)", () => { + mockGetConfig.mockReturnValue(3600) + + expect(getApiRequestTimeout()).toBe(3600000) + }) + + it("should round fractional milliseconds to an integer", () => { + mockGetConfig.mockReturnValue(1.2345) // 1234.5ms + + expect(getApiRequestTimeout()).toBe(1235) }) it("should handle null by using default", () => { diff --git a/src/api/providers/utils/timeout-config.ts b/src/api/providers/utils/timeout-config.ts index 39adec6202..e8d3ea3186 100644 --- a/src/api/providers/utils/timeout-config.ts +++ b/src/api/providers/utils/timeout-config.ts @@ -1,26 +1,26 @@ import * as vscode from "vscode" import { Package } from "../../../shared/package" +const DEFAULT_TIMEOUT_SECONDS = 600 +const MIN_TIMEOUT_SECONDS = 1 +const MAX_TIMEOUT_SECONDS = 3600 + +function isValidTimeout(value: unknown): value is number { + return typeof value === "number" && !isNaN(value) && value >= MIN_TIMEOUT_SECONDS && value <= MAX_TIMEOUT_SECONDS +} + /** * Gets the API request timeout from VSCode configuration with validation. * - * @returns The timeout in milliseconds. Returns undefined to disable timeout - * (letting the SDK use its default), or a positive number for explicit timeout. + * @returns The timeout in milliseconds. Out-of-range, NaN, or non-number values + * fall back to the default. Rounded to an integer so SDK positive-integer + * validation (e.g. the Anthropic SDK) never sees a float. */ -export function getApiRequestTimeout(): number | undefined { - // Get timeout with validation to ensure it's a valid non-negative number - const configTimeout = vscode.workspace.getConfiguration(Package.name).get("apiRequestTimeout", 600) - - // Validate that it's actually a number and not NaN - if (typeof configTimeout !== "number" || isNaN(configTimeout)) { - return 600 * 1000 // Default to 600 seconds - } - - // 0 or negative means "no timeout" - return undefined to let SDK use its default - // (OpenAI SDK interprets 0 as "abort immediately", so we return undefined instead) - if (configTimeout <= 0) { - return undefined - } +export function getApiRequestTimeout(): number { + const configTimeout = vscode.workspace + .getConfiguration(Package.name) + .get("apiRequestTimeout", DEFAULT_TIMEOUT_SECONDS) - return configTimeout * 1000 // Convert to milliseconds + const seconds = isValidTimeout(configTimeout) ? configTimeout : DEFAULT_TIMEOUT_SECONDS + return Math.round(seconds * 1000) } diff --git a/src/api/providers/xai.ts b/src/api/providers/xai.ts index 0cd9cb0273..e5c0ba0a81 100644 --- a/src/api/providers/xai.ts +++ b/src/api/providers/xai.ts @@ -34,6 +34,7 @@ export class XAIHandler extends BaseProvider implements SingleCompletionHandler baseURL: "https://api.x.ai/v1", apiKey: apiKey, defaultHeaders: DEFAULT_HEADERS, + timeout: this.timeoutMs, }) } diff --git a/src/core/assistant-message/NativeToolCallParser.ts b/src/core/assistant-message/NativeToolCallParser.ts index 6ccbb64ac9..86d095f9a3 100644 --- a/src/core/assistant-message/NativeToolCallParser.ts +++ b/src/core/assistant-message/NativeToolCallParser.ts @@ -829,6 +829,12 @@ export class NativeToolCallParser { break case "ask_followup_question": + // Require a question and a present follow_up. When follow_up is + // present-but-not-an-array (e.g. an object/string/number produced by + // incremental JSON parsing), we still construct nativeArgs and forward + // the raw value so the tool can emit a precise "must be an array" error + // instead of the generic parser failure, which would surface as a + // misleading "Missing value for required parameter 'follow_up'" error. if (args.question !== undefined && args.follow_up !== undefined) { nativeArgs = { question: args.question, diff --git a/src/core/prompts/tools/native-tools/apply_diff.ts b/src/core/prompts/tools/native-tools/apply_diff.ts index 3938e4886a..cdbd99df33 100644 --- a/src/core/prompts/tools/native-tools/apply_diff.ts +++ b/src/core/prompts/tools/native-tools/apply_diff.ts @@ -2,14 +2,19 @@ import type OpenAI from "openai" const APPLY_DIFF_DESCRIPTION = `Apply precise, targeted modifications to an existing file using one or more search/replace blocks. This tool is for surgical edits only; the 'SEARCH' block must exactly match the existing content, including whitespace and indentation. To make multiple targeted changes, provide multiple SEARCH/REPLACE blocks in the 'diff' parameter. Use the 'read_file' tool first if you are not confident in the exact content to search for.` -const DIFF_PARAMETER_DESCRIPTION = `A string containing one or more search/replace blocks defining the changes. The ':start_line:' is required and indicates the starting line number of the original content. You must not add a start line for the replacement content. Each block must follow this format: +const DIFF_PARAMETER_DESCRIPTION = `A string containing one or more search/replace blocks defining the changes. The ':start_line:' is strongly recommended and indicates the starting line number of the original content. You must not add a start line for the replacement content. Each block must follow this format: <<<<<<< SEARCH :start_line:[line_number] ------- [exact content to find] ======= [new content to replace with] ->>>>>>> REPLACE` +>>>>>>> REPLACE + +CRITICAL: +- The ':start_line:[line_number]' header is strongly recommended for accurate matching. When provided, it must follow the exact syntax ':start_line:[integer]' (for example: ':start_line:220'). Do not write headers with shorthand forms like ':220' or variations like ':start_line=220'. +- Copy the exact lines from the source file for a 100% string match including all whitespace, indentation, and newlines. +- Ensure the separator '-------' is on its own line immediately following ':start_line:[line_number]' with a newline.` export const apply_diff = { type: "function", diff --git a/src/core/task-persistence/TaskHistoryStore.ts b/src/core/task-persistence/TaskHistoryStore.ts index 4157d8b9fb..a65d0e866c 100644 --- a/src/core/task-persistence/TaskHistoryStore.ts +++ b/src/core/task-persistence/TaskHistoryStore.ts @@ -158,30 +158,36 @@ export class TaskHistoryStore { * updates the in-memory Map, and schedules a debounced index write. */ async upsert(item: HistoryItem): Promise { - return this.withLock(async () => { - const existing = this.cache.get(item.id) + return this.withLock(() => this._upsertUnlocked(item)) + } - // Merge: preserve existing metadata unless explicitly overwritten - const merged = existing ? { ...existing, ...item } : item + /** + * Upsert body executed without acquiring the lock. + * Must only be called from within a `withLock` callback. + */ + private async _upsertUnlocked(item: HistoryItem): Promise { + const existing = this.cache.get(item.id) - // Write per-task file (source of truth) - await this.writeTaskFile(merged) + // Merge: preserve existing metadata unless explicitly overwritten + const merged = existing ? { ...existing, ...item } : item - // Update in-memory cache - this.cache.set(merged.id, merged) + // Write per-task file (source of truth) + await this.writeTaskFile(merged) - // Schedule debounced index write - this.scheduleIndexWrite() + // Update in-memory cache + this.cache.set(merged.id, merged) - const all = this.getAll() + // Schedule debounced index write + this.scheduleIndexWrite() - // Call onWrite callback inside the lock for serialized write-through - if (this.onWrite) { - await this.onWrite(all) - } + const all = this.getAll() - return all - }) + // Call onWrite callback inside the lock for serialized write-through + if (this.onWrite) { + await this.onWrite(all) + } + + return all } /** @@ -529,6 +535,36 @@ export class TaskHistoryStore { }, TaskHistoryStore.RECONCILE_INTERVAL_MS) } + // ────────────────────────────── Atomic read-modify-write ────────────────────────────── + + /** + * Read a HistoryItem from the in-memory cache and write back an updated version, + * all within a single lock acquisition so no concurrent writer can interleave + * between the read and the write. + * + * The `updater` receives the current cached item and must return the new item + * synchronously. It must not perform I/O or acquire any other lock. + * + * @throws If the task ID is not present in the cache. + */ + public atomicReadAndUpdate(taskId: string, updater: (current: HistoryItem) => HistoryItem): Promise { + return this.withLock(async () => { + const current = this.cache.get(taskId) + if (!current) { + throw new Error(`[TaskHistoryStore] atomicReadAndUpdate: task ${taskId} not found in cache`) + } + // Deep-copy so a mutating updater cannot alter cached state before persistence. + const snapshot = structuredClone(current) + const updated = updater(snapshot) + if (updated.id !== taskId) { + throw new Error( + `[TaskHistoryStore] atomicReadAndUpdate: updater changed task id from ${taskId} to ${updated.id}`, + ) + } + return this._upsertUnlocked(updated) + }) + } + // ────────────────────────────── Private: Write lock ────────────────────────────── /** diff --git a/src/core/tools/AskFollowupQuestionTool.ts b/src/core/tools/AskFollowupQuestionTool.ts index c45acc824b..5298709eec 100644 --- a/src/core/tools/AskFollowupQuestionTool.ts +++ b/src/core/tools/AskFollowupQuestionTool.ts @@ -11,6 +11,9 @@ interface Suggestion { interface AskFollowupQuestionParams { question: string + // follow_up is typed as an array, but at runtime the value may arrive as a + // non-array (object/string/number) due to incremental JSON parsing, so the + // runtime validation in execute() guards against that explicitly. follow_up: Suggestion[] } @@ -28,17 +31,37 @@ export class AskFollowupQuestionTool extends BaseTool<"ask_followup_question"> { pushToolResult(await task.sayAndCreateMissingParamError("ask_followup_question", paramName)) } + const recordValidationError = async (message: string): Promise => { + task.consecutiveMistakeCount++ + task.recordToolError("ask_followup_question") + task.didToolFailInCurrentTurn = true + await task.say("error", message) + pushToolResult(formatResponse.toolError(message)) + } + try { if (!question) { await recordMissingParamError("question") return } - if (!follow_up || !Array.isArray(follow_up)) { + // Truly missing follow_up (null/undefined) -> report as a missing parameter. + if (follow_up === undefined || follow_up === null) { await recordMissingParamError("follow_up") return } + // Present-but-wrong-type follow_up (object/string/number) -> report a clear + // type/shape error rather than the misleading "Missing value" message, so the + // model can correct it instead of looping with the same payload. + if (!Array.isArray(follow_up)) { + await recordValidationError( + "The 'follow_up' parameter must be an array of suggestion objects, each shaped like { text: string, mode?: string }. " + + "Retry with 'follow_up' as a JSON array.", + ) + return + } + // Transform follow_up suggestions to the format expected by task.ask const follow_up_json = { question, diff --git a/src/core/tools/UseMcpToolTool.ts b/src/core/tools/UseMcpToolTool.ts index 98a481c524..3005be1698 100644 --- a/src/core/tools/UseMcpToolTool.ts +++ b/src/core/tools/UseMcpToolTool.ts @@ -1,4 +1,4 @@ -import type { ClineAskUseMcpServer, McpExecutionStatus } from "@roo-code/types" +import type { ClineAskUseMcpServer, McpExecutionStatus, McpResourceLink } from "@roo-code/types" import { Task } from "../task/Task" import { formatResponse } from "../prompts/responses" @@ -308,6 +308,10 @@ export class UseMcpToolTool extends BaseTool<"use_mcp_tool"> { } return "" } + if (item.type === "resource_link") { + const link = item as McpResourceLink + return `[${link.name}](${link.uri})${link.description ? ` — ${link.description}` : ""}` + } return "" }) .filter(Boolean) diff --git a/src/core/tools/__tests__/askFollowupQuestionTool.spec.ts b/src/core/tools/__tests__/askFollowupQuestionTool.spec.ts index 08a8394ac7..309eeea4b8 100644 --- a/src/core/tools/__tests__/askFollowupQuestionTool.spec.ts +++ b/src/core/tools/__tests__/askFollowupQuestionTool.spec.ts @@ -83,7 +83,7 @@ describe("AskFollowupQuestionTool", () => { expect(mockTask.sayAndCreateMissingParamError).toHaveBeenCalledWith("ask_followup_question", "follow_up") }) - it("should handle follow_up that is not an array", async () => { + it("should report a type error when follow_up is a string", async () => { const params = { question: "What?", follow_up: "not-an-array" as any } await tool.execute(params, mockTask, mockCallbacks) @@ -91,7 +91,35 @@ describe("AskFollowupQuestionTool", () => { expect(mockTask.consecutiveMistakeCount).toBe(1) expect(mockTask.recordToolError).toHaveBeenCalledWith("ask_followup_question") expect(mockTask.didToolFailInCurrentTurn).toBe(true) - expect(mockTask.sayAndCreateMissingParamError).toHaveBeenCalledWith("ask_followup_question", "follow_up") + // A present-but-non-array value is a type error. + expect(mockTask.sayAndCreateMissingParamError).not.toHaveBeenCalled() + const pushed = (mockCallbacks.pushToolResult as any).mock.calls[0][0] + expect(pushed).toContain("must be an array") + expect(pushed).not.toContain("Missing value") + }) + + it("should report a type error when follow_up is an object", async () => { + // Reproduces the issue: follow_up arrives as a keyed object instead of an array. + const params = { + question: "How should I proceed?", + follow_up: { + "0": { mode: null, text: "Keep the guard" }, + "1": { mode: null, text: "Remove the guard" }, + } as any, + } + + await tool.execute(params, mockTask, mockCallbacks) + + expect(mockTask.consecutiveMistakeCount).toBe(1) + expect(mockTask.recordToolError).toHaveBeenCalledWith("ask_followup_question") + expect(mockTask.didToolFailInCurrentTurn).toBe(true) + expect(mockTask.sayAndCreateMissingParamError).not.toHaveBeenCalled() + expect(mockTask.say).toHaveBeenCalledWith("error", expect.stringContaining("must be an array")) + const pushed = (mockCallbacks.pushToolResult as any).mock.calls[0][0] + expect(pushed).toContain("must be an array") + expect(pushed).not.toContain("Missing value") + // The tool must not proceed to ask the user with an invalid payload. + expect(mockTask.ask).not.toHaveBeenCalled() }) // ===== Happy path tests ===== diff --git a/src/core/tools/__tests__/useMcpToolTool.spec.ts b/src/core/tools/__tests__/useMcpToolTool.spec.ts index 93f115e865..820f5ce806 100644 --- a/src/core/tools/__tests__/useMcpToolTool.spec.ts +++ b/src/core/tools/__tests__/useMcpToolTool.spec.ts @@ -806,6 +806,62 @@ describe("useMcpToolTool", () => { expect(mockPushToolResult).toHaveBeenCalledWith(expect.stringContaining("with 1 image(s)")) }) + it("should render resource_link content as a markdown link (MCP spec 2025-06-18)", async () => { + const block: ToolUse = { + type: "tool_use", + name: "use_mcp_tool", + params: { + server_name: "figma-server", + tool_name: "get_node_info", + arguments: '{"nodeId": "123"}', + }, + nativeArgs: { + server_name: "figma-server", + tool_name: "get_node_info", + arguments: { nodeId: "123" }, + }, + partial: false, + } + + mockAskApproval.mockResolvedValue(true) + + const mockToolResult = { + content: [ + { + type: "resource_link", + uri: "https://example.com/spec.pdf", + name: "Design Spec", + description: "Full spec", + }, + ], + isError: false, + } + + mockProviderRef.deref.mockReturnValue({ + getMcpHub: () => ({ + callTool: vi.fn().mockResolvedValue(mockToolResult), + getAllServers: vi + .fn() + .mockReturnValue([ + { name: "figma-server", tools: [{ name: "get_node_info", description: "Get node info" }] }, + ]), + }), + postMessageToWebview: vi.fn(), + }) + + await useMcpToolTool.handle(mockTask as Task, block as any, { + askApproval: mockAskApproval, + handleError: mockHandleError, + pushToolResult: mockPushToolResult, + }) + + expect(mockTask.say).toHaveBeenCalledWith( + "mcp_server_response", + "[Design Spec](https://example.com/spec.pdf) — Full spec", + [], + ) + }) + it("should handle image with data URL already formatted", async () => { const block: ToolUse = { type: "tool_use", diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index af4b0c8b68..d9b6179e7a 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -3473,17 +3473,28 @@ export class ClineProvider }) // 5) Persist parent delegation metadata BEFORE the child starts writing. + // atomicReadAndUpdate reads from the in-memory cache and writes back within a + // single lock acquisition — no concurrent writer can slip between the read and + // write, and the pure updater cannot re-enter the lock (no deadlock). + // Broadcast and cache invalidation happen outside the lock after it releases. try { - const { historyItem } = await this.getTaskWithId(parentTaskId) - const childIds = Array.from(new Set([...(historyItem.childIds ?? []), child.taskId])) - const updatedHistory: typeof historyItem = { - ...historyItem, - status: "delegated", - delegatedToId: child.taskId, - awaitingChildId: child.taskId, - childIds, - } - await this.updateTaskHistory(updatedHistory) + await this.taskHistoryStore.atomicReadAndUpdate(parentTaskId, (historyItem) => { + const childIds = Array.from(new Set([...(historyItem.childIds ?? []), child.taskId])) + return { + ...historyItem, + status: "delegated", + delegatedToId: child.taskId, + awaitingChildId: child.taskId, + childIds, + } + }) + this.recentTasksCache = undefined + if (this.isViewLaunched) { + const updatedItem = this.taskHistoryStore.get(parentTaskId) + if (updatedItem) { + await this.postMessageToWebview({ type: "taskHistoryItemUpdated", taskHistoryItem: updatedItem }) + } + } } catch (err) { this.log( `[delegateParentAndOpenChild] Failed to persist parent metadata for ${parentTaskId} -> ${child.taskId}: ${ diff --git a/src/package.json b/src/package.json index 69e81cb649..43032c9e58 100644 --- a/src/package.json +++ b/src/package.json @@ -395,9 +395,9 @@ "description": "%settings.useAgentRules.description%" }, "tumble-code.apiRequestTimeout": { - "type": "number", + "type": "integer", "default": 600, - "minimum": 0, + "minimum": 1, "maximum": 3600, "description": "%settings.apiRequestTimeout.description%" }, @@ -490,7 +490,7 @@ "@google/genai": "^1.29.1", "@lmstudio/sdk": "^1.1.1", "@mistralai/mistralai": "^1.9.18", - "@modelcontextprotocol/sdk": "1.12.0", + "@modelcontextprotocol/sdk": "1.26.0", "@qdrant/js-client-rest": "^1.14.0", "@roo-code/cloud": "workspace:^", "@roo-code/core": "workspace:^", @@ -500,7 +500,7 @@ "@vscode/codicons": "^0.0.36", "ai-sdk-provider-poe": "2.0.18", "async-mutex": "^0.5.0", - "axios": "^1.16.1", + "axios": "^1.18.0", "cheerio": "^1.0.0", "chokidar": "^4.0.1", "clone-deep": "^4.0.1", @@ -556,7 +556,7 @@ "tmp": "^0.2.3", "tree-sitter-wasms": "^0.1.12", "turndown": "^7.2.0", - "undici": "^6.21.3", + "undici": "^6.27.0", "uuid": "^11.1.1", "vscode-material-icons": "^0.1.1", "web-tree-sitter": "^0.25.6", @@ -567,7 +567,6 @@ }, "devDependencies": { "@ai-sdk/openai-compatible": "^2.0.28", - "@openrouter/ai-sdk-provider": "^2.1.1", "@roo-code/build": "workspace:^", "@roo-code/config-eslint": "workspace:^", "@roo-code/config-typescript": "workspace:^", @@ -590,20 +589,15 @@ "@types/tmp": "^0.2.6", "@types/turndown": "^5.0.5", "@types/vscode": "^1.84.0", - "@vscode/test-electron": "^2.5.2", "@vscode/vsce": "3.3.2", "ai": "^6.0.75", "esbuild-wasm": "^0.25.0", - "execa": "^9.5.2", + "execa": "^9.6.1", "glob": "^11.1.0", "mkdirp": "^3.0.1", "nock": "^14.0.4", - "npm-run-all2": "^8.0.1", "ovsx": "0.10.4", "rimraf": "^6.0.1", - "tsup": "^8.4.0", - "tsx": "^4.19.3", - "vitest": "^3.2.3", - "zod-to-ts": "^1.2.0" + "vitest": "^3.2.3" } } diff --git a/src/package.nls.ca.json b/src/package.nls.ca.json index e3fff590f6..74804862e5 100644 --- a/src/package.nls.ca.json +++ b/src/package.nls.ca.json @@ -40,7 +40,7 @@ "settings.autoImportSettingsPath.description": "Ruta a un fitxer de configuració de Tumble Code per importar automàticament en iniciar l'extensió. Admet rutes absolutes i rutes relatives al directori d'inici (per exemple, '~/Documents/tumble-code-settings.json'). Deixeu-ho en blanc per desactivar la importació automàtica.", "settings.maximumIndexedFilesForFileSearch.description": "Nombre màxim de fitxers per indexar per a la funció de cerca de fitxers @. Valors més alts proporcionen millors resultats de cerca en projectes grans però poden utilitzar més memòria. Per defecte: 10.000.", "settings.useAgentRules.description": "Activa la càrrega de fitxers AGENTS.md per a regles específiques de l'agent (vegeu https://agent-rules.org/)", - "settings.apiRequestTimeout.description": "Temps màxim en segons per esperar les respostes de l'API (0 = sense temps d'espera, 1-3600s, per defecte: 600s). Es recomanen valors més alts per a proveïdors locals com LM Studio i Ollama que poden necessitar més temps de processament.", + "settings.apiRequestTimeout.description": "Temps d'espera de resposta API (segons, predeterminat: 600, rang: 1–3600). Es recomanen valors més alts per a proveïdors locals. Proveïdors no admesos: Amazon Bedrock, Google Gemini(directly or through the Vertex AI platform), Mistral, Moonshot, Ollama, Poe, VS Code LM API.", "settings.newTaskRequireTodos.description": "Requerir el paràmetre de tasques pendents quan es creïn noves tasques amb l'eina new_task", "settings.codeIndex.embeddingBatchSize.description": "La mida del lot per a operacions d'incrustació durant la indexació de codi. Ajusta això segons els límits del teu proveïdor d'API. Per defecte és 60.", "settings.debug.description": "Activa el mode de depuració per mostrar botons addicionals per veure l'historial de conversa de l'API i els missatges de la interfície d'usuari com a JSON embellert en fitxers temporals.", diff --git a/src/package.nls.de.json b/src/package.nls.de.json index d8e4cb30e1..3835cc8c36 100644 --- a/src/package.nls.de.json +++ b/src/package.nls.de.json @@ -40,7 +40,7 @@ "settings.autoImportSettingsPath.description": "Pfad zu einer Tumble Code-Konfigurationsdatei, die beim Start der Erweiterung automatisch importiert wird. Unterstützt absolute Pfade und Pfade relativ zum Home-Verzeichnis (z.B. '~/Documents/tumble-code-settings.json'). Leer lassen, um den automatischen Import zu deaktivieren.", "settings.maximumIndexedFilesForFileSearch.description": "Maximale Anzahl der zu indizierenden Dateien für die @-Dateisuchfunktion. Höhere Werte bieten bessere Suchergebnisse in großen Projekten, können aber mehr Speicher verbrauchen. Standard: 10.000.", "settings.useAgentRules.description": "Aktiviert das Laden von AGENTS.md-Dateien für agentenspezifische Regeln (siehe https://agent-rules.org/)", - "settings.apiRequestTimeout.description": "Maximale Wartezeit in Sekunden auf API-Antworten (0 = kein Timeout, 1-3600s, Standard: 600s). Höhere Werte werden für lokale Anbieter wie LM Studio und Ollama empfohlen, die möglicherweise mehr Verarbeitungszeit benötigen.", + "settings.apiRequestTimeout.description": "API-Antwort-Timeout (Sekunden, Standard: 600, Bereich: 1–3600). Höhere Werte werden für lokale Anbieter empfohlen. Nicht unterstützte Anbieter: Amazon Bedrock, Google Gemini(directly or through the Vertex AI platform), Mistral, Moonshot, Ollama, Poe, VS Code LM API.", "settings.newTaskRequireTodos.description": "Todos-Parameter beim Erstellen neuer Aufgaben mit dem new_task-Tool erfordern", "settings.codeIndex.embeddingBatchSize.description": "Die Batch-Größe für Embedding-Operationen während der Code-Indexierung. Passe dies an die Limits deines API-Anbieters an. Standard ist 60.", "settings.debug.description": "Aktiviere den Debug-Modus, um zusätzliche Schaltflächen zum Anzeigen des API-Konversationsverlaufs und der UI-Nachrichten als formatiertes JSON in temporären Dateien anzuzeigen.", diff --git a/src/package.nls.es.json b/src/package.nls.es.json index c42c55df56..7afe89ddba 100644 --- a/src/package.nls.es.json +++ b/src/package.nls.es.json @@ -40,7 +40,7 @@ "settings.autoImportSettingsPath.description": "Ruta a un archivo de configuración de Tumble Code para importar automáticamente al iniciar la extensión. Admite rutas absolutas y rutas relativas al directorio de inicio (por ejemplo, '~/Documents/tumble-code-settings.json'). Dejar vacío para desactivar la importación automática.", "settings.maximumIndexedFilesForFileSearch.description": "Número máximo de archivos a indexar para la función de búsqueda de archivos @. Valores más altos proporcionan mejores resultados de búsqueda en proyectos grandes pero pueden usar más memoria. Por defecto: 10.000.", "settings.useAgentRules.description": "Habilita la carga de archivos AGENTS.md para reglas específicas del agente (ver https://agent-rules.org/)", - "settings.apiRequestTimeout.description": "Tiempo máximo en segundos de espera para las respuestas de la API (0 = sin tiempo de espera, 1-3600s, por defecto: 600s). Se recomiendan valores más altos para proveedores locales como LM Studio y Ollama que puedan necesitar más tiempo de procesamiento.", + "settings.apiRequestTimeout.description": "Tiempo de espera de respuesta API (segundos, predeterminado: 600, rango: 1–3600). Se recomiendan valores más altos para proveedores locales. Proveedores no compatibles: Amazon Bedrock, Google Gemini(directly or through the Vertex AI platform), Mistral, Moonshot, Ollama, Poe, VS Code LM API.", "settings.newTaskRequireTodos.description": "Requerir el parámetro todos al crear nuevas tareas con la herramienta new_task", "settings.codeIndex.embeddingBatchSize.description": "El tamaño del lote para operaciones de embedding durante la indexación de código. Ajusta esto según los límites de tu proveedor de API. Por defecto es 60.", "settings.debug.description": "Activa el modo de depuración para mostrar botones adicionales para ver el historial de conversación de API y los mensajes de la interfaz de usuario como JSON embellecido en archivos temporales.", diff --git a/src/package.nls.fr.json b/src/package.nls.fr.json index fe1d7490d0..2b7a3cdfcb 100644 --- a/src/package.nls.fr.json +++ b/src/package.nls.fr.json @@ -40,7 +40,7 @@ "settings.autoImportSettingsPath.description": "Chemin d'accès à un fichier de configuration Tumble Code à importer automatiquement au démarrage de l'extension. Prend en charge les chemins absolus et les chemins relatifs au répertoire de base (par exemple, '~/Documents/tumble-code-settings.json'). Laisser vide pour désactiver l'importation automatique.", "settings.maximumIndexedFilesForFileSearch.description": "Nombre maximum de fichiers à indexer pour la fonctionnalité de recherche de fichiers @. Des valeurs plus élevées offrent de meilleurs résultats de recherche dans les grands projets mais peuvent consommer plus de mémoire. Par défaut : 10 000.", "settings.useAgentRules.description": "Activer le chargement des fichiers AGENTS.md pour les règles spécifiques à l'agent (voir https://agent-rules.org/)", - "settings.apiRequestTimeout.description": "Temps maximum en secondes d'attente pour les réponses de l'API (0 = pas de timeout, 1-3600s, par défaut : 600s). Des valeurs plus élevées sont recommandées pour les fournisseurs locaux comme LM Studio et Ollama qui peuvent nécessiter plus de temps de traitement.", + "settings.apiRequestTimeout.description": "Délai d'attente des réponses API (secondes, défaut : 600, plage : 1–3600). Des valeurs plus élevées sont recommandées pour les fournisseurs locaux. Fournisseurs non pris en charge : Amazon Bedrock, Google Gemini(directly or through the Vertex AI platform), Mistral, Moonshot, Ollama, Poe, VS Code LM API.", "settings.newTaskRequireTodos.description": "Exiger le paramètre todos lors de la création de nouvelles tâches avec l'outil new_task", "settings.codeIndex.embeddingBatchSize.description": "La taille du lot pour les opérations d'embedding lors de l'indexation du code. Ajustez ceci selon les limites de votre fournisseur d'API. Par défaut, c'est 60.", "settings.debug.description": "Active le mode debug pour afficher des boutons supplémentaires permettant de visualiser l'historique de conversation de l'API et les messages de l'interface utilisateur sous forme de JSON formaté dans des fichiers temporaires.", diff --git a/src/package.nls.hi.json b/src/package.nls.hi.json index 6395915723..5ac9091014 100644 --- a/src/package.nls.hi.json +++ b/src/package.nls.hi.json @@ -40,7 +40,7 @@ "settings.autoImportSettingsPath.description": "Tumble Code कॉन्फ़िगरेशन फ़ाइल का पथ जिसे एक्सटेंशन स्टार्टअप पर स्वचालित रूप से आयात किया जाएगा। होम डायरेक्टरी के सापेक्ष पूर्ण पथ और पथों का समर्थन करता है (उदाहरण के लिए '~/Documents/tumble-code-settings.json')। ऑटो-इंपोर्ट को अक्षम करने के लिए खाली छोड़ दें।", "settings.maximumIndexedFilesForFileSearch.description": "@ फ़ाइल खोज सुविधा के लिए अनुक्रमित करने के लिए फ़ाइलों की अधिकतम संख्या। उच्च मान बड़ी परियोजनाओं में बेहतर खोज परिणाम प्रदान करते हैं लेकिन अधिक मेमोरी का उपयोग कर सकते हैं। डिफ़ॉल्ट: 10,000।", "settings.useAgentRules.description": "एजेंट-विशिष्ट नियमों के लिए AGENTS.md फ़ाइलों को लोड करना सक्षम करें (देखें https://agent-rules.org/)", - "settings.apiRequestTimeout.description": "एपीआई प्रतिक्रियाओं की प्रतीक्षा करने के लिए सेकंड में अधिकतम समय (0 = कोई टाइमआउट नहीं, 1-3600s, डिफ़ॉल्ट: 600s)। एलएम स्टूडियो और ओलामा जैसे स्थानीय प्रदाताओं के लिए उच्च मानों की सिफारिश की जाती है जिन्हें अधिक प्रसंस्करण समय की आवश्यकता हो सकती है।", + "settings.apiRequestTimeout.description": "API प्रतिक्रिया टाइमआउट (सेकंड, डिफ़ॉल्ट: 600, रेंज: 1–3600)। स्थानीय प्रदाताओं के लिए उच्च मान अनुशंसित हैं। असमर्थित प्रदाता: Amazon Bedrock, Google Gemini(directly or through the Vertex AI platform), Mistral, Moonshot, Ollama, Poe, VS Code LM API।", "settings.newTaskRequireTodos.description": "new_task टूल के साथ नए कार्य बनाते समय टूडू पैरामीटर की आवश्यकता होती है", "settings.codeIndex.embeddingBatchSize.description": "कोड इंडेक्सिंग के दौरान एम्बेडिंग ऑपरेशन के लिए बैच साइज़। इसे अपने API प्रदाता की सीमाओं के अनुसार समायोजित करें। डिफ़ॉल्ट 60 है।", "settings.debug.description": "API conversation history और UI messages को temporary files में prettified JSON के रूप में देखने के लिए अतिरिक्त बटन दिखाने के लिए debug mode सक्षम करें।", diff --git a/src/package.nls.id.json b/src/package.nls.id.json index 0f00dbc797..2cf5ac5f08 100644 --- a/src/package.nls.id.json +++ b/src/package.nls.id.json @@ -40,7 +40,7 @@ "settings.autoImportSettingsPath.description": "Path ke file konfigurasi Tumble Code untuk diimpor secara otomatis saat ekstensi dimulai. Mendukung path absolut dan path relatif terhadap direktori home (misalnya '~/Documents/tumble-code-settings.json'). Biarkan kosong untuk menonaktifkan impor otomatis.", "settings.maximumIndexedFilesForFileSearch.description": "Jumlah maksimum file yang akan diindeks untuk fitur pencarian file @. Nilai yang lebih besar memberikan hasil pencarian yang lebih baik di proyek besar tetapi mungkin menggunakan lebih banyak memori. Default: 10.000.", "settings.useAgentRules.description": "Aktifkan pemuatan file AGENTS.md untuk aturan khusus agen (lihat https://agent-rules.org/)", - "settings.apiRequestTimeout.description": "Waktu maksimum dalam detik untuk menunggu respons API (0 = tidak ada batas waktu, 1-3600s, default: 600s). Nilai yang lebih tinggi disarankan untuk penyedia lokal seperti LM Studio dan Ollama yang mungkin memerlukan lebih banyak waktu pemrosesan.", + "settings.apiRequestTimeout.description": "Batas waktu respons API (detik, default: 600, rentang: 1–3600). Nilai lebih tinggi direkomendasikan untuk penyedia lokal. Penyedia tidak didukung: Amazon Bedrock, Google Gemini(directly or through the Vertex AI platform), Mistral, Moonshot, Ollama, Poe, VS Code LM API.", "settings.newTaskRequireTodos.description": "Memerlukan parameter todos saat membuat tugas baru dengan alat new_task", "settings.codeIndex.embeddingBatchSize.description": "Ukuran batch untuk operasi embedding selama pengindeksan kode. Sesuaikan ini berdasarkan batas penyedia API kamu. Default adalah 60.", "settings.debug.description": "Aktifkan mode debug untuk menampilkan tombol tambahan untuk melihat riwayat percakapan API dan pesan UI sebagai JSON yang diformat dalam file sementara.", diff --git a/src/package.nls.it.json b/src/package.nls.it.json index f93a7cbe2f..a49ba48592 100644 --- a/src/package.nls.it.json +++ b/src/package.nls.it.json @@ -40,7 +40,7 @@ "settings.autoImportSettingsPath.description": "Percorso di un file di configurazione di Tumble Code da importare automaticamente all'avvio dell'estensione. Supporta percorsi assoluti e percorsi relativi alla directory home (ad es. '~/Documents/tumble-code-settings.json'). Lasciare vuoto per disabilitare l'importazione automatica.", "settings.maximumIndexedFilesForFileSearch.description": "Numero massimo di file da indicizzare per la funzionalità di ricerca file @. Valori più alti forniscono migliori risultati di ricerca in progetti grandi ma possono consumare più memoria. Predefinito: 10.000.", "settings.useAgentRules.description": "Abilita il caricamento dei file AGENTS.md per regole specifiche dell'agente (vedi https://agent-rules.org/)", - "settings.apiRequestTimeout.description": "Tempo massimo in secondi di attesa per le risposte API (0 = nessun timeout, 1-3600s, predefinito: 600s). Valori più alti sono consigliati per provider locali come LM Studio e Ollama che potrebbero richiedere più tempo di elaborazione.", + "settings.apiRequestTimeout.description": "Timeout risposta API (secondi, predefinito: 600, intervallo: 1–3600). Valori più alti sono consigliati per provider locali. Provider non supportati: Amazon Bedrock, Google Gemini(directly or through the Vertex AI platform), Mistral, Moonshot, Ollama, Poe, VS Code LM API.", "settings.newTaskRequireTodos.description": "Richiedere il parametro todos quando si creano nuove attività con lo strumento new_task", "settings.codeIndex.embeddingBatchSize.description": "La dimensione del batch per le operazioni di embedding durante l'indicizzazione del codice. Regola questo in base ai limiti del tuo provider API. Il valore predefinito è 60.", "settings.debug.description": "Abilita la modalità debug per mostrare pulsanti aggiuntivi per visualizzare la cronologia delle conversazioni API e i messaggi dell'interfaccia utente come JSON formattato in file temporanei.", diff --git a/src/package.nls.ja.json b/src/package.nls.ja.json index 2952a4a6cf..eaf55c5b00 100644 --- a/src/package.nls.ja.json +++ b/src/package.nls.ja.json @@ -40,7 +40,7 @@ "settings.autoImportSettingsPath.description": "拡張機能の起動時に自動的にインポートするTumble Code設定ファイルへのパス。絶対パスとホームディレクトリからの相対パスをサポートします(例:'~/Documents/tumble-code-settings.json')。自動インポートを無効にするには、空のままにします。", "settings.maximumIndexedFilesForFileSearch.description": "@ファイル検索機能のためにインデックス化するファイルの最大数。大きな値は大規模プロジェクトでより良い検索結果を提供しますが、より多くのメモリを使用する可能性があります。デフォルト: 10,000。", "settings.useAgentRules.description": "エージェント固有のルールのためにAGENTS.mdファイルの読み込みを有効にします(参照:https://agent-rules.org/)", - "settings.apiRequestTimeout.description": "API応答を待機する最大時間(秒)(0 = タイムアウトなし、1-3600秒、デフォルト: 600秒)。LM StudioやOllamaのような、より多くの処理時間を必要とする可能性のあるローカルプロバイダーには、より高い値が推奨されます。", + "settings.apiRequestTimeout.description": "API応答タイムアウト(秒、デフォルト:600、範囲:1–3600)。ローカルプロバイダーには高い値を推奨します。非対応プロバイダー:Amazon Bedrock、Google Gemini(directly or through the Vertex AI platform)、Mistral、Moonshot、Ollama、Poe、VS Code LM API。", "settings.newTaskRequireTodos.description": "new_taskツールで新しいタスクを作成する際にtodosパラメータを必須にする", "settings.codeIndex.embeddingBatchSize.description": "コードインデックス作成中のエンベディング操作のバッチサイズ。APIプロバイダーの制限に基づいてこれを調整してください。デフォルトは60です。", "settings.debug.description": "デバッグモードを有効にして、API会話履歴とUIメッセージをフォーマットされたJSONとして一時ファイルで表示するための追加ボタンを表示します。", diff --git a/src/package.nls.json b/src/package.nls.json index e0d3f74d91..7edce79373 100644 --- a/src/package.nls.json +++ b/src/package.nls.json @@ -40,7 +40,7 @@ "settings.autoImportSettingsPath.description": "Path to a Tumble Code configuration file to automatically import on extension startup. Supports absolute paths and paths relative to the home directory (e.g. '~/Documents/tumble-code-settings.json'). Leave empty to disable auto-import.", "settings.maximumIndexedFilesForFileSearch.description": "Maximum number of files to index for the @ file search feature. Higher values provide better search results in large projects but may use more memory. Default: 10,000.", "settings.useAgentRules.description": "Enable loading of AGENTS.md files for agent-specific rules (see https://agent-rules.org/)", - "settings.apiRequestTimeout.description": "Maximum time in seconds to wait for API responses (0 = no timeout, 1-3600s, default: 600s). Higher values are recommended for local providers like LM Studio and Ollama that may need more processing time.", + "settings.apiRequestTimeout.description": "API response timeout (seconds, default: 600, range: 1–3600). Higher values are recommended for local providers. Unsupported providers: Amazon Bedrock, Google Gemini(directly or through the Vertex AI platform), Mistral, Moonshot, Ollama, Poe, VS Code LM API.", "settings.newTaskRequireTodos.description": "Require todos parameter when creating new tasks with the new_task tool", "settings.codeIndex.embeddingBatchSize.description": "The batch size for embedding operations during code indexing. Adjust this based on your API provider's limits. Default is 60.", "settings.debug.description": "Enable debug mode to show additional buttons for viewing API conversation history and UI messages as prettified JSON in temporary files.", diff --git a/src/package.nls.ko.json b/src/package.nls.ko.json index ef7ac9ace9..cc9e0356e7 100644 --- a/src/package.nls.ko.json +++ b/src/package.nls.ko.json @@ -40,7 +40,7 @@ "settings.autoImportSettingsPath.description": "확장 프로그램 시작 시 자동으로 가져올 Tumble Code 구성 파일의 경로입니다. 절대 경로 및 홈 디렉토리에 대한 상대 경로를 지원합니다(예: '~/Documents/tumble-code-settings.json'). 자동 가져오기를 비활성화하려면 비워 둡니다.", "settings.maximumIndexedFilesForFileSearch.description": "@ 파일 검색 기능을 위해 인덱싱할 최대 파일 수입니다. 더 큰 값은 대형 프로젝트에서 더 나은 검색 결과를 제공하지만 더 많은 메모리를 사용할 수 있습니다. 기본값: 10,000.", "settings.useAgentRules.description": "에이전트별 규칙에 대한 AGENTS.md 파일 로드를 활성화합니다 (참조: https://agent-rules.org/)", - "settings.apiRequestTimeout.description": "API 응답을 기다리는 최대 시간(초) (0 = 시간 초과 없음, 1-3600초, 기본값: 600초). 더 많은 처리 시간이 필요할 수 있는 LM Studio 및 Ollama와 같은 로컬 공급자에게는 더 높은 값을 사용하는 것이 좋습니다.", + "settings.apiRequestTimeout.description": "API 응답 대기 시간(초, 기본값: 600, 범위: 1~3600). 로컬 공급자에는 더 높은 값을 권장합니다. 지원되지 않는 공급자: Amazon Bedrock, Google Gemini(directly or through the Vertex AI platform), Mistral, Moonshot, Ollama, Poe, VS Code LM API.", "settings.newTaskRequireTodos.description": "new_task 도구로 새 작업을 생성할 때 todos 매개변수 필요", "settings.codeIndex.embeddingBatchSize.description": "코드 인덱싱 중 임베딩 작업의 배치 크기입니다. API 공급자의 제한에 따라 이를 조정하세요. 기본값은 60입니다.", "settings.debug.description": "디버그 모드를 활성화하여 API 대화 기록과 UI 메시지를 임시 파일에 포맷된 JSON으로 보기 위한 추가 버튼을 표시합니다.", diff --git a/src/package.nls.nl.json b/src/package.nls.nl.json index 63e25c2b64..ba85668d8d 100644 --- a/src/package.nls.nl.json +++ b/src/package.nls.nl.json @@ -40,7 +40,7 @@ "settings.autoImportSettingsPath.description": "Pad naar een Tumble Code-configuratiebestand om automatisch te importeren bij het opstarten van de extensie. Ondersteunt absolute paden en paden ten opzichte van de thuismap (bijv. '~/Documents/tumble-code-settings.json'). Laat leeg om automatisch importeren uit te schakelen.", "settings.maximumIndexedFilesForFileSearch.description": "Maximaal aantal bestanden om te indexeren voor de @ bestandszoekfunctie. Hogere waarden bieden betere zoekresultaten in grote projecten maar kunnen meer geheugen gebruiken. Standaard: 10.000.", "settings.useAgentRules.description": "Laden van AGENTS.md-bestanden voor agentspecifieke regels inschakelen (zie https://agent-rules.org/)", - "settings.apiRequestTimeout.description": "Maximale tijd in seconden om te wachten op API-reacties (0 = geen time-out, 1-3600s, standaard: 600s). Hogere waarden worden aanbevolen voor lokale providers zoals LM Studio en Ollama die mogelijk meer verwerkingstijd nodig hebben.", + "settings.apiRequestTimeout.description": "API-respons time-out (seconden, standaard: 600, bereik: 1–3600). Hogere waarden worden aanbevolen voor lokale providers. Niet-ondersteunde providers: Amazon Bedrock, Google Gemini(directly or through the Vertex AI platform), Mistral, Moonshot, Ollama, Poe, VS Code LM API.", "settings.newTaskRequireTodos.description": "Todos-parameter vereisen bij het maken van nieuwe taken met de new_task tool", "settings.codeIndex.embeddingBatchSize.description": "De batchgrootte voor embedding-operaties tijdens code-indexering. Pas dit aan op basis van de limieten van je API-provider. Standaard is 60.", "settings.debug.description": "Schakel debug-modus in om extra knoppen te tonen voor het bekijken van API-conversatiegeschiedenis en UI-berichten als opgemaakte JSON in tijdelijke bestanden.", diff --git a/src/package.nls.pl.json b/src/package.nls.pl.json index f0ad967634..27015d3742 100644 --- a/src/package.nls.pl.json +++ b/src/package.nls.pl.json @@ -40,7 +40,7 @@ "settings.autoImportSettingsPath.description": "Ścieżka do pliku konfiguracyjnego Tumble Code, który ma być automatycznie importowany podczas uruchamiania rozszerzenia. Obsługuje ścieżki bezwzględne i ścieżki względne do katalogu domowego (np. '~/Documents/tumble-code-settings.json'). Pozostaw puste, aby wyłączyć automatyczne importowanie.", "settings.maximumIndexedFilesForFileSearch.description": "Maksymalna liczba plików do indeksowania dla funkcji wyszukiwania plików @. Wyższe wartości zapewniają lepsze wyniki wyszukiwania w dużych projektach, ale mogą zużywać więcej pamięci. Domyślnie: 10 000.", "settings.useAgentRules.description": "Włącz wczytywanie plików AGENTS.md dla reguł specyficznych dla agenta (zobacz https://agent-rules.org/)", - "settings.apiRequestTimeout.description": "Maksymalny czas w sekundach oczekiwania na odpowiedzi API (0 = brak limitu czasu, 1-3600s, domyślnie: 600s). Wyższe wartości są zalecane dla lokalnych dostawców, takich jak LM Studio i Ollama, którzy mogą potrzebować więcej czasu na przetwarzanie.", + "settings.apiRequestTimeout.description": "Limit czasu odpowiedzi API (sekundy, domyślnie: 600, zakres: 1–3600). Wyższe wartości są zalecane dla lokalnych dostawców. Nieobsługiwani dostawcy: Amazon Bedrock, Google Gemini(directly or through the Vertex AI platform), Mistral, Moonshot, Ollama, Poe, VS Code LM API.", "settings.newTaskRequireTodos.description": "Wymagaj parametru todos podczas tworzenia nowych zadań za pomocą narzędzia new_task", "settings.codeIndex.embeddingBatchSize.description": "Rozmiar partii dla operacji osadzania podczas indeksowania kodu. Dostosuj to w oparciu o limity twojego dostawcy API. Domyślnie to 60.", "settings.debug.description": "Włącz tryb debugowania, aby wyświetlić dodatkowe przyciski do przeglądania historii rozmów API i komunikatów interfejsu użytkownika jako sformatowany JSON w plikach tymczasowych.", diff --git a/src/package.nls.pt-BR.json b/src/package.nls.pt-BR.json index 4eb86cb10f..d20956d7c6 100644 --- a/src/package.nls.pt-BR.json +++ b/src/package.nls.pt-BR.json @@ -40,7 +40,7 @@ "settings.autoImportSettingsPath.description": "Caminho para um arquivo de configuração do Tumble Code para importar automaticamente na inicialização da extensão. Suporta caminhos absolutos e caminhos relativos ao diretório inicial (por exemplo, '~/Documents/tumble-code-settings.json'). Deixe em branco para desativar a importação automática.", "settings.maximumIndexedFilesForFileSearch.description": "Número máximo de arquivos a indexar para a funcionalidade de busca de arquivos @. Valores maiores fornecem melhores resultados de busca em projetos grandes, mas podem consumir mais memória. Padrão: 10.000.", "settings.useAgentRules.description": "Habilita o carregamento de arquivos AGENTS.md para regras específicas do agente (consulte https://agent-rules.org/)", - "settings.apiRequestTimeout.description": "Tempo máximo em segundos de espera pelas respostas da API (0 = sem tempo limite, 1-3600s, padrão: 600s). Valores mais altos são recomendados para provedores locais como LM Studio e Ollama que podem precisar de mais tempo de processamento.", + "settings.apiRequestTimeout.description": "Tempo limite de resposta da API (segundos, padrão: 600, intervalo: 1–3600). Valores mais altos são recomendados para provedores locais. Provedores não suportados: Amazon Bedrock, Google Gemini(directly or through the Vertex AI platform), Mistral, Moonshot, Ollama, Poe, VS Code LM API.", "settings.newTaskRequireTodos.description": "Exigir parâmetro todos ao criar novas tarefas com a ferramenta new_task", "settings.codeIndex.embeddingBatchSize.description": "O tamanho do lote para operações de embedding durante a indexação de código. Ajuste isso com base nos limites do seu provedor de API. O padrão é 60.", "settings.debug.description": "Ativa o modo de depuração para mostrar botões adicionais para visualizar o histórico de conversas da API e mensagens da interface como JSON formatado em arquivos temporários.", diff --git a/src/package.nls.ru.json b/src/package.nls.ru.json index 5d0408ddf5..db3b8d8269 100644 --- a/src/package.nls.ru.json +++ b/src/package.nls.ru.json @@ -40,7 +40,7 @@ "settings.autoImportSettingsPath.description": "Путь к файлу конфигурации Tumble Code для автоматического импорта при запуске расширения. Поддерживает абсолютные пути и пути относительно домашнего каталога (например, '~/Documents/tumble-code-settings.json'). Оставьте пустым, чтобы отключить автоматический импорт.", "settings.maximumIndexedFilesForFileSearch.description": "Максимальное количество файлов для индексации при поиске файлов @. Большие значения обеспечивают лучшие результаты поиска в крупных проектах, но могут потреблять больше памяти. По умолчанию: 10 000.", "settings.useAgentRules.description": "Включить загрузку файлов AGENTS.md для специфичных для агента правил (см. https://agent-rules.org/)", - "settings.apiRequestTimeout.description": "Максимальное время в секундах для ожидания ответов API (0 = нет тайм-аута, 1-3600 с, по умолчанию: 600 с). Рекомендуются более высокие значения для локальных провайдеров, таких как LM Studio и Ollama, которым может потребоваться больше времени на обработку.", + "settings.apiRequestTimeout.description": "Тайм-аут ответа API (секунды, по умолчанию: 600, диапазон: 1–3600). Более высокие значения рекомендуются для локальных провайдеров. Неподдерживаемые провайдеры: Amazon Bedrock, Google Gemini(directly or through the Vertex AI platform), Mistral, Moonshot, Ollama, Poe, VS Code LM API.", "settings.newTaskRequireTodos.description": "Требовать параметр todos при создании новых задач с помощью инструмента new_task", "settings.codeIndex.embeddingBatchSize.description": "Размер пакета для операций встраивания во время индексации кода. Настройте это в соответствии с ограничениями вашего API-провайдера. По умолчанию 60.", "settings.debug.description": "Включить режим отладки, чтобы отображать дополнительные кнопки для просмотра истории разговоров API и сообщений интерфейса в виде форматированного JSON во временных файлах.", diff --git a/src/package.nls.tr.json b/src/package.nls.tr.json index 8c60d13a8c..0b047db2e2 100644 --- a/src/package.nls.tr.json +++ b/src/package.nls.tr.json @@ -40,7 +40,7 @@ "settings.autoImportSettingsPath.description": "Uzantı başlangıcında otomatik olarak içe aktarılacak bir Tumble Code yapılandırma dosyasının yolu. Mutlak yolları ve ana dizine göreli yolları destekler (ör. '~/Documents/tumble-code-settings.json'). Otomatik içe aktarmayı devre dışı bırakmak için boş bırakın.", "settings.maximumIndexedFilesForFileSearch.description": "@ dosya arama özelliği için dizinlenecek maksimum dosya sayısı. Daha yüksek değerler büyük projelerde daha iyi arama sonuçları sağlar ancak daha fazla bellek kullanabilir. Varsayılan: 10.000.", "settings.useAgentRules.description": "Aracıya özgü kurallar için AGENTS.md dosyalarının yüklenmesini etkinleştirin (bkz. https://agent-rules.org/)", - "settings.apiRequestTimeout.description": "API yanıtları için beklenecek maksimum süre (saniye cinsinden) (0 = zaman aşımı yok, 1-3600s, varsayılan: 600s). LM Studio ve Ollama gibi daha fazla işlem süresi gerektirebilecek yerel sağlayıcılar için daha yüksek değerler önerilir.", + "settings.apiRequestTimeout.description": "API yanıt zaman aşımı (saniye, varsayılan: 600, aralık: 1–3600). Yerel sağlayıcılar için daha yüksek değerler önerilir. Desteklenmeyen sağlayıcılar: Amazon Bedrock, Google Gemini(directly or through the Vertex AI platform), Mistral, Moonshot, Ollama, Poe, VS Code LM API.", "settings.newTaskRequireTodos.description": "new_task aracıyla yeni görevler oluştururken todos parametresini gerekli kıl", "settings.codeIndex.embeddingBatchSize.description": "Kod indeksleme sırasında gömme işlemleri için toplu iş boyutu. Bunu API sağlayıcınızın sınırlarına göre ayarlayın. Varsayılan 60'tır.", "settings.debug.description": "API konuşma geçmişini ve kullanıcı arayüzü mesajlarını geçici dosyalarda biçimlendirilmiş JSON olarak görüntülemek için ek düğmeler göstermek üzere hata ayıklama modunu etkinleştir.", diff --git a/src/package.nls.vi.json b/src/package.nls.vi.json index 706333ca53..8a95f13249 100644 --- a/src/package.nls.vi.json +++ b/src/package.nls.vi.json @@ -40,7 +40,7 @@ "settings.autoImportSettingsPath.description": "Đường dẫn đến tệp cấu hình Tumble Code để tự động nhập khi khởi động tiện ích mở rộng. Hỗ trợ đường dẫn tuyệt đối và đường dẫn tương đối đến thư mục chính (ví dụ: '~/Documents/tumble-code-settings.json'). Để trống để tắt tính năng tự động nhập.", "settings.maximumIndexedFilesForFileSearch.description": "Số lượng tệp tối đa để lập chỉ mục cho tính năng tìm kiếm tệp @. Giá trị cao hơn cung cấp kết quả tìm kiếm tốt hơn trong các dự án lớn nhưng có thể sử dụng nhiều bộ nhớ hơn. Mặc định: 10.000.", "settings.useAgentRules.description": "Bật tải tệp AGENTS.md cho các quy tắc dành riêng cho tác nhân (xem https://agent-rules.org/)", - "settings.apiRequestTimeout.description": "Thời gian tối đa tính bằng giây để đợi phản hồi API (0 = không có thời gian chờ, 1-3600 giây, mặc định: 600 giây). Nên sử dụng các giá trị cao hơn cho các nhà cung cấp cục bộ như LM Studio và Ollama có thể cần thêm thời gian xử lý.", + "settings.apiRequestTimeout.description": "Thời gian chờ phản hồi API (giây, mặc định: 600, phạm vi: 1–3600). Nên dùng giá trị cao hơn cho các nhà cung cấp cục bộ. Nhà cung cấp không được hỗ trợ: Amazon Bedrock, Google Gemini(directly or through the Vertex AI platform), Mistral, Moonshot, Ollama, Poe, VS Code LM API.", "settings.newTaskRequireTodos.description": "Yêu cầu tham số todos khi tạo nhiệm vụ mới với công cụ new_task", "settings.codeIndex.embeddingBatchSize.description": "Kích thước lô cho các hoạt động nhúng trong quá trình lập chỉ mục mã. Điều chỉnh điều này dựa trên giới hạn của nhà cung cấp API của bạn. Mặc định là 60.", "settings.debug.description": "Bật chế độ gỡ lỗi để hiển thị các nút bổ sung để xem lịch sử hội thoại API và thông điệp giao diện người dùng dưới dạng JSON được định dạng trong các tệp tạm thời.", diff --git a/src/package.nls.zh-CN.json b/src/package.nls.zh-CN.json index 644a0e070f..9847fc33f5 100644 --- a/src/package.nls.zh-CN.json +++ b/src/package.nls.zh-CN.json @@ -40,7 +40,7 @@ "settings.autoImportSettingsPath.description": "Tumble Code 配置文件的路径,用于在扩展启动时自动导入。支持绝对路径和相对于主目录的路径(例如 '~/Documents/tumble-code-settings.json')。留空以禁用自动导入。", "settings.maximumIndexedFilesForFileSearch.description": "为 @ 文件搜索功能建立索引时要索引的最大文件数。较大的值在大型项目中提供更好的搜索结果,但可能占用更多内存。默认值:10,000。", "settings.useAgentRules.description": "为特定于代理的规则启用 AGENTS.md 文件的加载(请参阅 https://agent-rules.org/)", - "settings.apiRequestTimeout.description": "等待 API 响应的最长时间(秒)(0 = 无超时,1-3600秒,默认值:600秒)。对于像 LM Studio 和 Ollama 这样可能需要更多处理时间的本地提供商,建议使用更高的值。", + "settings.apiRequestTimeout.description": "API 响应超时(秒,默认值:600,范围:1–3600)。建议本地提供商使用更高的值。不适用的提供商:Amazon Bedrock、Google Gemini(directly or through the Vertex AI platform)、Mistral、Moonshot、Ollama、Poe、VS Code LM API。", "settings.newTaskRequireTodos.description": "使用 new_task 工具创建新任务时需要 todos 参数", "settings.codeIndex.embeddingBatchSize.description": "代码索引期间嵌入操作的批处理大小。根据 API 提供商的限制调整此设置。默认值为 60。", "settings.debug.description": "启用调试模式以显示额外按钮,用于在临时文件中以格式化 JSON 查看 API 对话历史和 UI 消息。", diff --git a/src/package.nls.zh-TW.json b/src/package.nls.zh-TW.json index 9878ac318d..5ea2a1ad47 100644 --- a/src/package.nls.zh-TW.json +++ b/src/package.nls.zh-TW.json @@ -40,7 +40,7 @@ "settings.autoImportSettingsPath.description": "Tumble Code 設定檔案的路徑,用於在擴充功能啟動時自動匯入。支援絕對路徑和相對於主目錄的路徑(例如 '~/Documents/tumble-code-settings.json')。留空以停用自動匯入。", "settings.maximumIndexedFilesForFileSearch.description": "為 @ 檔案搜尋功能建立索引時要索引的最大檔案數。較大的值在大型專案中提供更好的搜尋結果,但可能佔用更多記憶體。預設值:10,000。", "settings.useAgentRules.description": "為特定於代理的規則啟用 AGENTS.md 檔案的載入(請參閱 https://agent-rules.org/)", - "settings.apiRequestTimeout.description": "等待 API 回應的最長時間(秒)(0 = 無超時,1-3600秒,預設值:600秒)。對於像 LM Studio 和 Ollama 這樣可能需要更多處理時間的本地提供商,建議使用更高的值。", + "settings.apiRequestTimeout.description": "API 回應逾時(秒,預設值:600,範圍:1–3600)。建議本地提供商使用更高的值。不適用的提供商:Amazon Bedrock、Google Gemini(directly or through the Vertex AI platform)、Mistral、Moonshot、Ollama、Poe、VS Code LM API。", "settings.newTaskRequireTodos.description": "使用 new_task 工具建立新工作時需要 todos 參數", "settings.codeIndex.embeddingBatchSize.description": "程式碼索引期間嵌入操作的批次大小。根據 API 提供商的限制調整此設定。預設值為 60。", "settings.debug.description": "啟用偵錯模式以顯示額外按鈕,用於在暫存檔案中以格式化 JSON 檢視 API 對話歷史紀錄和使用者介面訊息。", diff --git a/src/utils/__tests__/tag-matcher.spec.ts b/src/utils/__tests__/tag-matcher.spec.ts new file mode 100644 index 0000000000..114eaccce7 --- /dev/null +++ b/src/utils/__tests__/tag-matcher.spec.ts @@ -0,0 +1,154 @@ +// npx vitest utils/__tests__/tag-matcher.spec.ts + +import { TagMatcher } from "../tag-matcher" + +describe("TagMatcher", () => { + describe("collect() chunk merging (line 52)", () => { + it("merges consecutive same-type chars into one chunk within a single call", () => { + // Two text chars in one update() → both hit collect() with matched=false + // second char finds last chunk same type → last.data += char (line 52) + const matcher = new TagMatcher("think") + const result = matcher.update("ab") + expect(result).toEqual([{ matched: false, data: "ab" }]) + }) + + it("merges consecutive reasoning chars within a single call", () => { + const matcher = new TagMatcher("think") + matcher.update("") + const result = matcher.update("ab") + expect(result).toEqual([{ matched: true, data: "ab" }]) + }) + }) + + describe("final() with a chunk argument (line 131)", () => { + it("processes a chunk passed directly to final()", () => { + // Call final() with a chunk instead of update() — exercises line 131 + const matcher = new TagMatcher("think") + const result = matcher.final("hello") + expect(result).toEqual([{ matched: false, data: "hello" }]) + }) + + it("processes a closing tag passed to final()", () => { + const matcher = new TagMatcher("think") + // Don't use update() — keeps reasoning in the buffer so final() flushes it + const result = matcher.final("reasoning") + expect(result.some((r) => r.matched && r.data === "reasoning")).toBe(true) + }) + }) + + describe("space handling in TAG_OPEN (lines 93-97)", () => { + it("tolerates a space before tag name has started (line 95: all candidates at index 0)", () => { + // "< think>" — space arrives when all candidates are at index 0 + // hits line 95 (continue), candidates survive, 't' then matches normally + const matcher = new TagMatcher("think") + const result = matcher.final("< think>content") + expect(result.some((r) => r.matched && r.data === "content")).toBe(true) + }) + + it("drops mid-match candidates on a space (line 97)", () => { + // "" — space arrives mid-match (index > 0, index < name.length) + // those candidates are dropped, tag is not opened + const matcher = new TagMatcher("think") + const result = matcher.final("content") + expect(result.every((r) => !r.matched)).toBe(true) + }) + }) + + describe("multi-tag constructor (string[])", () => { + it("opens and closes when constructed with array", () => { + const matcher = new TagMatcher(["think", "thought"]) + const result = matcher.final("deep reasoningdone") + expect(result.some((r) => r.matched && r.data === "deep reasoning")).toBe(true) + expect(result.some((r) => !r.matched && r.data === "done")).toBe(true) + }) + + it("opens and closes when constructed with array", () => { + const matcher = new TagMatcher(["think", "thought"]) + const result = matcher.final("thinkingdone") + expect(result.some((r) => r.matched && r.data === "thinking")).toBe(true) + expect(result.some((r) => !r.matched && r.data === "done")).toBe(true) + }) + + it(" open is not closed by (cross-tag isolation)", () => { + const matcher = new TagMatcher(["think", "thought"]) + const result = matcher.final("reasoningstill reasoningdone") + // must be treated as text since active tag is + expect(result.some((r) => r.matched && r.data.includes(""))).toBe(true) + expect(result.some((r) => !r.matched && r.data === "done")).toBe(true) + }) + + it(" open is not closed by (inverse cross-tag isolation)", () => { + const matcher = new TagMatcher(["think", "thought"]) + const result = matcher.final("reasoningstill reasoningdone") + // must be treated as text since active tag is + expect(result.some((r) => r.matched && r.data.includes(""))).toBe(true) + expect(result.some((r) => !r.matched && r.data === "done")).toBe(true) + }) + }) + + describe("chunk split at mid-tag-name boundary", () => { + it("correctly opens tag split across two update() calls", () => { + const matcher = new TagMatcher("think") + const first = matcher.update("content") + expect(second.some((r) => r.matched && r.data === "content")).toBe(true) + }) + }) + + describe("unmatched > in TAG_OPEN falls back to TEXT", () => { + it("treats as plain text when xyz is not a configured tag name", () => { + const matcher = new TagMatcher("think") + const result = matcher.final("content") + expect(result.every((r) => !r.matched)).toBe(true) + }) + + it("treats stray closing tag as plain text when no tag is open", () => { + const matcher = new TagMatcher(["think", "thought"]) + const result = matcher.final("finaltext") + expect(result).toEqual([{ matched: false, data: "finaltext" }]) + }) + + it("treats extra closing tag after a closed block as plain text", () => { + const matcher = new TagMatcher(["think", "thought"]) + const result = matcher.final("thinkingfinaltext") + expect(result.some((r) => r.matched && r.data === "thinking")).toBe(true) + expect(result.some((r) => !r.matched && r.data === "finaltext")).toBe(true) + }) + }) + + describe("nested tags", () => { + it("treats inner as text when outer is active", () => { + const matcher = new TagMatcher(["think", "thought"]) + const result = matcher.final("outerinner middlefinal") + expect(result.some((r) => r.matched && r.data.includes("inner"))).toBe(true) + expect(result.some((r) => !r.matched && r.data === "final")).toBe(true) + }) + + it("correctly unwinds nested same-name tags", () => { + const matcher = new TagMatcher(["think", "thought"]) + const result = matcher.final("outerinner middlefinal") + expect(result.some((r) => r.matched && r.data.includes("inner"))).toBe(true) + expect(result.some((r) => !r.matched && r.data === "final")).toBe(true) + }) + }) + + describe("space handling in TAG_CLOSE (line 119)", () => { + it("tolerates a trailing space before > in closing tag ()", () => { + // space at index === tagName.length hits line 119 (continue) + const matcher = new TagMatcher("think") + const result = matcher.final("reasoningafter") + expect(result.some((r) => r.matched && r.data === "reasoning")).toBe(true) + expect(result.some((r) => !r.matched && r.data === "after")).toBe(true) + }) + + it("tolerates a leading space after )", () => { + // space at index === 0 hits line 119 (continue) + const matcher = new TagMatcher("think") + const result = matcher.final("reasoningafter") + expect(result.some((r) => r.matched && r.data === "reasoning")).toBe(true) + expect(result.some((r) => !r.matched && r.data === "after")).toBe(true) + }) + }) +}) diff --git a/src/utils/tag-matcher.ts b/src/utils/tag-matcher.ts index 38d99a2904..f10bf2f1ee 100644 --- a/src/utils/tag-matcher.ts +++ b/src/utils/tag-matcher.ts @@ -17,11 +17,17 @@ export class TagMatcher { state: "TEXT" | "TAG_OPEN" | "TAG_CLOSE" = "TEXT" depth = 0 pointer = 0 + private readonly tagNames: string[] + private activeTagNames: string[] = [] + private candidates: { name: string; index: number }[] = [] + constructor( - readonly tagName: string, + tagName: string | [string, ...string[]], readonly transform?: (chunks: TagMatcherResult) => Result, readonly position = 0, - ) {} + ) { + this.tagNames = Array.isArray(tagName) ? tagName : [tagName] + } private collect() { if (!this.cached.length) { return @@ -56,39 +62,64 @@ export class TagMatcher { if (this.state === "TEXT") { if (char === "<" && (this.pointer <= this.position + 1 || this.matched)) { this.state = "TAG_OPEN" - this.index = 0 + if (this.depth === 0) { + this.candidates = this.tagNames.map((name) => ({ name, index: 0 })) + } else { + const active = this.activeTagNames.at(-1) + this.candidates = active ? [{ name: active, index: 0 }] : [] + } } else { this.collect() } } else if (this.state === "TAG_OPEN") { - if (char === ">" && this.index === this.tagName.length) { - this.state = "TEXT" - if (!this.matched) { - this.cached = [] + if (char === ">") { + const matched = this.candidates.find((c) => c.index === c.name.length) + if (matched) { + this.state = "TEXT" + this.activeTagNames.push(matched.name) + if (!this.matched) { + this.cached = [] + } + this.depth++ + this.matched = true + continue + } else { + this.state = "TEXT" + this.collect() } - this.depth++ - this.matched = true - } else if (this.index === 0 && char === "/") { + } else if (this.candidates.every((c) => c.index === 0) && char === "/") { this.state = "TAG_CLOSE" - } else if (char === " " && (this.index === 0 || this.index === this.tagName.length)) { + this.index = 0 continue - } else if (this.tagName[this.index] === char) { - this.index++ + } else if (char === " ") { + const remaining = this.candidates.filter((c) => c.index === 0 || c.index === c.name.length) + if (remaining.length === this.candidates.length) { + continue + } + this.candidates = remaining } else { - this.state = "TEXT" - this.collect() + this.candidates = this.candidates.filter((c) => c.name[c.index] === char) + for (const c of this.candidates) { + c.index++ + } + if (this.candidates.length === 0) { + this.state = "TEXT" + this.collect() + } } } else if (this.state === "TAG_CLOSE") { - if (char === ">" && this.index === this.tagName.length) { + const tagName = this.activeTagNames.at(-1) ?? this.tagNames[0] + if (char === ">" && this.index === tagName.length) { this.state = "TEXT" this.depth-- + this.activeTagNames.pop() this.matched = this.depth > 0 if (!this.matched) { this.cached = [] } - } else if (char === " " && (this.index === 0 || this.index === this.tagName.length)) { + } else if (char === " " && (this.index === 0 || this.index === tagName.length)) { continue - } else if (this.tagName[this.index] === char) { + } else if (tagName[this.index] === char) { this.index++ } else { this.state = "TEXT" @@ -102,10 +133,15 @@ export class TagMatcher { this._update(chunk) } this.collect() + this.candidates = [] + this.activeTagNames = [] return this.pop() } update(chunk: string) { this._update(chunk) + if (this.state === "TEXT") { + this.collect() + } return this.pop() } } diff --git a/webview-ui/package.json b/webview-ui/package.json index 676b190820..d8f600b546 100644 --- a/webview-ui/package.json +++ b/webview-ui/package.json @@ -48,7 +48,6 @@ "hast-util-to-jsx-runtime": "^2.3.6", "i18next": "^25.0.0", "katex": "^0.16.11", - "knuth-shuffle-seeded": "^1.0.6", "lru-cache": "^11.1.0", "lucide-react": "^0.518.0", "mermaid": "^11.15.0", @@ -93,17 +92,16 @@ "@types/jest": "^29.0.0", "@types/katex": "^0.16.7", "@types/node": "^20.19.25", - "@types/react": "^18.3.23", - "@types/react-dom": "^18.3.5", + "@types/react": "18.3.23", + "@types/react-dom": "18.3.7", "@types/shell-quote": "^1.7.5", "@types/stacktrace-js": "^2.0.3", "@types/vscode-webview": "^1.57.5", "@vitejs/plugin-react": "^5.2.0", "@vitest/ui": "^3.2.3", "babel-plugin-react-compiler": "^1.0.0", - "identity-obj-proxy": "^3.0.0", "jsdom": "^26.0.0", - "vite": "^8.0.13", + "vite": "^8.0.16", "vitest": "^3.2.3" } } diff --git a/webview-ui/src/types.d.ts b/webview-ui/src/types.d.ts deleted file mode 100644 index ff5ef47841..0000000000 --- a/webview-ui/src/types.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -// Type declarations for third-party modules - -declare module "knuth-shuffle-seeded" { - export default function knuthShuffle(array: T[], seed: any): T[] -}