Skip to content

fix: prevent parent task from hanging when subtask delegation returns after per-mode API profile switch#466

Closed
DScoNOIZ wants to merge 71 commits into
Zoo-Code-Org:mainfrom
DScoNOIZ:fix/issue-457-delegation-return
Closed

fix: prevent parent task from hanging when subtask delegation returns after per-mode API profile switch#466
DScoNOIZ wants to merge 71 commits into
Zoo-Code-Org:mainfrom
DScoNOIZ:fix/issue-457-delegation-return

Conversation

@DScoNOIZ
Copy link
Copy Markdown

@DScoNOIZ DScoNOIZ commented Jun 3, 2026

Description

Fixes #457 — a critical bug where subtask delegation never returns to the parent when two modes use different API configuration profiles (per-mode modeApiConfigs).

Problem

When an orchestrator/parent task delegates to a subtask whose mode uses a different modeApiConfigs profile than the parent, the flow silently stalls after the subtask completes. The parent is never resumed, requiring manual intervention.

Root Cause

The per-mode profile switch runs handleModeSwitchactivateProviderProfile, which leaves the parent task record as status: "active" while awaitingChildId still points at the child. Two guards then reject because they require status === "delegated":

  1. reopenParentFromDelegation() — an overly strict cancelledDelegationChildIds.has(childTaskId) guard blocks reopen permanently when cancelTask() fails to detach the parent (race condition in runDelegationTransition). The child is added to the blacklist, and the second attempt_completion call can never recover.

  2. AttemptCompletionTool delegation pre-check — only accepts status === "delegated" for the parent, missing the "active" state that occurs after a per-mode profile switch.

Changes

src/core/webview/ClineProvider.ts

  • Removed cancelledDelegationChildIds check from reopenParentFromDelegation() guard. The blacklist is redundant: cancelTask() sets parent status to "active" before adding to the blacklist. If the parent is still "delegated" and awaiting this child, it is safe to reopen. The status + awaitingChildId check already handles orphaned children.

src/core/tools/AttemptCompletionTool.ts

  • Added parent verification before delegation: verify parentHistory.status === "delegated" and parentHistory.awaitingChildId === task.taskId before calling delegateToParent(). If parent detached (cancelled/resumed), skip delegation gracefully.
  • Exempt subtask delegation from didToolFailInCurrentTurn guard (&& !task.parentTaskId). Subtasks finishing their assigned work should not be blocked by a parent's error flag.
  • Added console.warn diagnostics for failed reopen attempts.
  • Fixed error logging — removed stale historyLookupTaskId variable that could point at the wrong task.

Testing

  • Same-profile delegation still works (existing behavior preserved)
  • Cross-profile delegation now returns correctly
  • Parent verification prevents corrupting unrelated tasks
  • Subtask with cancelled parent falls through to normal completion flow

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of task completion for delegated subtasks, allowing completion even when tools fail during the current turn
    • Enhanced parent task resumption logic during delegation with better status verification
    • Added clearer error messaging when delegation operations encounter issues

roomote Bot and others added 30 commits May 16, 2026 19:11
* test(list-files,search-files): unskip read-only e2e tools

* chore: tighten list_files replay predicates

* test(e2e): replace opaque smoke codes with explicit instructions

* test(e2e): log say messages when running against real endpoints

* test(e2e): restore replay result validation

* test(e2e): address review feedback on readonly tool fixtures

* fix(e2e): address readonly tool review feedback

* test(e2e): address search-files review feedback

* test: fix Windows list-files cwd assertions

* test: fix Windows list-files expected file path

---------

Co-authored-by: Roomote <roomote@roocode.com>
Co-authored-by: Elliott de Launay <edelauna@gmail.com>
* test(read-file): unskipping read-file tests

* test(list-files,search-files): unskip read-only e2e tools

* test: unskip mutating vscode e2e tool suites

* test(e2e): replace opaque smoke codes with explicit instructions

* test(e2e): address review feedback on readonly tool fixtures

* test(list-files,search-files): unskip read-only e2e tools

* test(e2e): replace opaque smoke codes with explicit instructions

* test(e2e): address review feedback on readonly tool fixtures

* test(e2e): address search-files review feedback

* test(e2e): address mutating tool review feedback

* test: validate mutating replay payload fixtures

* test: drop read-file fixture whitespace churn

---------

Co-authored-by: Elliott de Launay <edelauna@gmail.com>
Co-authored-by: Roomote <roomote@roocode.com>
…-Org#160)

* fix: cap default glm output reservation

* test: align zai e2e max_tokens expectations

---------

Co-authored-by: Roomote <roomote@roocode.com>
* update default zai model to glm-5.1

* change default model to glm-4.7 per coding plan recs
Co-authored-by: Roomote <roomote@roocode.com>
* test: add xai provider e2e coverage

* test(xai): updates from local testing

* fix(api): dedupe Responses API streamed tool calls

* Fix Z.ai e2e state reset and Responses API tool fallback

* Fix xAI e2e probe isolation

---------

Co-authored-by: Roomote <roomote@roocode.com>
Co-authored-by: Elliott de Launay <edelauna@gmail.com>
Co-authored-by: Roomote <roomote@roocode.com>
Co-authored-by: Naved  Merchant <naved.merchant@gmail.com>
* test(e2e): unskip use_mcp_tool replay coverage

* test(e2e): relax mcp completion wording checks

* test(e2e): use real MCP prompts in use_mcp_tool suite

* test(e2e): unskip use_mcp_tool replay coverage with local MCP server

---------

Co-authored-by: Roomote <roomote@roocode.com>
Co-authored-by: Elliott de Launay <edelauna@gmail.com>
* refactor(core): extract monolith helpers

Refs Zoo-Code-Org#8

* fix: address PR 27 review feedback

* fix: defensively copy pending edit images

* fix: preserve reasoning summary field

* test: cover api conversation edge cases

* docs: clarify reasoning summary fallback
* FEATURE: Add Xiaomi MiMo as a first-class API provider

- New MimoHandler with reasoning_content passthrough for multi-turn tool calling
- Custom message conversion preserving MiMo's interleaved thinking chain
- Strip OpenAI-specific extensions (strict, additionalProperties) from tool schemas
- Models: mimo-v2.5-pro, mimo-v2.5, mimo-v2-flash with official pricing
- UI settings with 4 base URL options (3 Token Plan regions + Pay-as-you-go)
- Full wiring: types, schema, model picker, i18n, provider config

* Update openrouter.ts

* Preserve Mimo reasoning fields & switch AMS endpoint

Update Mimo provider config and UI to use the token-plan-ams endpoint instead of the old FRA host. Allow Mimo assistant messages with string content to include and preserve a reasoning_content field when present. Also avoid attaching mapped reasoning_details when converting messages for Mimo models (skip mapping if modelId matches /mimo/i) to preserve the provider's original shape. Types, API handler, transform logic, and the settings UI were updated to keep Mimo-specific reasoning data intact and align the endpoint selection.

* Add MiMo i18n translations to all 17 locales and unit tests

Also hide the "not sure which model" hint for the MiMo provider
since it's a static model list, not fetched dynamically.

* Address CodeRabbit review feedback

Fix Japanese translation consistency, strengthen base URL tests to
verify actual values, and add ModelPicker test for MiMo hint hiding.

* Expand MiMo test coverage and remove dev/null files from PR

Added tests for tool_call_partial streaming, cache token usage,
API error handling, message conversion pipeline, empty delta chunks,
and tools parameter presence/absence. Removed dev/null/ hook files
that were accidentally included in the branch.

* Use data-testid for automaticFetch hint instead of i18n key matching

* Remove mimo-v2-flash model that no longer supports thinking mode

* Replace mimo-v2-flash with mimo-v2-pro model

* Drop mimo-v2-flash since it doesn't support thinking mode

* Address maintainer feedback: revert unrelated changes, add i18n, fix stream_options

* Update doc links to use puter developer URLs

* Fix i18n: JP spacing, FR translations, add multimodal support to message conversion

* Add docstrings and document mimo-v2-flash exclusion rationale

* Address edelauna review: sanitize tool IDs, use handleProviderError, fix prompt cache, clean up comments

* Fold text into last tool message to preserve reasoning continuity

* Refactor: use shared convertToR1Format, processToolCalls, cleanup unused imports/props

* Remove dead temperature param, add 2-tier pricing, clarify strict stripping

* Remove strict/additionalProperties stripping — proxy no longer rejects it

* Use longContextPricing instead of tiers for cost calculation

* Remove stale convertToolsForOpenAI tests (stripping removed)
…#199)

Co-authored-by: Roomote <roomote@roocode.com>
Co-authored-by: Naved  Merchant <naved.merchant@gmail.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Roomote <roomote@roocode.com>
…Org#207)

* chore: split packages/core integration coverage lane

* [Chore] Fix core integration lane follow-up issues

* [Chore] Relax Windows worktree integration matching

* [Chore] Align worktree delete integration assertion with service contract

* chore: Move core dual-lane test:coverage inside the package

* chore: Flag core unit/integration Codecov uploads separately

* chore: tighten core coverage lane wiring

* Optimize CI cache and coverage uploads

* chore: add merge queue triggers for required CI

---------

Co-authored-by: Roomote <roomote@roocode.com>
Co-authored-by: Elliott de Launay <edelauna@gmail.com>
…-Org#210)

* test: add comprehensive unit tests for MimoHandler provider

Add 45 unit tests covering the MimoHandler provider:
- Constructor: model selection, default model fallback, base URL config
- getModel(): model info for v2.5-pro, v2.5, unknown models
- completePrompt(): happy path, multi-turn, JSON mode, model override
- createMessage() with Anthropic format and custom baseUrl
- Edge cases: empty choices, null content, network/rate limit errors
- Streaming: multi-tool calls, parallel tools, tool call IDs, interruption
- Sanitization: model ID, tool call IDs, prompt caching
- convertToR1Format: empty arrays, thinking blocks, nested structures

All 45 tests passing.

* fix: sanitize mimo streaming tool call ids

---------

Co-authored-by: Roomote <roomote@roocode.com>
* test(tools): add unit tests for SwitchModeTool

- Add comprehensive test suite for SwitchModeTool (18 tests)
- Cover mode slug validation (valid/invalid/missing)
- Test error propagation from mode loading
- Validate approval flow with correct params
- Test mode switching delegation to Controller
- Verify resolve() returns correct messages for both paths
- Follow existing test patterns from ask.spec.ts

