feat(chat): add provider-scoped OAuth reauth flow#1738
Conversation
📝 WalkthroughWalkthroughThis PR introduces OAuth reauthentication support for Anthropic Claude across the chat service and desktop UI. It adds new authentication infrastructure modules for managing OAuth credentials, token refresh, and retry logic; implements error handling for OAuth expiration in both the UI and agent runtime; and refactors the run-agent flow to integrate provider-aware retry mechanisms with proper session and stream management. Changes
Sequence Diagram(s)sequenceDiagram
participant Agent as Agent Manager
participant OAuth as OAuth Refresh
participant Config as Config
participant Token as Superagent
Agent->>Agent: startup()
Agent->>OAuth: getOrRefreshAnthropicOAuthCredentials()
OAuth->>Config: getCredentialsFromConfig()
Config-->>OAuth: ClaudeOAuthCredentials | null
alt OAuth Credentials Found
OAuth->>OAuth: Check expiration & refresh if needed
OAuth->>Config: saveOAuthCredentialsToConfig()
OAuth-->>Agent: Updated credentials
Agent->>Token: setAnthropicAuthToken(token)
else No OAuth or Refresh Failed
Agent->>Token: setAnthropicAuthToken(null)
Agent->>Agent: Log fallback to legacy credentials
end
sequenceDiagram
participant Client as run-agent
participant Retry as Provider Auth Retry
participant OAuth as OAuth Retry
participant Sync as OAuth Sync
participant Agent as Anthropic Agent
Client->>Retry: runWithProviderAuthRetry(operation, modelId)
Retry->>Retry: resolveModelProvider(modelId)
alt Anthropic Model
Retry->>OAuth: withAnthropicOAuthRetry(operation)
OAuth->>Sync: syncToken() [preflight]
Sync-->>OAuth: "synced" | "reauth-required"
alt Reauth Required at Preflight
OAuth-->>Retry: throw AnthropicOAuthReauthRequiredError
else Reauth Not Required
OAuth->>Agent: operation()
alt Operation Succeeds
Agent-->>OAuth: result
OAuth-->>Retry: result
else Operation Fails with Expired Token
OAuth->>Sync: syncToken({ forceRefresh: true })
Sync-->>OAuth: "synced" | error
alt Refresh Succeeds
OAuth->>Agent: retry operation()
Agent-->>OAuth: result
OAuth-->>Retry: result
else Refresh Fails
OAuth-->>Retry: throw AnthropicOAuthReauthRequiredError
end
end
end
else Other Provider
Retry->>Agent: operation() [direct execution]
Agent-->>Retry: result
end
sequenceDiagram
participant UI as Desktop UI
participant Parser as MessagePartsRenderer
participant OAuth as OAuth Error Handler
participant Error as ChatErrorMessage
Parser->>Parser: process error part
Parser->>OAuth: resolveOAuthReauthErrorUi(part)
alt OAuth Reauth Detected
OAuth-->>Parser: OAuthReauthErrorUi { title, description, actionUrl }
Parser->>Error: ChatErrorMessage({ title, message: description, action: { label, onClick: openUrl } })
Error-->>UI: Rendered error block with action button
else Regular Error
Parser->>Error: ChatErrorMessage({ message: error.text, showIcon: true })
Error-->>UI: Rendered error block with icon
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 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 |
🚀 Preview Deployment🔗 Preview Links
Preview updates automatically with new commits |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/ChatPane/ChatInterface/components/MessagePartsRenderer/oauth-error.ts (1)
20-53: Optional: DRY the OAuth reauth UI payload to avoid string drift.
The same object is duplicated in both branches; a small helper keeps title/description/action consistent.♻️ Suggested refactor
+const buildAnthropicOAuthReauthUi = (): OAuthReauthErrorUi => ({ + kind: "oauth-reauth", + title: "Claude authentication required", + description: + "Your Anthropic OAuth session expired and could not be refreshed. Run `claude auth login` in your terminal, then retry.", + actionLabel: "Open Anthropic Console", + actionUrl: ANTHROPIC_CONSOLE_URL, +}); + export function resolveOAuthReauthErrorUi( part: ErrorPartLike, ): OAuthReauthErrorUi | null { if (part.code === ANTHROPIC_OAUTH_REAUTH_ERROR_CODE) { - return { - kind: "oauth-reauth", - title: "Claude authentication required", - description: - "Your Anthropic OAuth session expired and could not be refreshed. Run `claude auth login` in your terminal, then retry.", - actionLabel: "Open Anthropic Console", - actionUrl: ANTHROPIC_CONSOLE_URL, - }; + return buildAnthropicOAuthReauthUi(); } @@ - return { - kind: "oauth-reauth", - title: "Claude authentication required", - description: - "Your Anthropic OAuth session expired and could not be refreshed. Run `claude auth login` in your terminal, then retry.", - actionLabel: "Open Anthropic Console", - actionUrl: ANTHROPIC_CONSOLE_URL, - }; + return buildAnthropicOAuthReauthUi(); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/ChatPane/ChatInterface/components/MessagePartsRenderer/oauth-error.ts` around lines 20 - 53, The OAuth reauth UI payload is duplicated in resolveOAuthReauthErrorUi; extract the repeated object into a single helper (e.g., a constant or small factory function like oauthReauthUiPayload) and return that helper from both the ANTHROPIC_OAUTH_REAUTH_ERROR_CODE branch and the text-match branch to ensure title/description/actionLabel/actionUrl (use ANTHROPIC_CONSOLE_URL) remain consistent and avoid string drift.packages/chat/src/host/chat-service/agent-manager/stream-watcher/run-agent/run-agent-session.ts (1)
29-32: Consider consolidating session state cleanup inclearSessionStateForFailurefor defensive programming.While abort controllers are properly released via
releaseSessionAbortControllerin finally blocks for all failure paths, consolidating all session state cleanup—includingsessionAbortControllers—intoclearSessionStateForFailureimproves code maintainability and prevents future bugs if the function is reused elsewhere without paired cleanup.♻️ Suggested change
export function clearSessionStateForFailure(sessionId: string): void { sessionRunIds.delete(sessionId); sessionContext.delete(sessionId); + sessionAbortControllers.delete(sessionId); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/chat/src/host/chat-service/agent-manager/stream-watcher/run-agent/run-agent-session.ts` around lines 29 - 32, The function clearSessionStateForFailure currently removes sessionRunIds and sessionContext but omits sessionAbortControllers; update clearSessionStateForFailure to also remove the abort controller entry (i.e., call sessionAbortControllers.delete(sessionId)) so all session state is consolidated there, and then review callers (e.g., sites that call releaseSessionAbortController in finally blocks) to ensure no double-use — you can keep releaseSessionAbortController for invoking abort() but rely on clearSessionStateForFailure for the shared-map cleanup to prevent stale entries.packages/chat/src/host/auth/anthropic/keychain-credentials.ts (1)
10-35: Add a timeout to keychain lookups to prevent potential blocking.
execSynccan hang if the macOS keychain subsystem stalls or prompts unexpectedly. Thetimeoutoption protects against this and ensures the host remains responsive during initialization.♻️ Suggested change (timeout)
const result = execSync( 'security find-generic-password -s "claude-cli" -a "api-key" -w 2>/dev/null', - { encoding: "utf-8" }, + { encoding: "utf-8", timeout: 2000 }, ).trim(); @@ const result = execSync( 'security find-generic-password -s "anthropic-api-key" -w 2>/dev/null', - { encoding: "utf-8" }, + { encoding: "utf-8", timeout: 2000 }, ).trim();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/chat/src/host/auth/anthropic/keychain-credentials.ts` around lines 10 - 35, The two synchronous keychain lookups using execSync in keychain-credentials.ts can hang; add a timeout option to both execSync calls (e.g., include a timeout in the options object such as { encoding: "utf-8", timeout: <ms> }) so the call will throw if the macOS keychain blocks, leaving the existing try/catch behavior to treat it as "not found"; update both occurrences that run 'security find-generic-password -s "claude-cli"...' and 'security find-generic-password -s "anthropic-api-key"...' to include the timeout option.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/chat/src/host/chat-service/agent-manager/agent-manager.ts`:
- Around line 50-70: The fallback logic after
getOrRefreshAnthropicOAuthCredentials() currently ignores non-oauth credentials;
update the branch that handles cliCredentials (from getCredentialsFromConfig()
?? getCredentialsFromKeychain()) to recognize and apply apiKey credentials by
calling setAnthropicAuthToken(...) with the API key and logging a message
indicating the source; keep the existing oauth-warning behavior for kind ===
"oauth" and the ignore-warning for other kinds. Ensure you reference
getOrRefreshAnthropicOAuthCredentials, setAnthropicAuthToken,
getCredentialsFromConfig, getCredentialsFromKeychain, and cliCredentials.kind
when locating and modifying the code.
In
`@packages/chat/src/host/chat-service/agent-manager/stream-watcher/run-agent/run-agent-oauth.ts`:
- Around line 12-37: In syncAnthropicOAuthToken, when
getOrRefreshAnthropicOAuthCredentials returns null do not unconditionally call
setAnthropicAuthToken(null); instead only clear the token and return
"reauth-required" if hasConfiguredOAuthCredentials is true or
options?.forceRefresh is true, otherwise leave the existing token untouched and
return "unavailable"; update the null branch around
getOrRefreshAnthropicOAuthCredentials and keep the catch behavior consistent (it
already conditionally clears the token).
---
Nitpick comments:
In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/ChatPane/ChatInterface/components/MessagePartsRenderer/oauth-error.ts`:
- Around line 20-53: The OAuth reauth UI payload is duplicated in
resolveOAuthReauthErrorUi; extract the repeated object into a single helper
(e.g., a constant or small factory function like oauthReauthUiPayload) and
return that helper from both the ANTHROPIC_OAUTH_REAUTH_ERROR_CODE branch and
the text-match branch to ensure title/description/actionLabel/actionUrl (use
ANTHROPIC_CONSOLE_URL) remain consistent and avoid string drift.
In `@packages/chat/src/host/auth/anthropic/keychain-credentials.ts`:
- Around line 10-35: The two synchronous keychain lookups using execSync in
keychain-credentials.ts can hang; add a timeout option to both execSync calls
(e.g., include a timeout in the options object such as { encoding: "utf-8",
timeout: <ms> }) so the call will throw if the macOS keychain blocks, leaving
the existing try/catch behavior to treat it as "not found"; update both
occurrences that run 'security find-generic-password -s "claude-cli"...' and
'security find-generic-password -s "anthropic-api-key"...' to include the
timeout option.
In
`@packages/chat/src/host/chat-service/agent-manager/stream-watcher/run-agent/run-agent-session.ts`:
- Around line 29-32: The function clearSessionStateForFailure currently removes
sessionRunIds and sessionContext but omits sessionAbortControllers; update
clearSessionStateForFailure to also remove the abort controller entry (i.e.,
call sessionAbortControllers.delete(sessionId)) so all session state is
consolidated there, and then review callers (e.g., sites that call
releaseSessionAbortController in finally blocks) to ensure no double-use — you
can keep releaseSessionAbortController for invoking abort() but rely on
clearSessionStateForFailure for the shared-map cleanup to prevent stale entries.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (31)
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/ChatPane/ChatInterface/components/ChatErrorMessage/ChatErrorMessage.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/ChatPane/ChatInterface/components/ChatErrorMessage/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/ChatPane/ChatInterface/components/ChatInputFooter/ChatInputFooter.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/ChatPane/ChatInterface/components/MessagePartsRenderer/MessagePartsRenderer.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/ChatPane/ChatInterface/components/MessagePartsRenderer/oauth-error.test.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/ChatPane/ChatInterface/components/MessagePartsRenderer/oauth-error.tspackages/agent/src/superagent.tspackages/chat/src/host/auth/anthropic/anthropic.test.tspackages/chat/src/host/auth/anthropic/anthropic.tspackages/chat/src/host/auth/anthropic/config-credentials.tspackages/chat/src/host/auth/anthropic/constants.tspackages/chat/src/host/auth/anthropic/index.tspackages/chat/src/host/auth/anthropic/keychain-credentials.tspackages/chat/src/host/auth/anthropic/oauth-refresh.tspackages/chat/src/host/auth/anthropic/types.tspackages/chat/src/host/chat-service/agent-manager/agent-manager.tspackages/chat/src/host/chat-service/agent-manager/stream-watcher/run-agent/oauth-retry.test.tspackages/chat/src/host/chat-service/agent-manager/stream-watcher/run-agent/oauth-retry.tspackages/chat/src/host/chat-service/agent-manager/stream-watcher/run-agent/provider-auth-retry.test.tspackages/chat/src/host/chat-service/agent-manager/stream-watcher/run-agent/provider-auth-retry.tspackages/chat/src/host/chat-service/agent-manager/stream-watcher/run-agent/run-agent-oauth.test.tspackages/chat/src/host/chat-service/agent-manager/stream-watcher/run-agent/run-agent-oauth.tspackages/chat/src/host/chat-service/agent-manager/stream-watcher/run-agent/run-agent-options.test.tspackages/chat/src/host/chat-service/agent-manager/stream-watcher/run-agent/run-agent-options.tspackages/chat/src/host/chat-service/agent-manager/stream-watcher/run-agent/run-agent-session.tspackages/chat/src/host/chat-service/agent-manager/stream-watcher/run-agent/run-agent-stream.test.tspackages/chat/src/host/chat-service/agent-manager/stream-watcher/run-agent/run-agent-stream.tspackages/chat/src/host/chat-service/agent-manager/stream-watcher/run-agent/run-agent.test.tspackages/chat/src/host/chat-service/agent-manager/stream-watcher/run-agent/run-agent.tspackages/chat/src/session-db/collections/messages/materialize.test.tspackages/chat/src/session-db/collections/messages/materialize.ts
| // Initialize Claude OAuth credentials (auto-refresh if close to expiry). | ||
| const oauthCredentials = await getOrRefreshAnthropicOAuthCredentials(); | ||
| if (oauthCredentials) { | ||
| setAnthropicAuthToken(oauthCredentials.apiKey); | ||
| console.log("[agent-manager] Using Claude OAuth credentials from config"); | ||
| } else { | ||
| setAnthropicAuthToken(null); | ||
|
|
||
| const cliCredentials = | ||
| getCredentialsFromConfig() ?? getCredentialsFromKeychain(); | ||
| if (cliCredentials) { | ||
| if (cliCredentials.kind === "oauth") { | ||
| console.warn( | ||
| "[agent-manager] Claude OAuth token is unavailable or expired and could not be refreshed", | ||
| ); | ||
| } else { | ||
| console.warn( | ||
| `[agent-manager] Ignoring non-OAuth Claude credentials from ${cliCredentials.source}`, | ||
| ); | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# First, let's check where ANTHROPIC_API_KEY is used
echo "=== Searching for ANTHROPIC_API_KEY usage ==="
rg -n -C3 'ANTHROPIC_API_KEY' --type ts --type tsx
echo ""
echo "=== Searching for getCredentialsFromConfig, getCredentialsFromKeychain, and setAnthropicAuthToken ==="
rg -n -C5 'getCredentialsFromConfig|getCredentialsFromKeychain|setAnthropicAuthToken' --type ts --type tsxRepository: superset-sh/superset
Length of output: 285
🏁 Script executed:
#!/bin/bash
# Let's also check the file structure and understand the context better
echo "=== File location and size ==="
wc -l packages/chat/src/host/chat-service/agent-manager/agent-manager.ts
echo ""
echo "=== Check the agent-manager file around lines 50-70 and beyond ==="
sed -n '40,100p' packages/chat/src/host/chat-service/agent-manager/agent-manager.tsRepository: superset-sh/superset
Length of output: 1939
🏁 Script executed:
#!/bin/bash
# Search for where setAnthropicAuthToken might be setting the API key
echo "=== Searching for setAnthropicAuthToken definition and usage ==="
rg -n 'setAnthropicAuthToken|const.*setAnthropicAuthToken' --type ts --type tsx -B2 -A5
echo ""
echo "=== Look for Anthropic client initialization ==="
rg -n 'new Anthropic|Anthropic\(' --type ts --type tsx -B2 -A2Repository: superset-sh/superset
Length of output: 251
🏁 Script executed:
#!/bin/bash
# Search without file type restrictions
echo "=== Searching for ANTHROPIC_API_KEY usage ==="
rg -n 'ANTHROPIC_API_KEY' -C3
echo ""
echo "=== Searching for setAnthropicAuthToken definition ==="
rg -n 'setAnthropicAuthToken\s*=' -C5
echo ""
echo "=== Searching for getCredentialsFromConfig definition ==="
rg -n 'function\s+getCredentialsFromConfig|const\s+getCredentialsFromConfig|export.*getCredentialsFromConfig' -C3Repository: superset-sh/superset
Length of output: 7857
🏁 Script executed:
#!/bin/bash
# Look at the full agent-manager.ts file to understand the complete flow
cat -n packages/chat/src/host/chat-service/agent-manager/agent-manager.tsRepository: superset-sh/superset
Length of output: 6376
🏁 Script executed:
#!/bin/bash
# Search for where cliCredentials.kind === "apiKey" is handled
echo "=== Search for apiKey handling ==="
rg -n 'apiKey|kind.*===.*"apiKey"' -C3
echo ""
echo "=== Search for Anthropic imports and initialization ==="
rg -n 'import.*Anthropic|new Anthropic' -C2Repository: superset-sh/superset
Length of output: 41768
🏁 Script executed:
#!/bin/bash
# Check how setAnthropicAuthToken is implemented and if it handles apiKey
echo "=== Looking for setAnthropicAuthToken implementation in `@superset/agent` ==="
find . -path ./node_modules -prune -o -type f -name "*.ts" -o -name "*.tsx" | grep -v node_modules | xargs grep -l "export.*setAnthropicAuthToken" 2>/dev/null
echo ""
echo "=== Search for where apiKey credentials should be applied ==="
rg -n 'kind.*apiKey|apiKey.*kind' -B3 -A3 packages/chat/src/host/auth/anthropic/Repository: superset-sh/superset
Length of output: 3478
🏁 Script executed:
#!/bin/bash
# Check if there's another credential application path after agent-manager.start()
echo "=== Looking for where cliCredentials.kind === 'apiKey' is handled elsewhere ==="
rg -n 'cliCredentials|\.kind.*===.*["'"'"']apiKey' packages/chat/src/ -B2 -A2Repository: superset-sh/superset
Length of output: 1502
Missing API-key fallback: Non-OAuth credentials are ignored instead of applied.
When OAuth credentials are unavailable, the code logs a warning for any config/keychain credentials but only if their kind is "oauth"—and does nothing for API-key credentials. This breaks the API-key authentication fallback; the agent remains unauthenticated unless process.env.ANTHROPIC_API_KEY is already set.
Add a case to apply API-key credentials:
if (cliCredentials.kind === "apiKey") {
setAnthropicAuthToken(cliCredentials.apiKey);
console.log(`[agent-manager] Using Claude API key from ${cliCredentials.source}`);
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/chat/src/host/chat-service/agent-manager/agent-manager.ts` around
lines 50 - 70, The fallback logic after getOrRefreshAnthropicOAuthCredentials()
currently ignores non-oauth credentials; update the branch that handles
cliCredentials (from getCredentialsFromConfig() ?? getCredentialsFromKeychain())
to recognize and apply apiKey credentials by calling setAnthropicAuthToken(...)
with the API key and logging a message indicating the source; keep the existing
oauth-warning behavior for kind === "oauth" and the ignore-warning for other
kinds. Ensure you reference getOrRefreshAnthropicOAuthCredentials,
setAnthropicAuthToken, getCredentialsFromConfig, getCredentialsFromKeychain, and
cliCredentials.kind when locating and modifying the code.
| export async function syncAnthropicOAuthToken( | ||
| options?: OAuthTokenSyncOptions, | ||
| ): Promise<OAuthTokenSyncResult> { | ||
| const configuredCredentials = getCredentialsFromConfig(); | ||
| const hasConfiguredOAuthCredentials = configuredCredentials?.kind === "oauth"; | ||
|
|
||
| try { | ||
| const oauthCredentials = await getOrRefreshAnthropicOAuthCredentials({ | ||
| forceRefresh: options?.forceRefresh, | ||
| }); | ||
|
|
||
| if (!oauthCredentials) { | ||
| setAnthropicAuthToken(null); | ||
| return hasConfiguredOAuthCredentials ? "reauth-required" : "unavailable"; | ||
| } | ||
|
|
||
| setAnthropicAuthToken(oauthCredentials.apiKey); | ||
| return "synced"; | ||
| } catch (error) { | ||
| console.warn("[run-agent] Failed to sync Anthropic OAuth token:", error); | ||
| if (hasConfiguredOAuthCredentials || options?.forceRefresh) { | ||
| setAnthropicAuthToken(null); | ||
| return "reauth-required"; | ||
| } | ||
| return "unavailable"; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for all usages of setAnthropicAuthToken to understand its scope
rg -n "setAnthropicAuthToken" -g'*.{ts,tsx}' -A 2 -B 2Repository: superset-sh/superset
Length of output: 4507
🏁 Script executed:
#!/bin/bash
# Search for anthropicAuthToken usage to understand token management
rg -n "anthropicAuthToken" -g'*.{ts,tsx}' -A 2 -B 2Repository: superset-sh/superset
Length of output: 1082
🏁 Script executed:
#!/bin/bash
# Look for getOrRefreshAnthropicOAuthCredentials to understand when it returns null
rg -n "getOrRefreshAnthropicOAuthCredentials" -g'*.{ts,tsx}' -A 5 -B 2Repository: superset-sh/superset
Length of output: 20415
🏁 Script executed:
#!/bin/bash
# Check the file structure around the sync function and any related API-key handling
fd "run-agent" -g'*.{ts,tsx}' | head -20Repository: superset-sh/superset
Length of output: 294
🏁 Script executed:
#!/bin/bash
# Check if API-key credentials interact with setAnthropicAuthToken
# Look for patterns where API keys are used alongside setAnthropicAuthToken
rg -n "apiKey|api-key" -g'*.{ts,tsx}' -B 3 -A 3 | grep -A 5 -B 5 "setAnthropicAuthToken"Repository: superset-sh/superset
Length of output: 3368
🏁 Script executed:
#!/bin/bash
# Check the calling context of syncAnthropicOAuthToken
rg -n "syncAnthropicOAuthToken" -g'*.{ts,tsx}' -B 2 -A 5Repository: superset-sh/superset
Length of output: 7024
🏁 Script executed:
#!/bin/bash
# Examine how the function is used in the full test file to understand the expected behavior
cat -n packages/chat/src/host/chat-service/agent-manager/stream-watcher/run-agent/run-agent-oauth.test.ts | head -80Repository: superset-sh/superset
Length of output: 2226
Only clear the token when OAuth is configured or forceRefresh is requested.
The current code unconditionally clears the Anthropic token when getOrRefreshAnthropicOAuthCredentials returns null, even in the "unavailable" case where OAuth was never configured. This is semantically incorrect—token clearing should only happen when OAuth was actually configured (indicating a reauth is needed) or when explicitly forced via forceRefresh.
Suggested adjustment
- if (!oauthCredentials) {
- setAnthropicAuthToken(null);
- return hasConfiguredOAuthCredentials ? "reauth-required" : "unavailable";
- }
+ if (!oauthCredentials) {
+ if (hasConfiguredOAuthCredentials || options?.forceRefresh) {
+ setAnthropicAuthToken(null);
+ }
+ return hasConfiguredOAuthCredentials ? "reauth-required" : "unavailable";
+ }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@packages/chat/src/host/chat-service/agent-manager/stream-watcher/run-agent/run-agent-oauth.ts`
around lines 12 - 37, In syncAnthropicOAuthToken, when
getOrRefreshAnthropicOAuthCredentials returns null do not unconditionally call
setAnthropicAuthToken(null); instead only clear the token and return
"reauth-required" if hasConfiguredOAuthCredentials is true or
options?.forceRefresh is true, otherwise leave the existing token untouched and
return "unavailable"; update the null branch around
getOrRefreshAnthropicOAuthCredentials and keep the catch behavior consistent (it
already conditionally clears the token).
Summary
ChatErrorMessage) with structured-code-first detection and fallback text matchingKey Files
packages/chat/src/host/auth/anthropic/*packages/chat/src/host/chat-service/agent-manager/agent-manager.tspackages/chat/src/host/chat-service/agent-manager/stream-watcher/run-agent/*packages/chat/src/session-db/collections/messages/materialize.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/ChatPane/ChatInterface/components/MessagePartsRenderer/*apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/ChatPane/ChatInterface/components/ChatErrorMessage/*Validation
bun test packages/chat/src/host/auth/anthropic/anthropic.test.ts packages/chat/src/host/chat-service/agent-manager/stream-watcher/run-agent/oauth-retry.test.ts packages/chat/src/host/chat-service/agent-manager/stream-watcher/run-agent/provider-auth-retry.test.ts packages/chat/src/host/chat-service/agent-manager/stream-watcher/run-agent/run-agent-oauth.test.ts packages/chat/src/host/chat-service/agent-manager/stream-watcher/run-agent/run-agent-options.test.ts packages/chat/src/host/chat-service/agent-manager/stream-watcher/run-agent/run-agent-stream.test.ts packages/chat/src/host/chat-service/agent-manager/stream-watcher/run-agent/run-agent.test.ts packages/chat/src/session-db/collections/messages/materialize.test.ts apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/ChatPane/ChatInterface/components/MessagePartsRenderer/oauth-error.test.ts78 pass, 0 failbunx biome checkon touched filesbunx tsc -p packages/chat/tsconfig.json --noEmitNotes
bun.lockchange out of this PR commitapps/desktopfulltsccurrently reports unrelated pre-existing route generation/type issues in this branchSummary by CodeRabbit
Release Notes
New Features
Bug Fixes