Skip to content

fix(auth): preserve provider-owned credential pools (LAB-100) - #1262

Closed
eddieparc wants to merge 12 commits into
code-yeongyu:mainfrom
eddieparc:eddieparc/lab-100-claude-provider-resume-regression
Closed

fix(auth): preserve provider-owned credential pools (LAB-100)#1262
eddieparc wants to merge 12 commits into
code-yeongyu:mainfrom
eddieparc:eddieparc/lab-100-claude-provider-resume-regression

Conversation

@eddieparc

@eddieparc eddieparc commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

https://linear.app/jgplabs/issue/LAB-100/omo-claude-sdk-oauth-provider-%EB%AF%B8%EC%84%A4%EC%A0%95-fallback-%EC%8B%A4%ED%8C%A8

Summary

  • preserve complete provider-owned OAuth pools at the generic login persistence boundary
  • prevent synthetic sentinel login-N slots from replacing real provider-added accounts
  • cover resume-projected Claude OAuth auth and provider-owned pooled login persistence

Evidence

  • mutation RED: projected selected account returned undefined; GREEN: 9/9
  • persistence RED: received [default, login-2]; GREEN: 5/5 with [default, second]
  • adjacent Claude OAuth suites: 18/18
  • bun run check: exit 0
  • senpi-qa common 10/10, Anthropic mock loop 22/22, CLI smoke 8/8
  • live global omo: LAB100_OK on opus-5 medium and LAB100_FALLBACK_OK on opus-4.8 medium, both exit 0

No credentials or auth values are included in this PR.


Summary by cubic

Fixes LAB-100 across provider-owned OAuth persistence and Claude SDK session recovery. OAuth logins now retain real accounts, resumed sessions use selected credentials, and compacted sessions can restart without replaying the full context.

Bug Fixes

  • Provider-owned OAuth logins persist complete returned pools and merge overlapping accounts instead of creating unusable login-N sentinels.
  • Claude SDK OAuth availability recognizes a resume-projected top-level credential when no sibling accounts pool exists.
  • Preflight projects the pinned stored account into the credential before streaming.
  • Claude SDK OAuth restart sidecars rebuild from the active session context after the first successful post-compaction assistant turn, accept goal-continuation metadata after the anchor, and persist even when the resident registry entry closes before message_end.
  • Provider-owned This operation was aborted results use the configured turn retry budget before model fallback; user aborts remain terminal.

New Features

  • /fallback restore returns the current session to the model and thinking level active before fallback without changing global defaults.

Written for commit 41aea64. Summary will update on new commits.

Review in cubic

