Repository navigation
fix: prevent parent task from hanging when subtask delegation returns after per-mode API profile switch - #1
Merged
Conversation
…de-Org#344) * feat(zoo-gateway): add provider types, handler, and model fetcher Co-authored-by: Cursor <[email protected]> * 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 <[email protected]> * fix(types): include zoo-gateway key in requestRouterModels record so RouterName stays exhaustive Co-authored-by: Cursor <[email protected]> * test(router-models): expect zoo-gateway in requestRouterModels responses Co-authored-by: Cursor <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * test(zoo-gateway): align expected OpenAI headers with Zoo Code branding Co-authored-by: Cursor <[email protected]> * fix(zoo-gateway): defer auth check, fail-closed models, reuse Vercel schemas Co-authored-by: Cursor <[email protected]> * fix(zoo-gateway): accept models list without created/description fields Co-authored-by: Cursor <[email protected]> * refactor(zoo-gateway): centralize auth-scoped cache-skip via AUTH_SCOPED_PROVIDERS Co-authored-by: Cursor <[email protected]> * chore(types): drop retired 'roo' provider from getApiProtocol allowlist Co-authored-by: Cursor <[email protected]> --------- Co-authored-by: James Mtendamema <[email protected]> Co-authored-by: Cursor <[email protected]>
…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 <[email protected]>
…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 <[email protected]> Co-authored-by: edelauna <[email protected]>
* feat(zoo-gateway): add provider types, handler, and model fetcher Co-authored-by: Cursor <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * feat(zoo-gateway): add settings UI, validation, and i18n Co-authored-by: Cursor <[email protected]> * 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 <[email protected]> * fix(deps): drop stale webview-ui package.json drift that broke frozen-lockfile installs Co-authored-by: Cursor <[email protected]> * fix(i18n): restore googleCloudCredentialsPathWarning and automaticFetch 'free' search hint dropped on rebase Co-authored-by: Cursor <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * fix(i18n): restore UTF-8 for Google Cloud warning and model picker strings Co-authored-by: Cursor <[email protected]> * fix(zoo-gateway): settings UI sign-in button, validation tests, defer auth Co-authored-by: Cursor <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> --------- Co-authored-by: James Mtendamema <[email protected]> Co-authored-by: Cursor <[email protected]>
…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 <[email protected]> Co-authored-by: Elliott de Launay <[email protected]>
…de-Org#347) * feat(zoo-gateway): auth callback, profile token sync, and sign-out Co-authored-by: Cursor <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * fix(zoo-gateway): address PR review feedback on auth, seeding, and errors Co-authored-by: Cursor <[email protected]> * test(zoo-gateway): cover stale baseUrl seeding path Co-authored-by: Cursor <[email protected]> * fix(zoo-gateway): sign-out clears stale profile tokens, simplify model fetch Co-authored-by: Cursor <[email protected]> * test(zoo-gateway): drop stale profile-scan test for requestRouterModels Co-authored-by: Cursor <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> --------- Co-authored-by: James Mtendamema <[email protected]> Co-authored-by: Cursor <[email protected]>
…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 <[email protected]> * 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 <[email protected]> --------- Co-authored-by: Cursor <[email protected]>
… 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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Fixes Zoo-Code-Org#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
modeApiConfigsprofile 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
handleModeSwitch→activateProviderProfile, which leaves the parent task record asstatus: "active"whileawaitingChildIdstill points at the child. Two guards then reject because they requirestatus === "delegated":reopenParentFromDelegation()— an overly strictcancelledDelegationChildIds.has(childTaskId)guard blocks reopen permanently whencancelTask()fails to detach the parent (race condition inrunDelegationTransition). The child is added to the blacklist, and the secondattempt_completioncall can never recover.AttemptCompletionTooldelegation pre-check — only acceptsstatus === "delegated"for the parent, missing the"active"state that occurs after a per-mode profile switch.Changes
src/core/webview/ClineProvider.tscancelledDelegationChildIdscheck fromreopenParentFromDelegation()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. Thestatus + awaitingChildIdcheck already handles orphaned children.src/core/tools/AttemptCompletionTool.tsparentHistory.status === "delegated"andparentHistory.awaitingChildId === task.taskIdbefore callingdelegateToParent(). If parent detached (cancelled/resumed), skip delegation gracefully.didToolFailInCurrentTurnguard (&& !task.parentTaskId). Subtasks finishing their assigned work should not be blocked by a parent's error flag.historyLookupTaskIdvariable that could point at the wrong task.Testing