fix(auth): keep Claude OAuth named pools and stop provider hops - #1304
fix(auth): keep Claude OAuth named pools and stop provider hops#1304eddieparc wants to merge 4 commits into
Conversation
Preserve provider-owned account pools on login so a second Claude SDK OAuth account keeps its prompted name and tokens instead of a sentinel login-N slot. Count a rotation-selected concrete OAuth slot as configured, and do not treat Provider is not configured as a hard-error model fallback onto another provider. Ultraworked with [omo](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
There was a problem hiding this comment.
1 issue found across 11 files
You’re at about 99% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/coding-agent/src/core/agent-session.ts">
<violation number="1" location="packages/coding-agent/src/core/agent-session.ts:7381">
P3: This guard couples fallback behavior to the exact English wording of an error message that is generated in two other files (`Provider is not configured: ${model.provider}` at packages/ai/src/models.ts:680 and packages/coding-agent/src/core/model-runtime.ts:711), with no shared constant. If either generator is reworded or localized, the startsWith check silently stops matching and the auth-miss hard-error hop this fix is meant to prevent quietly returns. Export a shared prefix constant (e.g. in packages/ai where ModelsError is defined) and reference it in both throw sites and this guard, as is done with TURN_RETRY_SUPPRESSION_PREFIX.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| private _isHardErrorFallbackEligible(message: AssistantMessage): boolean { | ||
| return ( | ||
| !message.errorMessage?.startsWith(TURN_RETRY_SUPPRESSION_PREFIX) && | ||
| !message.errorMessage?.startsWith("Provider is not configured:") && |
There was a problem hiding this comment.
P3: This guard couples fallback behavior to the exact English wording of an error message that is generated in two other files (Provider is not configured: ${model.provider} at packages/ai/src/models.ts:680 and packages/coding-agent/src/core/model-runtime.ts:711), with no shared constant. If either generator is reworded or localized, the startsWith check silently stops matching and the auth-miss hard-error hop this fix is meant to prevent quietly returns. Export a shared prefix constant (e.g. in packages/ai where ModelsError is defined) and reference it in both throw sites and this guard, as is done with TURN_RETRY_SUPPRESSION_PREFIX.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/coding-agent/src/core/agent-session.ts, line 7381:
<comment>This guard couples fallback behavior to the exact English wording of an error message that is generated in two other files (`Provider is not configured: ${model.provider}` at packages/ai/src/models.ts:680 and packages/coding-agent/src/core/model-runtime.ts:711), with no shared constant. If either generator is reworded or localized, the startsWith check silently stops matching and the auth-miss hard-error hop this fix is meant to prevent quietly returns. Export a shared prefix constant (e.g. in packages/ai where ModelsError is defined) and reference it in both throw sites and this guard, as is done with TURN_RETRY_SUPPRESSION_PREFIX.</comment>
<file context>
@@ -7378,6 +7378,7 @@ export class AgentSession {
private _isHardErrorFallbackEligible(message: AssistantMessage): boolean {
return (
!message.errorMessage?.startsWith(TURN_RETRY_SUPPRESSION_PREFIX) &&
+ !message.errorMessage?.startsWith("Provider is not configured:") &&
message.stopReason === "error" &&
!isContextOverflow(message, this.model?.contextWindow ?? 0) &&
</file context>
Remount the single Input widget when the account-name prompt follows an unsubmitted paste-code field, so a browser-callback login no longer shows two stacked > rows. Ultraworked with [omo](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
There was a problem hiding this comment.
1 issue found across 4 files (changes from recent commits).
You’re at about 99% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/coding-agent/src/modes/interactive/components/login-dialog.ts">
<violation number="1" location="packages/coding-agent/src/modes/interactive/components/login-dialog.ts:85">
P3: The new liveHint dedup only covers showManualInput and showPrompt. showWaiting, showDetails, and showInfo(showCloseHint) still append their own (to cancel)/(to close) hint rows without removing a previously remounted hint, so an interleaved wait/info step leaves a stale hint beside the live one. Route those hint additions through the same remount/tracking so only one hint row is ever live.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| } | ||
|
|
||
| /** The Input widget is a single instance; mounting it twice paints two live `>` rows. */ | ||
| private remountInput(hint: Text): void { |
There was a problem hiding this comment.
P3: The new liveHint dedup only covers showManualInput and showPrompt. showWaiting, showDetails, and showInfo(showCloseHint) still append their own (to cancel)/(to close) hint rows without removing a previously remounted hint, so an interleaved wait/info step leaves a stale hint beside the live one. Route those hint additions through the same remount/tracking so only one hint row is ever live.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/coding-agent/src/modes/interactive/components/login-dialog.ts, line 85:
<comment>The new liveHint dedup only covers showManualInput and showPrompt. showWaiting, showDetails, and showInfo(showCloseHint) still append their own (to cancel)/(to close) hint rows without removing a previously remounted hint, so an interleaved wait/info step leaves a stale hint beside the live one. Route those hint additions through the same remount/tracking so only one hint row is ever live.</comment>
<file context>
@@ -80,6 +81,16 @@ export class LoginDialogComponent extends Container implements Focusable {
}
+ /** The Input widget is a single instance; mounting it twice paints two live `>` rows. */
+ private remountInput(hint: Text): void {
+ this.contentContainer.children = this.contentContainer.children.filter(
+ (child) => child !== this.input && child !== this.liveHint,
</file context>
Classify "Lock file is already being held" as a transient retry and remint the same model instead of hard-error-falling back onto another provider. Ultraworked with [omo](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
After a stream-start timeout the live registry entry is gone, so prompt/toolset drift flattened megabytes of transcript, then invalid_request hard-hopped onto an unauthenticated OpenGateway route. Fork the persisted binding instead, and remint stream stalls plus bare invalid_request on the same model. Ultraworked with [omo](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
A provider whose own login returns the complete pooled credential was double-pooled: appendLoginSlot read that result's top-level fields as a flat credential and appended them as a second slot. For claude-sdk-oauth those fields are the managed sentinel, so a second account produced a login-2 slot holding placeholder tokens. Adopts the slots.ts hunk from #1304 and #1196 by @eddieparc. Ultraworked with [omo](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
…d account Shared credential rotation projects one named slot onto the flat credential shape and strips accounts, so the availability predicate counted zero accounts and reported "Provider is not configured: claude-sdk-oauth". A projected sentinel still counts as zero, leaving the ambient opt-in path unchanged. Adopts the oauth-login.ts hunk from #1304 and #1196 by @eddieparc. Ultraworked with [omo](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
|
#1309 (merged as 10f58c0) adopted the |
|
Thanks @eddieparc — the Claude SDK OAuth named-pool / projected-slot fixes from this PR were adopted on main via #1309 ( Please do not merge this branch onto current main ( Remaining work if this PR stays open:
Until that split/rebase, this stays open and unmerged. |
Summary
login-Nslot copied from the managed sentinel.Provider is not configured: claude-sdk-oauth.opengateway.Root causes
appendLoginSlotalways treated the login result as a flat credential. Claude SDK OAuth already returns the full named pool, so the second account becamelogin-2with sentinel tokens.accountsarray).oauth.checkonly countedaccounts.length, so every request looked unconfigured.AgentSessiontreated thatModelsError("auth")as a hard-error and walked the fallback chain onto another provider that happened to have a key.Test plan
packages/aicredential-pool-resolve-slot.test.ts— named pool preserved; unnamed flat still becomeslogin-Npackages/coding-agentclaude-sdk-oauth-login.test.ts— projected non-sentinel slot passescheck; sentinel does notpackages/coding-agentretry-fallback-hard-error.test.ts—Provider is not configured:does not switch modelsThis is a focused replay of the pool/readiness fixes from #1196 (that branch is far behind
main) plus the hard-error hop guard. No internal tracker ids.Summary by cubic
Fixes Claude SDK OAuth login, retry, session continuity, and interactive login behavior so account selection stays stable and transient failures do not trigger unrelated provider fallbacks.
Bug Fixes
login-Nslot copied from the managed sentinel.invalid_requeston the same model instead of hopping providers.Inputwidget so a browser callback no longer displays two live>prompts.Written for commit 501c106. Summary will update on new commits.