eddieparc and others added 3 commits September 2, 2026 00:53
Ultraworked with [omo](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
Ultraworked with [omo](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/ai/src/auth/pool/slots.ts Outdated
Comment thread packages/ai/src/auth/pool/slots.ts Outdated
Comment thread packages/coding-agent/test/suite/claude-sdk-oauth-extension.test.ts Outdated
Ultraworked with [omo](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
@eddieparc

Copy link
Copy Markdown
Contributor Author

Cubic의 3개 지적을 모두 검증·수정했습니다.

  • overlapping provider-owned logins: deterministic RED에서 first account 유실 → serialized merge GREEN
  • explicit accounts: []: RED에서 synthetic login-2 생성 → empty pool 보존 GREEN
  • misleading multi-login preflight: original single-login title/fixture로 복원

검증: AI focused 7/7, Claude OAuth adjacent 18/18, changed-file LSP clean, root bun run check exit 0. Cubic latest verdict: All reported issues were addressed.

현재 작성자 계정은 upstream READ 권한이라 merge/reviewer 요청 권한이 없습니다. Latest-head CI가 모두 통과하면 maintainer merge 부탁드립니다.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 4 files (changes from recent commits).

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/test/suite/claude-sdk-oauth-extension.test.ts">

<violation number="1">
P3: The preflight test was downgraded from two stored logins to a single one, removing the only multi-login `streamSimple` coverage in this suite. The PR targets projected-selected-account resolution and pool preservation across multiple accounts, so reducing the fixture to one account leaves that path with no regression guard; the prior multi-account assertion was replaced rather than fixed.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Ultraworked with [omo](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
@eddieparc

Copy link
Copy Markdown
Contributor Author

Final head 968d379cf is ready for maintainer merge.

  • all 18 checks passed; 0 pending / 0 failing
  • Cubic final check passed after a mutation-proven pinned second-account projection test was added
  • canonical publish tarball installed locally into omo; nested artifacts contain both fixes
  • live opus-5 and opus-4.8 smokes pass
  • the sole stalled Orca session was relaunched on claude-sdk-oauth/claude-opus-5:medium with literal $mass-ulw; old pre-fix terminal closed

The contributor account has upstream READ permission, and direct merge, auto-merge, reviewer request, and the unauthenticated Orca browser cannot cross that permission boundary. Please merge with a merge commit.

eddieparc and others added 2 commits September 2, 2026 10:12
Ultraworked with [omo](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
Ultraworked with [omo](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
@eddieparc

Copy link
Copy Markdown
Contributor Author

Follow-up runtime recovery fix pushed at 79114336c, rebased by merge commit 471ff050c onto current upstream main.

New evidence:

  • post-compaction restart sidecar regression: RED undefined, GREEN 14/14
  • provider-owned operation abort: initial + 3 retries, then fallback; GREEN 10/10
  • /fallback restore: real session returns to the pre-fallback model and clears fallback state; command/host GREEN 18/18
  • affected focused suite: 67/67
  • root bun run check: PASS
  • full workspace production build: PASS

Root cause of the reported invalid_request: accepted compaction deleted the restart sidecar, but every later assistant refused to recreate it because the old branch hash walker returned no hashes once any compaction entry existed. A restart cold-seeded 484 messages; the aborted seed left a zero-count lineage, and the next options-changed reattach replayed 488 messages before the SDK rejected it.

Please rerun CI/review on head 471ff050c and merge when green.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 14 files (changes from recent commits).

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:2357">
P1: For a provider response whose abort message is exactly `This operation was aborted`, this admission only runs in the asynchronous agent-end processor. The synchronous retry-promise and `willRetry` preflights still reject that message, so callers can observe the turn as settled before the retry starts and extensions receive `willRetry: false`; include the same provider-abort predicate in both preflight paths, preferably through a shared helper.</violation>
</file>

<file name="packages/coding-agent/src/changes.md">

<violation number="1" location="packages/coding-agent/src/changes.md:8">
P3: The changelog says `/fallback restore` "then clears the live fallback state", but restoreFallbackPrimary() restores the model and thinking level without clearing `_retryFallback.activeState`. After the command, getFallbackStatus() still reports the session active and the state survives until a later turn-boundary revert. Either clear the fallback controller state in the restore path or reword the entry.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

// User/system abort provenance still wins and never replays a cancelled turn.
const providerOperationAbort =
msg.stopReason === "aborted" && msg.errorMessage?.trim().toLowerCase() === "this operation was aborted";
const retryableError = this._isRetryableError(msg) || providerOperationAbort;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: For a provider response whose abort message is exactly This operation was aborted, this admission only runs in the asynchronous agent-end processor. The synchronous retry-promise and willRetry preflights still reject that message, so callers can observe the turn as settled before the retry starts and extensions receive willRetry: false; include the same provider-abort predicate in both preflight paths, preferably through a shared helper.

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 2357:

<comment>For a provider response whose abort message is exactly `This operation was aborted`, this admission only runs in the asynchronous agent-end processor. The synchronous retry-promise and `willRetry` preflights still reject that message, so callers can observe the turn as settled before the retry starts and extensions receive `willRetry: false`; include the same provider-abort predicate in both preflight paths, preferably through a shared helper.</comment>

<file context>
@@ -2349,8 +2349,12 @@ export class AgentSession {
+			// User/system abort provenance still wins and never replays a cancelled turn.
+			const providerOperationAbort =
+				msg.stopReason === "aborted" && msg.errorMessage?.trim().toLowerCase() === "this operation was aborted";
+			const retryableError = this._isRetryableError(msg) || providerOperationAbort;
 			const hardErrorFallbackEligible = this._isHardErrorFallbackEligible(msg);
 			const cursorZeroTokenRe = isCursorZeroTokenResourceExhausted(msg);
</file context>

### What changed

- Provider-owned `This operation was aborted` results enter the ordinary turn retry budget, which defaults to three retries, before model fallback. Explicit user aborts remain terminal.
- `/fallback restore` switches only the current session back to the model and thinking level active before fallback, then clears the live fallback state.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The changelog says /fallback restore "then clears the live fallback state", but restoreFallbackPrimary() restores the model and thinking level without clearing _retryFallback.activeState. After the command, getFallbackStatus() still reports the session active and the state survives until a later turn-boundary revert. Either clear the fallback controller state in the restore path or reword the entry.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/coding-agent/src/changes.md, line 8:

<comment>The changelog says `/fallback restore` "then clears the live fallback state", but restoreFallbackPrimary() restores the model and thinking level without clearing `_retryFallback.activeState`. After the command, getFallbackStatus() still reports the session active and the state survives until a later turn-boundary revert. Either clear the fallback controller state in the restore path or reword the entry.</comment>

<file context>
@@ -1,5 +1,27 @@
+### What changed
+
+- Provider-owned `This operation was aborted` results enter the ordinary turn retry budget, which defaults to three retries, before model fallback. Explicit user aborts remain terminal.
+- `/fallback restore` switches only the current session back to the model and thinking level active before fallback, then clears the live fallback state.
+- `ExtensionSessionSettings` exposes the narrow `restoreFallbackPrimary()` action used by the builtin command.
+
</file context>

eddieparc and others added 4 commits September 2, 2026 10:46
Ultraworked with [omo](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
Ultraworked with [omo](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
Ultraworked with [omo](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
Ultraworked with [omo](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
@eddieparc

Copy link
Copy Markdown
Contributor Author

Final head 97b3a846e is ready for maintainer merge.

All 19 required checks passed, including all three coding-agent shards, Changelog gate, Static checks, Cubic, Windows RPC, and GitGuardian.

Live production-surface evidence on the original affected 585-message session:

  • installed global omo engine: Senpi 2026.9.2 from this PR head
  • compacted-session fork returned LAB100_BINDING_FINAL_OK and wrote a fixed-size sidecar (sentCount=395)
  • original session relaunched on claude-sdk-oauth/claude-opus-5:medium and resumed its active mass-ulw goal
  • real target sidecar recreated on opus-5
  • post-anchor goal-continuation suffix now remains admissible while later assistant/unknown state still rejects stale anchors
  • /fallback restore returns a live session to its pre-fallback model/thinking level without changing global defaults
  • recoverable transient failures retain the shipped three-retry turn budget; user/system aborts remain terminal

Direct merge is blocked by branch policy and this account lacks auto-merge permission. Please merge PR #1262.

Ultraworked with [omo](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
@eddieparc

Copy link
Copy Markdown
Contributor Author

Definitive head 41aea6462 is all-green: 19 successful checks, all three coding-agent shards, Changelog gate, Static checks, Cubic, Windows RPC, and GitGuardian.

Final runtime correction beyond 97b3a846e:

  • if a successful SDK turn closes its resident registry entry before host message_end, persistence now uses the current committed continuity binding
  • it refuses stale bindings unless binding.sentCount === activeContextHashes.length
  • post-anchor goal/user delta stays admissible, but later assistants and unknown extension state still reject stale anchors
  • focused final suite 79/79; root bun run check PASS
  • global omo patched to Senpi 2026.9.2 with the darwin-arm64 Claude native binary intact

The original session had already lost its SDK lineage and grew to a deterministic 2.4MB/612-message cold-seed failure. Its durable plan/Boulder/ledger goal has been migrated to a fresh opus-5 session rather than executing copied transcript fragments.

Direct merge remains blocked by branch policy and this account lacks auto-merge permission. Please merge PR #1262.

@eddieparc

Copy link
Copy Markdown
Contributor Author

Superseded by two clean, scoped PRs:

Both replacement branches start from current main, contain no internal tracker identifiers, retain the regression coverage from this branch, and have independent focused-test, static-check, and real-CLI QA evidence.

Closing this mixed PR so the two concerns can be reviewed and merged independently.

@eddieparc eddieparc closed this Sep 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant