Feat/zoo gateway#229
Conversation
…-gateway provider
…users and adjust telemetry retention policies
…oss all provider instances
…oken against existing profile
…MessageHandler tests
…ling and updating session token display
📝 WalkthroughWalkthroughThis PR introduces Zoo Gateway as a new LLM provider, including type system registration, model discovery and caching, API completion handling, Zoo Code authentication with profile management, settings UI integration, and comprehensive multi-language localization. It also broadens telemetry eligibility to all authenticated users (free and paid). ChangesZoo Gateway Provider Integration
Sequence Diagram(s)sequenceDiagram
participant User
participant UI as Settings UI
participant Auth as ZooCode OAuth
participant Provider as ClineProvider
participant API as ZooGatewayHandler
participant Gateway as Zoo Gateway
User->>UI: Select zoo-gateway provider
User->>UI: Click "Sign in to Zoo Code"
UI->>Auth: Open OAuth flow
Auth->>User: OAuth consent screen
User->>Auth: Approve
Auth-->>Provider: Token received
Provider->>Provider: Broadcast to all instances
Provider->>Provider: Seed/update zoo-gateway profiles
Provider-->>UI: Auth state updated
UI->>UI: Show "Authenticated as {{name}}"
User->>API: Request completion with model
API->>Gateway: POST /chat/completions (auth header)
Gateway-->>API: Stream response chunks
API-->>UI: Display streamed text
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/api/providers/fetchers/modelCache.ts (1)
130-147:⚠️ Potential issue | 🟠 Major | ⚡ Quick winCache bypass is incomplete for
zoo-gateway.
getModelsskips cache, butrefreshModelsstill persists models. That reintroduces stale cross-auth-context data for this provider.Suggested fix
export const refreshModels = async (options: GetModelsOptions): Promise<ModelRecord> => { const { provider } = options + const shouldSkipCache = provider === "zoo-gateway" @@ - // Update memory cache first - memoryCache.set(provider, models) - - // Atomically write to disk (safeWriteJson handles atomic writes) - await writeModels(provider, models).catch((err) => - console.error(`[refreshModels] Error writing ${provider} models to disk:`, err), - ) + if (!shouldSkipCache) { + // Update memory cache first + memoryCache.set(provider, models) + + // Atomically write to disk (safeWriteJson handles atomic writes) + await writeModels(provider, models).catch((err) => + console.error(`[refreshModels] Error writing ${provider} models to disk:`, err), + ) + }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/api/providers/fetchers/modelCache.ts` around lines 130 - 147, The zoo-gateway provider still gets written into the shared cache by refreshModels; update refreshModels so it never persists models for provider === "zoo-gateway" (use the existing shouldSkipCache boolean) — i.e., before calling memoryCache.set (or any cache write), check !shouldSkipCache and skip the write for zoo-gateway; ensure any other code paths in refreshModels that call memoryCache.set are similarly guarded.
🧹 Nitpick comments (2)
src/activate/__tests__/handleUri.spec.ts (1)
65-82: ⚡ Quick winAdd a true multi-instance regression test.
This only proves the zero-instance branch. A regression back to updating just the visible provider would still pass because nothing here asserts that every entry from
getAllInstances()receives the callback token.🧪 Suggested test shape
+ const hiddenProvider = { + handleZooCodeCallback: vi.fn(), + } as any + mockGetAllInstances.mockReturnValue([mockVisibleProvider, hiddenProvider]) + + await handleUri({ + path: "/auth-callback", + query: "token=zoo_ext_test_token", + } as any) + + expect(mockVisibleProvider.handleZooCodeCallback).toHaveBeenCalledWith("zoo_ext_test_token") + expect(hiddenProvider.handleZooCodeCallback).toHaveBeenCalledWith("zoo_ext_test_token")As per coding guidelines,
**/{__tests__,tests,test}/**/*.{test,spec}.{ts,tsx,js}: Use package-local unit tests for pure logic, parsing, state transitions, validation, serialization, request construction, retry decisions, and error handling.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/activate/__tests__/handleUri.spec.ts` around lines 65 - 82, The current test only covers the zero-instance path; add a multi-instance regression test for handleUri that ensures every instance returned by getAllInstances receives the callback token. Mock getVisibleInstance to return one visible provider and mock getAllInstances to return an array of two or more provider instances (each with a spy on handleZooCodeCallback), call handleUri with the same auth-callback query, and assert mockHandleZooCodeAuthCallback was called with the token, mockSetZooCodeUserInfo was called with parsed user info, and that each returned instance.handleZooCodeCallback was called with "zoo_ext_test_token" (and optionally that the visible provider was also called).src/core/webview/__tests__/webviewMessageHandler.spec.ts (1)
324-379: ⚡ Quick winExercise the new separate-profile Zoo Gateway lookup.
This only verifies that
zoo-gatewayappears in the aggregated response. The new fallback path never runs here becausemockClineProviderhas noproviderSettingsManager, andmockGetModelsstill succeeds withundefinedcredentials.Add a case with a non-
zoo-gatewayactive config plus mockedproviderSettingsManager.listConfig/getProfile, then assertgetModelsreceives the recoveredapiKeyandbaseUrl. As per coding guidelines,**/{__tests__,tests,test}/**/*.{test,spec}.{ts,tsx,js}: Use package-local unit tests for pure logic, parsing, state transitions, validation, serialization, request construction, retry decisions, and error handling.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/core/webview/__tests__/webviewMessageHandler.spec.ts` around lines 324 - 379, The test doesn’t exercise the new Zoo Gateway separate-profile fallback path; update the spec for webviewMessageHandler to add a case where mockClineProvider has a providerSettingsManager with a non-"zoo-gateway" active config and stub providerSettingsManager.listConfig and providerSettingsManager.getProfile to return a config that maps to a zoo-gateway profile (with apiKey and baseUrl), then invoke webviewMessageHandler and assert mockGetModels was called with an object containing provider: "zoo-gateway" plus the recovered apiKey and baseUrl (and still verify the aggregated routerModels includes "zoo-gateway"); reference webviewMessageHandler, mockClineProvider, mockGetModels, providerSettingsManager.listConfig and providerSettingsManager.getProfile to locate and implement the new assertion.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@PRIVACY.md`:
- Around line 43-48: Update the "Zoo Code Observability (All Authenticated
Users)" section to remove the phrase "unlimited retention" for Pro and higher
users and replace it with a bounded retention policy or an explicit legal basis
and deletion workflow; specifically, change the account-linked telemetry
retention statement to either a fixed maximum retention period (e.g., X
days/months) or state the legal basis plus a documented deletion/archival SLA
and how users can request deletion, and ensure the text mentions the scope (task
ID, provider/model, token counts, estimated cost) and any plan-specific
differences (Free: 7 days; Pro+: specified retention or deletion workflow) so
the privacy claim is not indefinite.
In `@src/api/providers/fetchers/zoo-gateway.ts`:
- Around line 70-72: The axios.get call that discovers models (the
axios.get<ZooGatewayModelsResponse>(`${baseURL}/models`, { headers }) call)
needs an explicit timeout to avoid hanging; add a timeout option (e.g. timeout:
MODEL_DISCOVERY_TIMEOUT_MS) to the request options object, define
MODEL_DISCOVERY_TIMEOUT_MS as a configurable constant or env-backed value
(fallback to a sensible default like 5000 ms), and pass that into the axios.get
call so model discovery will fail fast on network stalls.
- Around line 93-95: The current console.error call serializes the entire error
(JSON.stringify(error, Object.getOwnPropertyNames(error))) which can leak
sensitive headers/bearer tokens; change the logging to avoid dumping the full
error object—log only safe fields such as error.message, error.name, and
error.stack (or error.code) and/or use a sanitizer that strips
request/config/headers before logging. Replace the JSON.stringify usage in the
console.error call (the line that logs "Error fetching Zoo Gateway models...")
with a call that builds a safeError summary (excluding any request, config,
headers, or auth fields) or simply log error.message and a truncated stack to
ensure no bearer tokens are emitted.
In `@src/core/webview/webviewMessageHandler.ts`:
- Around line 2477-2501: The sign-out loop in webviewMessageHandler.ts only
updates the in-memory active Zoo Gateway profile when the persisted profile
still has zooSessionToken, which misses cases where disk is already clean but
the in-memory currentSettings/token is stale; modify the loop so you always
construct a cleanedProfile (e.g., clone profile and delete zooSessionToken) and,
if isThisProfileActive (use isZooGatewayActive and currentApiConfigName ===
entry.name), always call provider.upsertProviderProfile(entry.name,
cleanedProfile, true) to clear the in-memory handler, while retaining the
existing behavior of calling
provider.providerSettingsManager.saveConfig(entry.name, cleanedProfile) when the
persisted profile originally contained a token (or simply always persist if you
prefer); reference symbols: entry, profile, cleanedProfile, isThisProfileActive,
isZooGatewayActive, currentApiConfigName, provider.upsertProviderProfile,
provider.providerSettingsManager.saveConfig.
In `@webview-ui/src/components/settings/ApiOptions.tsx`:
- Around line 709-718: The provider switch to "zoo-gateway" in ApiOptions.tsx
doesn't initialize zooGatewayModelId because "zoo-gateway" is missing from
PROVIDER_MODEL_CONFIG; add a PROVIDER_MODEL_CONFIG entry for "zoo-gateway" that
provides the model field/key used to initialize/set zooGatewayModelId (so
setApiConfigurationField or the same init logic used for other providers runs
when selectedProvider === "zoo-gateway"), update any references in ApiOptions
(and the ZooGateway wrapper) to use that config key, and add a local webview-ui
test covering provider-switch behavior to ensure the dynamic-provider model id
is set on switch.
In `@webview-ui/src/i18n/locales/tr/settings.json`:
- Around line 908-909: The two Turkish strings use informal second-person;
change them to formal phrasing to match existing validation messages: update
"qwenCodeOauthPath" from "Geçerli bir OAuth kimlik bilgileri yolu sağlamalısın"
to a formal form like "Geçerli bir OAuth kimlik bilgileri yolu sağlamalısınız"
and update "zooGatewaySignIn" from "Zoo Gateway'i kullanmak için Zoo Code'a
giriş yapmalısın. Kimlik doğrulamak için 'Giriş Yap'a tıkla." to a formal
equivalent such as "Zoo Gateway'i kullanmak için Zoo Code'a giriş yapmalısınız.
Kimlik doğrulamak için 'Giriş Yap' düğmesine tıklayın." Ensure both keys
("qwenCodeOauthPath" and "zooGatewaySignIn") are replaced accordingly to keep
tone consistent.
---
Outside diff comments:
In `@src/api/providers/fetchers/modelCache.ts`:
- Around line 130-147: The zoo-gateway provider still gets written into the
shared cache by refreshModels; update refreshModels so it never persists models
for provider === "zoo-gateway" (use the existing shouldSkipCache boolean) —
i.e., before calling memoryCache.set (or any cache write), check
!shouldSkipCache and skip the write for zoo-gateway; ensure any other code paths
in refreshModels that call memoryCache.set are similarly guarded.
---
Nitpick comments:
In `@src/activate/__tests__/handleUri.spec.ts`:
- Around line 65-82: The current test only covers the zero-instance path; add a
multi-instance regression test for handleUri that ensures every instance
returned by getAllInstances receives the callback token. Mock getVisibleInstance
to return one visible provider and mock getAllInstances to return an array of
two or more provider instances (each with a spy on handleZooCodeCallback), call
handleUri with the same auth-callback query, and assert
mockHandleZooCodeAuthCallback was called with the token, mockSetZooCodeUserInfo
was called with parsed user info, and that each returned
instance.handleZooCodeCallback was called with "zoo_ext_test_token" (and
optionally that the visible provider was also called).
In `@src/core/webview/__tests__/webviewMessageHandler.spec.ts`:
- Around line 324-379: The test doesn’t exercise the new Zoo Gateway
separate-profile fallback path; update the spec for webviewMessageHandler to add
a case where mockClineProvider has a providerSettingsManager with a
non-"zoo-gateway" active config and stub providerSettingsManager.listConfig and
providerSettingsManager.getProfile to return a config that maps to a zoo-gateway
profile (with apiKey and baseUrl), then invoke webviewMessageHandler and assert
mockGetModels was called with an object containing provider: "zoo-gateway" plus
the recovered apiKey and baseUrl (and still verify the aggregated routerModels
includes "zoo-gateway"); reference webviewMessageHandler, mockClineProvider,
mockGetModels, providerSettingsManager.listConfig and
providerSettingsManager.getProfile to locate and implement the new assertion.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: ed06aec1-7fb4-4521-8e6a-45343efe1826
📒 Files selected for processing (62)
PRIVACY.mdREADME.mdlocales/ca/README.mdlocales/de/README.mdlocales/es/README.mdlocales/fr/README.mdlocales/hi/README.mdlocales/id/README.mdlocales/it/README.mdlocales/ja/README.mdlocales/ko/README.mdlocales/nl/README.mdlocales/pl/README.mdlocales/pt-BR/README.mdlocales/ru/README.mdlocales/tr/README.mdlocales/vi/README.mdlocales/zh-CN/README.mdlocales/zh-TW/README.mdpackages/types/src/provider-settings.tspackages/types/src/providers/index.tspackages/types/src/providers/zoo-gateway.tssrc/activate/__tests__/handleUri.spec.tssrc/activate/handleUri.tssrc/api/index.tssrc/api/providers/fetchers/modelCache.tssrc/api/providers/fetchers/zoo-gateway.tssrc/api/providers/index.tssrc/api/providers/zoo-gateway.tssrc/core/webview/ClineProvider.tssrc/core/webview/__tests__/ClineProvider.spec.tssrc/core/webview/__tests__/webviewMessageHandler.spec.tssrc/core/webview/webviewMessageHandler.tssrc/services/zoo-telemetry.tssrc/shared/api.tswebview-ui/src/components/settings/ApiOptions.tsxwebview-ui/src/components/settings/ModelPicker.tsxwebview-ui/src/components/settings/constants.tswebview-ui/src/components/settings/providers/ZooGateway.tsxwebview-ui/src/components/settings/providers/index.tswebview-ui/src/components/ui/hooks/useSelectedModel.tswebview-ui/src/components/welcome/WelcomeViewProvider.tsxwebview-ui/src/i18n/locales/ca/settings.jsonwebview-ui/src/i18n/locales/de/settings.jsonwebview-ui/src/i18n/locales/en/settings.jsonwebview-ui/src/i18n/locales/es/settings.jsonwebview-ui/src/i18n/locales/fr/settings.jsonwebview-ui/src/i18n/locales/hi/settings.jsonwebview-ui/src/i18n/locales/id/settings.jsonwebview-ui/src/i18n/locales/it/settings.jsonwebview-ui/src/i18n/locales/ja/settings.jsonwebview-ui/src/i18n/locales/ko/settings.jsonwebview-ui/src/i18n/locales/nl/settings.jsonwebview-ui/src/i18n/locales/pl/settings.jsonwebview-ui/src/i18n/locales/pt-BR/settings.jsonwebview-ui/src/i18n/locales/ru/settings.jsonwebview-ui/src/i18n/locales/tr/settings.jsonwebview-ui/src/i18n/locales/vi/settings.jsonwebview-ui/src/i18n/locales/zh-CN/settings.jsonwebview-ui/src/i18n/locales/zh-TW/settings.jsonwebview-ui/src/utils/__tests__/validate.spec.tswebview-ui/src/utils/validate.ts
| - **Zoo Code Observability (All Authenticated Users):** If you sign in to | ||
| Zoo Code, Zoo Code will send LLM usage telemetry to the Zoo Code backend | ||
| (zoocode.dev). This includes task ID, AI provider name, model name, token | ||
| counts (input/output/cache), and estimated cost. This data is linked to your | ||
| authenticated Zoo Code account. Free plan users have their telemetry retained | ||
| for 7 days; Pro and higher plan users have unlimited retention. You can stop |
There was a problem hiding this comment.
Revisit “unlimited retention” for account-linked telemetry.
Stating indefinite retention for authenticated Pro+ users is a compliance risk for data-minimization/retention-limitation requirements. Add a bounded retention policy (or explicit legal basis + deletion workflow/SLAs) in this section.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@PRIVACY.md` around lines 43 - 48, Update the "Zoo Code Observability (All
Authenticated Users)" section to remove the phrase "unlimited retention" for Pro
and higher users and replace it with a bounded retention policy or an explicit
legal basis and deletion workflow; specifically, change the account-linked
telemetry retention statement to either a fixed maximum retention period (e.g.,
X days/months) or state the legal basis plus a documented deletion/archival SLA
and how users can request deletion, and ensure the text mentions the scope (task
ID, provider/model, token counts, estimated cost) and any plan-specific
differences (Free: 7 days; Pro+: specified retention or deletion workflow) so
the privacy claim is not indefinite.
| const response = await axios.get<ZooGatewayModelsResponse>(`${baseURL}/models`, { | ||
| headers, | ||
| }) |
There was a problem hiding this comment.
Add an explicit timeout to model discovery requests.
Without a timeout, this call can hang and stall provider initialization paths.
Suggested fix
const response = await axios.get<ZooGatewayModelsResponse>(`${baseURL}/models`, {
headers,
+ timeout: 15_000,
})📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const response = await axios.get<ZooGatewayModelsResponse>(`${baseURL}/models`, { | |
| headers, | |
| }) | |
| const response = await axios.get<ZooGatewayModelsResponse>(`${baseURL}/models`, { | |
| headers, | |
| timeout: 15_000, | |
| }) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/api/providers/fetchers/zoo-gateway.ts` around lines 70 - 72, The
axios.get call that discovers models (the
axios.get<ZooGatewayModelsResponse>(`${baseURL}/models`, { headers }) call)
needs an explicit timeout to avoid hanging; add a timeout option (e.g. timeout:
MODEL_DISCOVERY_TIMEOUT_MS) to the request options object, define
MODEL_DISCOVERY_TIMEOUT_MS as a configurable constant or env-backed value
(fallback to a sensible default like 5000 ms), and pass that into the axios.get
call so model discovery will fail fast on network stalls.
| console.error( | ||
| `Error fetching Zoo Gateway models: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`, | ||
| ) |
There was a problem hiding this comment.
Don’t serialize full request errors for authenticated calls.
This can leak the bearer token from request headers into logs.
Suggested fix
} catch (error) {
- console.error(
- `Error fetching Zoo Gateway models: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`,
- )
+ const err = error as { message?: string; response?: { status?: number; statusText?: string } }
+ console.error(
+ `Error fetching Zoo Gateway models: status=${err.response?.status ?? "unknown"} ${err.response?.statusText ?? ""} message=${err.message ?? "unknown error"}`,
+ )
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| console.error( | |
| `Error fetching Zoo Gateway models: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`, | |
| ) | |
| const err = error as { message?: string; response?: { status?: number; statusText?: string } } | |
| console.error( | |
| `Error fetching Zoo Gateway models: status=${err.response?.status ?? "unknown"} ${err.response?.statusText ?? ""} message=${err.message ?? "unknown error"}`, | |
| ) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/api/providers/fetchers/zoo-gateway.ts` around lines 93 - 95, The current
console.error call serializes the entire error (JSON.stringify(error,
Object.getOwnPropertyNames(error))) which can leak sensitive headers/bearer
tokens; change the logging to avoid dumping the full error object—log only safe
fields such as error.message, error.name, and error.stack (or error.code) and/or
use a sanitizer that strips request/config/headers before logging. Replace the
JSON.stringify usage in the console.error call (the line that logs "Error
fetching Zoo Gateway models...") with a call that builds a safeError summary
(excluding any request, config, headers, or auth fields) or simply log
error.message and a truncated stack to ensure no bearer tokens are emitted.
| for (const entry of allProfiles) { | ||
| if (entry.apiProvider === "zoo-gateway") { | ||
| const profile = await provider.providerSettingsManager.getProfile({ name: entry.name }) | ||
| if (profile.zooSessionToken) { | ||
| // Clear the token from the profile | ||
| const { zooSessionToken: _removed, ...cleanedProfile } = profile | ||
|
|
||
| // If this is the currently active profile, push to in-memory handler | ||
| // so the current Task's API handler doesn't retain the stale token. | ||
| const isThisProfileActive = isZooGatewayActive && currentApiConfigName === entry.name | ||
|
|
||
| if (isThisProfileActive) { | ||
| // Push cleared profile to in-memory handler | ||
| await provider.upsertProviderProfile(entry.name, cleanedProfile, true) | ||
| provider.log( | ||
| `[zooCodeSignOut] Cleared zooSessionToken from "${entry.name}" profile and updated in-memory handler`, | ||
| ) | ||
| } else { | ||
| // Just persist to disk; this profile is not currently active | ||
| await provider.providerSettingsManager.saveConfig(entry.name, cleanedProfile) | ||
| provider.log( | ||
| `[zooCodeSignOut] Cleared zooSessionToken from "${entry.name}" profile`, | ||
| ) | ||
| } | ||
| } |
There was a problem hiding this comment.
Always clear the active Zoo Gateway profile in memory.
The in-memory upsertProviderProfile(..., true) only runs when the persisted profile still has zooSessionToken. If disk state is already clean but currentSettings still carries the token, sign-out skips the in-memory update and the active handler keeps stale auth.
🧹 Proposed fix
for (const entry of allProfiles) {
if (entry.apiProvider === "zoo-gateway") {
const profile = await provider.providerSettingsManager.getProfile({ name: entry.name })
- if (profile.zooSessionToken) {
- // Clear the token from the profile
- const { zooSessionToken: _removed, ...cleanedProfile } = profile
-
- // If this is the currently active profile, push to in-memory handler
- // so the current Task's API handler doesn't retain the stale token.
- const isThisProfileActive = isZooGatewayActive && currentApiConfigName === entry.name
-
- if (isThisProfileActive) {
- // Push cleared profile to in-memory handler
- await provider.upsertProviderProfile(entry.name, cleanedProfile, true)
- provider.log(
- `[zooCodeSignOut] Cleared zooSessionToken from "${entry.name}" profile and updated in-memory handler`,
- )
- } else {
- // Just persist to disk; this profile is not currently active
- await provider.providerSettingsManager.saveConfig(entry.name, cleanedProfile)
- provider.log(
- `[zooCodeSignOut] Cleared zooSessionToken from "${entry.name}" profile`,
- )
- }
+ const { zooSessionToken: _removed, ...cleanedProfile } = profile
+ const isThisProfileActive = isZooGatewayActive && currentApiConfigName === entry.name
+
+ if (profile.zooSessionToken || isThisProfileActive) {
+ if (isThisProfileActive) {
+ await provider.upsertProviderProfile(entry.name, cleanedProfile, true)
+ provider.log(
+ `[zooCodeSignOut] Cleared zooSessionToken from "${entry.name}" profile and updated in-memory handler`,
+ )
+ } else {
+ await provider.providerSettingsManager.saveConfig(entry.name, cleanedProfile)
+ provider.log(
+ `[zooCodeSignOut] Cleared zooSessionToken from "${entry.name}" profile`,
+ )
+ }
}
}
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/core/webview/webviewMessageHandler.ts` around lines 2477 - 2501, The
sign-out loop in webviewMessageHandler.ts only updates the in-memory active Zoo
Gateway profile when the persisted profile still has zooSessionToken, which
misses cases where disk is already clean but the in-memory currentSettings/token
is stale; modify the loop so you always construct a cleanedProfile (e.g., clone
profile and delete zooSessionToken) and, if isThisProfileActive (use
isZooGatewayActive and currentApiConfigName === entry.name), always call
provider.upsertProviderProfile(entry.name, cleanedProfile, true) to clear the
in-memory handler, while retaining the existing behavior of calling
provider.providerSettingsManager.saveConfig(entry.name, cleanedProfile) when the
persisted profile originally contained a token (or simply always persist if you
prefer); reference symbols: entry, profile, cleanedProfile, isThisProfileActive,
isZooGatewayActive, currentApiConfigName, provider.upsertProviderProfile,
provider.providerSettingsManager.saveConfig.
| {selectedProvider === "zoo-gateway" && ( | ||
| <ZooGateway | ||
| apiConfiguration={apiConfiguration} | ||
| setApiConfigurationField={setApiConfigurationField} | ||
| routerModels={routerModels} | ||
| organizationAllowList={organizationAllowList} | ||
| modelValidationError={modelValidationError} | ||
| simplifySettings={fromWelcomeView} | ||
| /> | ||
| )} |
There was a problem hiding this comment.
Add provider-switch model initialization for Zoo Gateway.
When apiProvider is switched to "zoo-gateway" (Line 709), zooGatewayModelId is never initialized because "zoo-gateway" is missing from PROVIDER_MODEL_CONFIG. This can leave config in an invalid state and block completion flows that require a dynamic-provider model id.
Proposed fix
@@
vercelAiGatewayDefaultModelId,
+ zooGatewayDefaultModelId,
minimaxDefaultModelId,
@@
"vercel-ai-gateway": { field: "vercelAiGatewayModelId", default: vercelAiGatewayDefaultModelId },
+ "zoo-gateway": { field: "zooGatewayModelId", default: zooGatewayDefaultModelId },
openai: { field: "openAiModelId" },🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@webview-ui/src/components/settings/ApiOptions.tsx` around lines 709 - 718,
The provider switch to "zoo-gateway" in ApiOptions.tsx doesn't initialize
zooGatewayModelId because "zoo-gateway" is missing from PROVIDER_MODEL_CONFIG;
add a PROVIDER_MODEL_CONFIG entry for "zoo-gateway" that provides the model
field/key used to initialize/set zooGatewayModelId (so setApiConfigurationField
or the same init logic used for other providers runs when selectedProvider ===
"zoo-gateway"), update any references in ApiOptions (and the ZooGateway wrapper)
to use that config key, and add a local webview-ui test covering provider-switch
behavior to ensure the dynamic-provider model id is set on switch.
| "qwenCodeOauthPath": "Geçerli bir OAuth kimlik bilgileri yolu sağlamalısın", | ||
| "zooGatewaySignIn": "Zoo Gateway'i kullanmak için Zoo Code'a giriş yapmalısın. Kimlik doğrulamak için 'Giriş Yap'a tıkla." |
There was a problem hiding this comment.
Use consistent formal tone in Turkish validation copy.
Line 908 and Line 909 switch to informal second-person wording, while nearby validation messages use formal phrasing. Please align these two strings to keep UX tone consistent.
✍️ Suggested wording
- "qwenCodeOauthPath": "Geçerli bir OAuth kimlik bilgileri yolu sağlamalısın",
- "zooGatewaySignIn": "Zoo Gateway'i kullanmak için Zoo Code'a giriş yapmalısın. Kimlik doğrulamak için 'Giriş Yap'a tıkla."
+ "qwenCodeOauthPath": "Geçerli bir OAuth kimlik bilgileri yolu sağlamalısınız",
+ "zooGatewaySignIn": "Zoo Gateway'i kullanmak için Zoo Code'a giriş yapmalısınız. Kimlik doğrulamak için 'Giriş Yap'a tıklayın."📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "qwenCodeOauthPath": "Geçerli bir OAuth kimlik bilgileri yolu sağlamalısın", | |
| "zooGatewaySignIn": "Zoo Gateway'i kullanmak için Zoo Code'a giriş yapmalısın. Kimlik doğrulamak için 'Giriş Yap'a tıkla." | |
| "qwenCodeOauthPath": "Geçerli bir OAuth kimlik bilgileri yolu sağlamalısınız", | |
| "zooGatewaySignIn": "Zoo Gateway'i kullanmak için Zoo Code'a giriş yapmalısınız. Kimlik doğrulamak için 'Giriş Yap'a tıklayın." |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@webview-ui/src/i18n/locales/tr/settings.json` around lines 908 - 909, The two
Turkish strings use informal second-person; change them to formal phrasing to
match existing validation messages: update "qwenCodeOauthPath" from "Geçerli bir
OAuth kimlik bilgileri yolu sağlamalısın" to a formal form like "Geçerli bir
OAuth kimlik bilgileri yolu sağlamalısınız" and update "zooGatewaySignIn" from
"Zoo Gateway'i kullanmak için Zoo Code'a giriş yapmalısın. Kimlik doğrulamak
için 'Giriş Yap'a tıkla." to a formal equivalent such as "Zoo Gateway'i
kullanmak için Zoo Code'a giriş yapmalısınız. Kimlik doğrulamak için 'Giriş Yap'
düğmesine tıklayın." Ensure both keys ("qwenCodeOauthPath" and
"zooGatewaySignIn") are replaced accordingly to keep tone consistent.
Pull Request: Zoo Gateway Provider Integration
Description
This PR introduces Zoo Gateway as a new provider option in Zoo Code, enabling users to access AI models through the Zoo Code platform's unified API gateway.
Key Implementation Details
1. New Provider Architecture
src/api/providers/zoo-gateway.ts— Full provider handler extendingRouterProviderwith OpenAI-compatible API, prompt caching support for Claude models, and enrichment headers for request tracking (X-Zoo-Task-ID,X-Zoo-Mode,X-Zoo-Extension-Version,X-Zoo-Editor)src/api/providers/fetchers/zoo-gateway.ts— Model fetcher that retrieves available models from the Zoo Gateway/modelsendpoint, reusing Vercel AI Gateway's Zod schemas and parsing logic since API formats are identicalpackages/types/src/providers/zoo-gateway.ts— Type definitions withclaude-sonnet-4as default model, shared prompt caching model list with Vercel AI Gateway2. Authentication Flow
Authentication uses the existing Zoo Code OAuth flow (via
/auth-callbackURI handler):token,name,email, andimagehandleUri.tsprocesses callback across all ClineProvider instances (not just visible ones) to ensure token persistence even when sidebar is hiddenzooSessionTokenin zoo-gateway provider profiles3. Multi-Profile Token Synchronization
A significant design decision was made to handle profile management robustly:
ClineProvider.handleZooCodeCallback()scans all profiles withapiProvider === "zoo-gateway"and updates tokens in each (profile names are user-renameable)ensureZooGatewayProfileSeeded()handles migration for existing users who authenticated before Zoo Gateway was added — creates a profile or updates stale tokens on webview initwebviewMessageHandler.tsclearszooSessionTokenfrom all zoo-gateway profiles, not just the active one4. Base URL Environment Handling
The gateway base URL is derived dynamically from
ZOO_CODE_BASE_URLenvironment variable:https://www.zoocode.dev/api/gateway/v15. Settings UI
webview-ui/src/components/settings/providers/ZooGateway.tsx:validation.zooGatewaySignIn)6. Validation Changes
webview-ui/src/utils/validate.ts:zooCodeIsAuthenticatedstateTrade-offs & Design Decisions
Token in all profiles vs. single profile: Chose to update all zoo-gateway profiles rather than just one canonical profile. This prevents stale tokens in renamed/duplicate profiles from causing auth failures when model fetcher does
.find()and hits the wrong profile first.Fire-and-forget profile seeding:
ensureZooGatewayProfileSeeded()runs on webview init without blocking. If it fails, users can still re-authenticate manually.No auto-switch on sign-in: When user signs in, we update zoo-gateway profiles but do NOT switch the active provider. Users must explicitly select Zoo Gateway to avoid disrupting mid-conversation flows.
Test Procedure
Unit Tests Updated:
src/activate/__tests__/handleUri.spec.ts— AddedgetAllInstancesmock, verified multi-instance token propagationsrc/core/webview/__tests__/ClineProvider.spec.ts— Added zoo-gateway to mock modelssrc/core/webview/__tests__/webviewMessageHandler.spec.ts— Added zoo-gateway to mock modelswebview-ui/src/utils/__tests__/validate.spec.ts— Added zoo-gateway caseManual Testing Steps:
New User Flow:
Existing User Migration:
Multi-Profile Scenarios:
Sign-Out Flow:
Documentation Updates
Summary by CodeRabbit
Release Notes
New Features
Documentation