* test: tighten switch mode review feedback cases

---------

Co-authored-by: Roomote <roomote@roocode.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…Org#226)

Closes Zoo-Code-Org#193

The temporary diagnostics file generated via the 'Report Issue' feature
retained the legacy 'roo-diagnostics-' prefix from the Roo Code fork.
This commit replaces it with 'zoo-diagnostics-' to match Zoo Code
branding.

Changes:
- diagnosticsHandler.ts: rename temp file template from
  roo-diagnostics- to zoo-diagnostics-
- diagnosticsHandler.spec.ts: update two assertions that matched the
  old prefix
* Merge upstream/main (Roo Code sunset) into Zoo Code

* test: updating e2e collision and optimization 4.7 tests

* chore: remove web-roo-code website and evals packages

* fix: deepseek e2e fixture matching and mock model list fallback

* refactor: make reasoningEffort optional in GetModelReasoningOptions

* test(deepseek): updating fixture format

* test: adding back coverage for telemetry and marketplace

* fix(Announcement): reverting finalRelease message key

* fix(ModeSelector): adding back telemetry

* fix(DismissibleUpsell): adding back telemetry

* fix(Announcement): adding back className

* fix(McpView): keeping marketplace view

* fix: adding back marketplace buttons

* fix: adding back telemetry

* fix: merge history

* test: bumping coverage

* merge: feedback
…oo-Code-Org#148)

* test: add Gemini provider e2e coverage

* fix(gemini): INVALID_ARGUMENT when loaded too many MCPs

* fix(gemini): resolve $ref, deep-merge allOf, align e2e fixtures

* refactor: dropping extra command

* fix: preserve top-level Gemini schema fields with allOf

* fix: guard recursive Gemini schema refs

* fix(gemini): preserve keyword-named tool parameters during schema sanitization

* test(gemini-e2e): wire aimock recording and use real model id

---------

Co-authored-by: Roomote <roomote@roocode.com>
Co-authored-by: Elliott de Launay <edelauna@gmail.com>
…o-Code-Org#154) (Zoo-Code-Org#240)

remark-gfm treats a single ~ around text (e.g. "~10", "1~3") as strikethrough,
unlike VS Code's markdown. Pass { singleTilde: false } so only "~~text~~"
renders as strikethrough.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
…ode-Org#215) (Zoo-Code-Org#233)

* fix(openai): omit temperature for models that don't support it (Zoo-Code-Org#215)

claude-opus-4-7 (and similar) reject requests through the OpenAI-Compatible
provider with a 400 error because 'temperature' is deprecated/unsupported.
Honor the model's existing supportsTemperature flag (already respected by
openai-native, gemini, lite-llm and vercel-ai-gateway) and omit temperature
from the streaming request when it is explicitly set to false. undefined keeps
sending temperature, preserving current behavior for all other models.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* test(openai): cover both temperature branches behind supportsTemperature gate (Zoo-Code-Org#215)

Add two tests so the temperature expression's branches are fully exercised:
- explicit modelTemperature (left side of the `??`)
- deepseek-reasoner default of DEEP_SEEK_DEFAULT_TEMPERATURE (truthy ternary)

Closes the partial-branch gap codecov/patch flagged on line 162.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
* chore: prepare v3.55.0 release

* chore: genericize release announcement highlight keys

* chore: remove v3.55.0 release artwork

---------

Co-authored-by: Roomote <roomote@roocode.com>
…e-Org#260)

* docs: clarify stable release version lane

* docs: use generic release version placeholders

---------

Co-authored-by: Roomote <roomote@roocode.com>
…Code-Org#245) (Zoo-Code-Org#261)

releaseTerminalsForTask only disassociated the terminal (taskId = undefined)
without aborting a still-running command, so cancel (✕) left the process
orphaned and the terminal stuck "busy" until a manual kill. Now abort the
running process for busy terminals on release. Adds TerminalRegistry tests.

Co-authored-by: Armando Vaquera <263793884+proyectoauraorg@users.noreply.github.com>
…-Org#186) (Zoo-Code-Org#230)

* fix(diff): repair truncated Grok diffs by reinserting missing markers (Zoo-Code-Org#186)

Grok frequently truncates streamed diffs, leaving SEARCH blocks without the
======= separator and/or the >>>>>>> REPLACE closer, which makes applyDiff
fail with 'Expected ======= was not found'. repairTruncatedDiff() detects
incomplete blocks and reinserts the missing markers while preserving valid
blocks and escaped markers.

* test(diff): add fixture-based regression tests for truncated Grok diffs (Zoo-Code-Org#186)

Per review feedback: end-to-end applyDiff() regression guards using realistic
truncated-Grok fixtures (missing >>>>>>> REPLACE, missing ======= separator),
plus a well-formed multi-block diff that must pass through unchanged.

* refactor(diff): address CodeRabbit review on truncated-diff repair (Zoo-Code-Org#186)

- Use a local repairedDiff in applyDiff instead of reassigning the diffContent
  parameter, keeping the original input observable.
- When a block has a closer but no ======= separator, splice the separator in
  before the existing >>>>>>> REPLACE rather than synthesizing a second closer.
- Strip leading Grok header directives (:start_line:, :end_line:, -------) before
  the first-line-is-SEARCH heuristic so metadata isn't treated as content; the
  directives are preserved on the SEARCH section.

* fix: remove unused needsRepair variable in repairTruncatedDiff

Address review feedback from @edelauna (code review #3284681514):
needsRepair was assigned but never read, making it a dead store.
The variable served no functional purpose in the repair loop,
so it has been removed.

---------

Co-authored-by: Armando Vaquera <263793884+proyectoauraorg@users.noreply.github.com>
* chore(webview): migrate build to Vite 8

* update lockfile

* remove minify on nightly mode
roomote Bot and others added 20 commits May 29, 2026 12:57
…g#383)

* chore: prepare v3.55.1 release

* chore: drop release image references

* fix: localize v3.55.1 marketplace notes

---------

Co-authored-by: Roomote <roomote@roocode.com>
…g#385)

Drops the no-op webview handler and the unused message types. No callers remain in the extension.

Co-authored-by: James Mtendamema <jmtendamema@geologicai.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…Org#400)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…de-Org#235)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…rg#258) (Zoo-Code-Org#275)

* feat(markdown): render GitHub-style alerts in the webview (Zoo-Code-Org#258)

GitHub-style alerts ([!NOTE], [!TIP], [!IMPORTANT], [!WARNING], [!CAUTION])
were rendered as plain blockquotes, losing their semantic meaning and visual
priority. Adds a focused remark transform (no new dependency) that detects a
leading alert marker in a blockquote and tags it, plus a blockquote component
that renders a codicon + label header and per-type accent styling using VS Code
theme variables. Normal blockquotes (and unsupported markers) render unchanged.

Closes Zoo-Code-Org#258

* fix(markdown): address PR Zoo-Code-Org#275 review feedback

---------

Co-authored-by: Armando Vaquera <263793884+proyectoauraorg@users.noreply.github.com>
Co-authored-by: Elliott de Launay <edelauna@gmail.com>
…oo-Code-Org#341)

* dep(tmp): removing unused dependancy

* chore(deps): remove unused dependencies from src

* test: remove stale default-shell mocks

---------

Co-authored-by: Elliott de Launay <edelauna@gmail.com>
Co-authored-by: Elliott de Launay <edelauna@gmail.com>
…oo-Code-Org#276)

* feat(settings): add configurable chat font size (Zoo-Code-Org#157)

The Zoo Code chat font could not be sized independently of VS Code's UI
zoom. Adds an optional chatFontSize setting (px, 8-32) surfaced as a slider
with a 'Use VS Code default' reset in the UI settings section.

When unset the appearance is unchanged: the --zoo-chat-font-size CSS var
defaults to --vscode-font-size, and the webview text scale derives from it.
When set, the value is applied to the document root and persisted via the
generic updateSettings path (nullish + null-on-reset, matching
allowedMaxRequests). Includes init-vs-user-edit webview tests and full i18n
for all 18 locales.

Closes Zoo-Code-Org#157

* fix(webview): normalize nullish chatFontSize in context value (Zoo-Code-Org#157)

* fix(settings): scope chat font size to the chat markdown surface (Zoo-Code-Org#157)

* feat(settings): emit telemetry for chat font size changes (Zoo-Code-Org#157)

The chat font size change/reset handlers now emit telemetry like the other
UI settings handlers in this file (ui_settings_chat_font_size_changed with
the value, and ui_settings_chat_font_size_reset). Covered by UISettings spec.

---------

Co-authored-by: Armando Vaquera <263793884+proyectoauraorg@users.noreply.github.com>
* test: unskip subtasks e2e suite

* test: tighten subtasks cancellation replay

* test(e2e): validation and bumping codecov

* fix: prevent child delegation start after parent metadata failure

* chore: address delegation review cleanup

* fix: delegated subtask lifecycle races

* fix: harden delegated subtask cancellation

* test(e2e): simplyfying e2e tests

---------

Co-authored-by: Roomote <roomote@roocode.com>
Co-authored-by: Elliott de Launay <edelauna@gmail.com>
… with native path (Zoo-Code-Org#222)

Add unit coverage for ReadFileTool: input validation, rooignore blocking,
directory/binary/image handling, image memory limits, approval flow, slice and
indentation modes, output structure, and the legacy multi-file format.

Source changes:
- Drop the temporary "[read_file] Legacy format detected" debug console.warn.
- Mirror the native path in the legacy read path: set didToolFailInCurrentTurn
  on rooignore blocks, directory reads, and read errors so a failed legacy read
  fails the tool turn consistently.
- Recognize re-hydrated bare-`files` calls in isLegacyReadFileParams (history
  persisted before the _legacyFormat flag existed).

Co-authored-by: Armando Vaquera <263793884+proyectoauraorg@users.noreply.github.com>
…de-Org#344)

* feat(zoo-gateway): add provider types, handler, and model fetcher

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(zoo-gateway): respect readonly client, real version header, safer fetch

- Stop reassigning RouterProvider.client; thread Zoo enrichment headers
  through openAiHeaders so a single OpenAI client is used.
- Replace npm_package_version (never populated at extension runtime)
  with Package.version from the shared package shim.
- Default the model list to [] on a structurally broken response so we
  log and recover instead of crashing on response.data.data being
  undefined.
- Bypass inFlightRefresh de-duplication for zoo-gateway: a refresh
  triggered after sign-out/sign-in must not return the previous user's
  in-flight response.
- Add fetcher unit tests covering auth header, timeout, error
  redaction, and bad-response handling.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(types): include zoo-gateway key in requestRouterModels record so RouterName stays exhaustive

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(router-models): expect zoo-gateway in requestRouterModels responses

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(zoo-gateway): add ZooGatewayHandler unit tests for codecov patch

Cover constructor auth guard, base URL resolution, streaming, task/mode headers, temperature, cache breakpoints, tool calls, and completePrompt.

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(zoo-gateway): mock cached-token + clear-token auth helpers

The downstream stack (settings-ui) calls getCachedZooCodeToken and
clearZooCodeToken from the auth handler. CI on stacked PRs merges base
into head so this spec runs against the cached-token-aware handler;
expand the auth module mock so the auth guard test exercises the real
throw path instead of vitest's missing-mock-export error.

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(zoo-gateway): align expected OpenAI headers with Zoo Code branding

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(zoo-gateway): defer auth check, fail-closed models, reuse Vercel schemas

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(zoo-gateway): accept models list without created/description fields

Co-authored-by: Cursor <cursoragent@cursor.com>

* refactor(zoo-gateway): centralize auth-scoped cache-skip via AUTH_SCOPED_PROVIDERS

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore(types): drop retired 'roo' provider from getApiProtocol allowlist

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: James Mtendamema <jmtendamema@geologicai.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…rg#242) (Zoo-Code-Org#247)

When "use custom temperature" is off, the OpenAI-Compatible provider still sent temperature: 0 (the fallback), so the model's server-side default never applied. Per the discussion on Zoo-Code-Org#242 (option A), omit the temperature field in that case. Preserves the supportsTemperature gate (Zoo-Code-Org#233), model-required defaults (deepseek-reasoner), and a deliberately-set 0.

Co-authored-by: Armando Vaquera <263793884+proyectoauraorg@users.noreply.github.com>
…Code-Org#161) (Zoo-Code-Org#274)

* feat(zai): expose configurable max output tokens for GLM models (Zoo-Code-Org#161)

Add a standalone "max output tokens" slider for models that advertise
`supportsMaxTokens` (e.g. Z.ai GLM) but do not surface a reasoning budget, and
send the chosen value to the provider as `max_tokens`.

- ThinkingBudget: extract the max-tokens slider into a small render helper shared
  by the standalone control and the reasoning-budget branch, and surface it for
  binary-reasoning models that also support a configurable max.
- getModelMaxOutputTokens: honor the user's `modelMaxTokens` override for
  `supportsMaxTokens` models (capped at the model ceiling) instead of the 20%
  context-window clamp, so the runtime budget matches what is sent to the provider.
- ProviderSettingsManager.export(): preserve `modelMaxTokens` for configurable-max
  models while dropping it for models that support neither reasoning budgets nor a
  configurable max.

* Update src/shared/api.ts

* Update webview-ui/src/components/settings/ThinkingBudget.tsx

---------

Co-authored-by: Armando Vaquera <263793884+proyectoauraorg@users.noreply.github.com>
Co-authored-by: edelauna <54631123+edelauna@users.noreply.github.com>
* feat(zoo-gateway): add provider types, handler, and model fetcher

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(zoo-gateway): respect readonly client, real version header, safer fetch

- Stop reassigning RouterProvider.client; thread Zoo enrichment headers
  through openAiHeaders so a single OpenAI client is used.
- Replace npm_package_version (never populated at extension runtime)
  with Package.version from the shared package shim.
- Default the model list to [] on a structurally broken response so we
  log and recover instead of crashing on response.data.data being
  undefined.
- Bypass inFlightRefresh de-duplication for zoo-gateway: a refresh
  triggered after sign-out/sign-in must not return the previous user's
  in-flight response.
- Add fetcher unit tests covering auth header, timeout, error
  redaction, and bad-response handling.

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(zoo-gateway): mock cached-token + clear-token auth helpers

The downstream stack (settings-ui) calls getCachedZooCodeToken and
clearZooCodeToken from the auth handler. CI on stacked PRs merges base
into head so this spec runs against the cached-token-aware handler;
expand the auth module mock so the auth guard test exercises the real
throw path instead of vitest's missing-mock-export error.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(zoo-gateway): add settings UI, validation, and i18n

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(zoo-gateway): dynamic dashboard URL and cached-token fallback

- Resolve ModelPicker serviceUrl from zooCodeBaseUrl so staging/dev
  environments link to the matching dashboard.
- Fall back to getCachedZooCodeToken() in the handler and model fetcher
  when the profile has not been seeded yet (auth before webview open).

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(deps): drop stale webview-ui package.json drift that broke frozen-lockfile installs

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(i18n): restore googleCloudCredentialsPathWarning and automaticFetch 'free' search hint dropped on rebase

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(zoo-gateway): pick default model from fetched list, prefer Sonnet 4.5

Resolves Sonnet 4.5 from the gateway model catalog instead of a static Vercel slug so test (Bedrock) and live accounts both get a valid default. Reassigns stale profile model IDs when they are not in the catalog.

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(zoo-gateway): cover dynamic default model picker for codecov patch

Exports pickZooGatewayDefaultModelId so the helper is unit-testable and adds
component tests for the auto-default useEffect (no-op while catalog loads,
auto-pick on empty profile, repair stale id, no-op when valid).

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(i18n): restore UTF-8 for Google Cloud warning and model picker strings

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(zoo-gateway): settings UI sign-in button, validation tests, defer auth

Co-authored-by: Cursor <cursoragent@cursor.com>

* refactor(zoo-gateway): localize auth-state UX to provider component

Move the zoo-gateway sign-in error out of the shared form-validation effect
in ApiOptions and into ZooGateway.tsx, where it renders inline via
ApiErrorMessage. ApiOptions can then drop zooCodeIsAuthenticated from its
useEffect dependency list, and validateApiConfigurationExcludingModelErrors
short-circuits zoo-gateway entirely.

Also rename the inline isSonnet45ModelId helper to isClaudeSonnetModelId
and let pickZooGatewayDefaultModelId express the version-priority order
directly, so the helper has no version baked into its name.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(zoo-gateway): enforce org provider check, simplify sign-in copy

The settings-form short-circuit for zoo-gateway also bypassed the
organization PROVIDER_NOT_ALLOWED check, so a workspace that disallows
zoo-gateway could not surface that error. Scope the short-circuit to
the keys/sign-in check only and let the org allowlist check run for
every provider.

Drop the quoted CTA from zooGatewaySignIn in all 18 locales: the
sign-in button is rendered immediately below the inline error, so a
duplicate label was just a drift hazard.

Co-authored-by: Cursor <cursoragent@cursor.com>

* refactor(webview): move provider model config + docs slugs out of ApiOptions

Lift the inline PROVIDER_MODEL_CONFIG map and the docs-slug lookup out
of ApiOptions.tsx into webview-ui/src/components/settings/utils/providerModelConfig.ts
behind getProviderModelConfig and getProviderDocsSlug helpers. ApiOptions
now reads provider-specific model fields, defaults, and docs slugs through
those helpers, leaving the component focused on rendering.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(i18n): align French zooGatewaySignIn to formal vous register

validation.zooGatewaySignIn used "Connecte-toi" while
providers.zooGateway.signInDescription uses "Connectez-vous".
Use vous consistently across both strings.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: James Mtendamema <jmtendamema@geologicai.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…ode-Org#277)

* feat(terminal): add inline terminal profile selection (Zoo-Code-Org#119)

Add a 'terminalProfile' setting that lets users choose which VS Code
terminal profile the inline terminal uses. On Windows the default
cmd/PowerShell shell may use a non-UTF-8 code page (e.g. GBK) and garble
output; selecting a UTF-8 profile such as Git Bash resolves this.

The setting reuses VS Code's terminal profile concept: when set, the
profile name is resolved against terminal.integrated.profiles.<platform>
to derive shellPath/shellArgs for createTerminal. When empty/unset the
default terminal behavior is preserved unchanged.

Adds backend unit tests for profile resolution and a webview test for
the settings dropdown wiring.

* fix(test): relax spy types for overloaded VS Code APIs (Zoo-Code-Org#119)

* fix(test): use ES import instead of require() in terminal profile spec (Zoo-Code-Org#119)

* refactor(terminal): address review feedback on inline terminal profile (Zoo-Code-Org#119)

- Route profile names through a dedicated allowlisted `requestTerminalProfiles`
  message instead of the generic `getVSCodeSetting` (which reads any key the
  webview supplies); the extension reads the profiles and returns only names.
- Preserve the profile's `env` (sanitized to string/null; null unsets a var),
  merged onto the base env in createTerminal.
- Clarify the setting copy (en + es) vs the 'Use Inline Terminal' description.
- Add tests: updateSettings->setTerminalProfile bridge, resolveWebviewView
  startup hydration, and profile env preservation/sanitization.

* fix(terminal): resolve profile path[] to first existing candidate (Zoo-Code-Org#119)

VS Code selects the first terminal-profile path candidate that exists on
disk; mirror that instead of always taking index 0, falling back to the
first non-empty candidate when none exist. Addresses CodeRabbit review on Zoo-Code-Org#277.

* fix(terminal): hide inline profile picker when inline execution is off

The terminal-profile dropdown only affects inline execution, which is active
when shell integration is disabled. It previously stayed visible and editable
even when inline mode was off, where it has no effect. Guard it with
terminalShellIntegrationDisabled (defaulting to shown, matching the checkbox),
mirroring the inline-only settings below. Addresses PR Zoo-Code-Org#277 review (edelauna).

* fix(terminal): address review feedback

* refactor(terminal): scope profile picker to VS Code integrated terminal, filter source-only profiles

* fix(terminal): scope profile config to user settings, filter shellArgs, fix PATH join

* refactor(terminal): address review feedback on profile picker

* refactor(terminal): address review feedback on profile picker

* refactor(terminal): promote no_shell_integration payload to typed object

* feat(terminal): replace pWaitFor with event-based shell integration wait; add cmd.exe fast-path

* fix(terminal): retry via execa silently when shell integration fails before submission

* fix(terminal): close idle terminals when profile changes

* fix(terminal): skip ZDOTDIR injection with profile override; clear cached profile on picker open

* test(e2e): smoke-test VS Code terminal profile override lifecycle

* fix(terminal): avoid replaying commands after shell integration failure

* fix(terminal): harden profile settings and cleanup

* Revert "feat(terminal): replace pWaitFor with event-based shell integration wait; add cmd.exe fast-path"

This reverts commit 40e17b1.

* fix(TerminalProcess): warning even when shell opens

---------

Co-authored-by: Armando Vaquera <263793884+proyectoauraorg@users.noreply.github.com>
Co-authored-by: Elliott de Launay <edelauna@gmail.com>
…de-Org#347)

* feat(zoo-gateway): auth callback, profile token sync, and sign-out

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(zoo-gateway): cover auth callback profile sync and sign-out

Add ClineProvider tests for handleZooCodeCallback, ensureZooGatewayProfileSeeded, and webviewMessageHandler zooCodeSignOut to satisfy codecov patch on PR Zoo-Code-Org#347.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(zoo-gateway): surface 401/402/403 errors with actionable toasts

Clear the cached token on 401 and offer sign-in. On insufficient credits
or budget limits, open the credits page. On account frozen/banned, open
support. Errors still propagate to the task layer after the toast.

Co-authored-by: Cursor <cursoragent@cursor.com>

* i18n(zoo-gateway): backfill zooAuth translations for 17 non-English locales

Adds session_expired, out_of_credits, account_unavailable, budget_exceeded
under zooAuth.errors and a new zooAuth.buttons block (sign_in, add_credits,
contact_support) introduced by the gateway 401/402/403 UX so check-translations
passes.

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(zoo-gateway): cover surfaceGatewayApiError UX branches for codecov patch

Adds vscode + i18n mocks and asserts the 401/402/403/429 paths in
surfaceGatewayApiError: token clear + sign-in URL on 401, add-credits
URL on 402 and budget-coded 429, support URL on 403, no-op on 429
without a budget code or on errors without a status. Also verifies the
helper still runs before completePrompt rewraps the upstream error.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(zoo-gateway): address PR review feedback on auth, seeding, and errors

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(zoo-gateway): cover stale baseUrl seeding path

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(zoo-gateway): sign-out clears stale profile tokens, simplify model fetch

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(zoo-gateway): drop stale profile-scan test for requestRouterModels

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(zoo-gateway): treat verify 5xx as transient, do not clear token

The website's /api/extension/auth/verify route now returns 503 when the
backend can't reach the database, instead of crashing. The extension
previously treated any non-OK response from this endpoint as a
definitively invalid token, which meant a transient backend hiccup
would silently clear the user's session and force a fresh sign-in.

verifyZooCodeToken now returns "unreachable" for 5xx responses (same
classification as a network error), so initZooCodeAuth keeps the cached
token in place and reports subscription status as "unknown" until the
backend recovers. handleAuthCallback shows the could-not-verify message
on 5xx so users see this is a temporary issue rather than a bad token.

Co-authored-by: Cursor <cursoragent@cursor.com>

* refactor(zoo-gateway): split error classification from UX, extract callback fan-out

Address review feedback on PR Zoo-Code-Org#347:

- zoo-gateway.ts: split surfaceGatewayApiError into a pure
  classifyGatewayApiError (error -> action) plus a thin UX layer that
  switches on the action. The classifier is exported and covered by
  focused unit tests, so the status/code -> action mapping no longer
  needs the VS Code notification mocks to verify.
- handleUri.ts: extract the per-instance token propagation loop into a
  propagateZooGatewayCallback helper, keeping the /auth-callback case
  focused on routing. Behaviour (sequential read-modify-write, per
  instance error isolation) is unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: isolate per-profile token cleanup on sign-out and assert state refresh

Wrap per-profile work in the zoo-gateway sign-out cleanup loop in its own
try/catch so one corrupted profile or failed write no longer aborts cleanup
of the remaining profiles. Also assert postStateToWebview runs on the
handleZooCodeCallback persistence-failure path.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: James Mtendamema <jmtendamema@geologicai.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…gs (Zoo-Code-Org#346)

* feat(privacy): remove extension LLM telemetry and observability section

Delete the extension-side LLM telemetry path (zoo-telemetry.ts and its
Task.ts hook) so BYOK requests no longer send usage metadata to zoocode.dev.
Drop the "Zoo Code Observability" section from PRIVACY.md entirely: server-side
gateway request logging is not extension behavior and is documented at
zoocode.dev like any other provider's, so the extension privacy policy should
not single it out.

Co-authored-by: Cursor <cursoragent@cursor.com>

* refactor(zoo-code-auth): drop subscription-status helpers with telemetry

The checkSubscriptionStatus / getCachedSubscriptionStatus helpers existed only
to gate the now-removed LLM telemetry path. Remove them and their tests.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
… after per-mode API profile switch

**Problem:**
When an orchestrator/parent task delegates to a subtask whose mode is bound to a different API-configuration profile (modeApiConfigs) than the parent, the subtask completes and calls attempt_completion, but control never returns to the parent. The run silently waits for manual click instead of auto-resuming.

**Root cause:**
handleModeSwitch → activateProviderProfile leaves the parent task record as status="active" while awaitingChildId still points at the child. Two guards then reject because they require parent status to be exactly "delegated":

1. reopenParentFromDelegation() had an overly strict guard checking cancelledDelegationChildIds.has(childTaskId). When cancelTask() fails to detach the parent due to a runDelegationTransition race, the child is added to the blacklist. On the second attempt_completion call, the guard blocks reopen permanently.

2. Guard in attempt_completion delegation pre-check only accepts status === "delegated".

**Fix:**
- Remove cancelledDelegationChildIds from reopenParentFromDelegation guard. The cancelledDelegationChildIds blacklist is redundant — cancelTask() already sets parent status to "active" before adding to blacklist. If parent is still "delegated" and awaiting this child, it is safe to reopen.
- Add parentHistory verification before delegation: check parentHistory.status === "delegated" && parentHistory.awaitingChildId === task.taskId before calling delegateToParent.
- Exempt subtask delegation from didToolFailInCurrentTurn guard (&& !task.parentTaskId) — the subtask is legitimately finishing assigned work.

Fixes Zoo-Code-Org#457
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 3, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3372710a-399a-4261-8316-b94998db221c

📥 Commits

Reviewing files that changed from the base of the PR and between 52a8cc0 and b2a8c5c.

📒 Files selected for processing (2)
  • src/core/tools/AttemptCompletionTool.ts
  • src/core/webview/ClineProvider.ts

📝 Walkthrough

Walkthrough

The PR refines subtask completion and return-to-parent flow by exempting delegated subtasks from tool-failure guards, re-validating that the parent is still actively awaiting the child before delegation, and simplifying the parent reopening precondition by removing a now-obsolete blacklist check. Changes span both the tool execution layer and the webview provider state management.

Changes

Subtask delegation return flow robustness

Layer / File(s) Summary
Subtask completion and delegation flow refinement
src/core/tools/AttemptCompletionTool.ts, src/core/webview/ClineProvider.ts
attempt_completion now exempts delegated subtasks from the tool-failure guard (lines 43–46), re-fetches parent task history to confirm the parent is still awaiting the child before delegation (lines 101–104), skips delegation and falls back to user confirmation if the parent is no longer awaiting (lines 121–129), and logs failures using correct task identifiers (line 139). On reopen failure, adds a warning explaining manual resume is required (lines 192–195). Parent reopening precondition in ClineProvider.reopenParentFromDelegation removes the cancelledDelegationChildIds blacklist check and relies solely on verifying parent status and awaitingChildId match (lines 3534–3540).

Sequence Diagram(s)

sequenceDiagram
  participant AttemptCompletion as AttemptCompletionTool
  participant ParentState as Parent Task State
  participant ClineProvider as ClineProvider.reopenParent
  participant User as User (fallback)
  
  AttemptCompletion->>AttemptCompletion: Check: tool failed but parentTaskId exists?
  Note over AttemptCompletion: Exempted for delegated subtasks
  AttemptCompletion->>ParentState: Re-fetch parent history
  ParentState-->>AttemptCompletion: parentHistory
  AttemptCompletion->>AttemptCompletion: Verify parent.status=delegated && awaitingChildId=childId?
  alt Parent still awaiting child
    AttemptCompletion->>ClineProvider: reopenParentFromDelegation
    Note over ClineProvider: Check parent status & awaitingChildId (no blacklist)
    ClineProvider-->>AttemptCompletion: success/failure
    alt Reopen succeeded
      ClineProvider-->>ParentState: resume parent
    else Reopen failed
      ClineProvider->>User: log warning, user must resume manually
    end
  else Parent no longer awaiting
    AttemptCompletion->>User: log warning, fall back to user confirmation
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • #457: Directly addresses the root cause of subtask never returning to parent after per-mode model/profile switch by re-validating parent delegation state and removing the blacklist precondition that was rejecting valid returns.
  • #365: Related change to reopenParentFromDelegation precondition logic in the same file, may interact with the blacklist removal and state-check simplification.
  • #366: Related refactoring of reopenParentFromDelegation and parent-delegation state checks in the same location.

Poem

🐰 When subtasks complete their fleeting quest,
They whisper homeward to the nest—
A state recheck, no blacklist's spite,
Now parent and child rejoin just right! 🌙✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: preventing parent task hangs when subtask delegation returns after a per-mode API profile switch.
Description check ✅ Passed The PR description comprehensively covers the problem, root cause, implementation changes, and testing. It follows the template structure with clear sections on Description, Problem, Root Cause, Changes, and Testing.
Linked Issues check ✅ Passed The PR directly addresses issue #457 by implementing both suggested fixes: relaxing guards to accept 'active' status alongside 'delegated' when awaitingChildId matches, removing the cancelledDelegationChildIds blacklist check, and exempting subtask delegation from didToolFailInCurrentTurn. All coding requirements are met.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing issue #457: modifications to reopenParentFromDelegation() guard logic and AttemptCompletionTool delegation handling. No unrelated refactoring or scope creep is evident.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@DScoNOIZ DScoNOIZ closed this Jun 4, 2026
@DScoNOIZ DScoNOIZ force-pushed the fix/issue-457-delegation-return branch from d4995a0 to 2433c85 Compare June 4, 2026 04:08
@DScoNOIZ DScoNOIZ deleted the fix/issue-457-delegation-return branch June 4, 2026 07:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Subtask never returns to parent after a per-mode model/profile switch (attempt_completion silently waits for human)