Skip to content

Make lazy authentication cancellable and route subagent auth challenges through the interactive host #2562

Description

@acoliver

Objective

Make lazy OAuth a cancellable, host-owned operation. A subagent may discover that authentication is required, but it must never start or render an interactive authentication flow inside its own context. It must wait while the top-level interactive host handles the challenge, then resume or terminate deterministically.

What happened

A Codex account exhausted its quota, so the user logged out and allowed lazy authentication to start:

  1. Lazy auth opened the browser with the wrong existing account/profile—the account whose quota was already exhausted.
  2. Cancelling or abandoning that auth attempt did not settle the pending operation; the LLxprt instance appeared to hang.
  3. Another LLxprt instance attempted a device-code fallback, but the intended second account did not have that flow enabled because it is not used for headless authentication.
  4. Recovery required killing and continuing instances until one authenticated through the intended browser account.
  5. Separately, subagents that encountered missing/expired Codex authentication printed instructions telling the user to enable authentication inside the subagent context and then waited there. That context is not the interactive owner and may not be directly accessible.

Browser/profile association for OAuth buckets is already tracked by #1045. This issue is about deterministic cancellation and ownership of interactive auth, whether lazy auth was triggered by the host or by a subagent.

Expected behavior

Authentication may be required by any provider runtime, but only the top-level interactive host owns authentication UI, browser/device-flow selection, cancellation, and completion.

A subagent that needs authentication should enter a visible waiting-for-auth state and escalate a structured challenge to the host. It must not print user instructions, launch a browser, start device polling, or block on an interaction local to the subagent.

The host should complete one of four terminal outcomes—success, cancelled, failed, or timed out—and deliver it to every attached waiter exactly once. Successful authentication may resume the blocked request through the established retry/recovery policy. Every other result must settle it with a typed, recoverable outcome rather than leave a pending promise, stream, task, listener, or process.

Required architecture and invariants

Host-owned auth coordinator

  • Introduce or designate one session-level coordinator as the owner of interactive authentication.
  • Provider/agent runtimes report an auth challenge containing the provider, configured profile/bucket identity, requesting runtime/task identity, reason, and a correlation ID. Do not send credentials through the event.
  • Propagate subagent challenges through the orchestrator to the owning interactive host.
  • Coalesce equivalent concurrent challenges so one host interaction can serve all matching waiters.
  • Represent waiting explicitly in task/subagent status without adding auth instructions to the subagent transcript.
  • If there is no interactive supervising host, fail fast with an actionable structured error instead of opening an invisible prompt or waiting indefinitely.

Cancellation and cleanup

  • The visible cancel action aborts provider polling, callback listeners/servers, timers, and ephemeral auth-session state.
  • User cancellation of a shared host interaction settles all attached waiters as cancelled while keeping the host, parent agent, and subagents alive.
  • Cancellation of one requesting task detaches only that waiter; it must not abort a shared auth attempt while other live waiters still require it.
  • If no waiters remain, the coordinator cleans up the orphaned attempt.
  • Host shutdown, provider failure, callback failure, and timeout use the same deterministic teardown path.
  • Existing credentials remain unchanged unless replacement authentication succeeds.
  • Cancellation/failure must not silently switch from browser auth to device-code auth or loop back into the same account. The host may explicitly offer retry, another configured bucket/account, or another supported auth method.
  • A cancelled attempt can be retried without restarting LLxprt.
  • Every waiter and resource settles exactly once, including races between success, cancellation, timeout, and shutdown.

Timeout and retry safety

  • Every interactive auth session has a finite, configurable lifetime and performs the same cleanup as explicit cancellation when it expires.
  • Timeout is a backstop, not a substitute for a working cancel action.
  • Authentication success only resumes/retries a request when permitted by the shared request recovery/commit policy in Unify streaming retry, recovery, and failover under one committed request budget #2532; authentication must not cause replay after observable output.

Acceptance criteria

  • Lazy Codex auth can be cancelled from the host UI without hanging, killing, or restarting LLxprt.
  • Cancellation stops callback listeners/device polling/timers and leaves no pending auth operation.
  • Cancellation does not silently fall back to device code or automatically reopen the same browser flow.
  • The user can explicitly retry and select another configured bucket/account or supported auth method; browser-profile discovery/association remains in Bucket / default - Chrome profile association #1045.
  • A subagent auth challenge produces one host-owned auth interaction and an explicit waiting-for-auth subagent status.
  • Subagent output/transcripts never tell the user to authenticate inside the subagent context.
  • Successful host authentication resumes all matching live waiters through the safe retry path.
  • User cancellation, failure, timeout, and host shutdown deliver typed terminal outcomes to all matching waiters exactly once.
  • Cancelling one waiter leaves a shared auth attempt active for other waiters; an orphaned attempt is cleaned up.
  • Concurrent equivalent challenges are coalesced rather than opening competing browser/device flows.
  • A noninteractive runtime with no supervising interactive host fails fast and never waits on hidden UI.
  • Existing credentials are preserved on cancellation/failure and become visible to waiting runtimes after successful replacement auth.
  • Tests cover success/cancel and cancel/success races, timeout, provider failure, host shutdown, waiter detachment, coalescing, noninteractive mode, and no unsafe post-output replay.
  • The ownership and state-transition contract is documented for host, orchestrator, subagent, provider, and UI layers.

Related issues

Appreciable outcome

Lazy auth no longer turns a wrong-account browser launch into a process-restart loop. Authentication has one visible owner, cancel always settles, subagents wait without impersonating the UI, and successful auth resumes the work that actually requested it.

Metadata

Metadata

Assignees

Labels

architectureArchitectural refactor / clean designcodex / GPTrelated to codex auth and GPT model support.oauthoauth related issuessubagentsasync / synch / other types of subagents.

Type

Projects

Status
In Progress

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions