fix(agents): safe codex tool-terminal continuation, auth-true simple-completion routes, doctor opt-out#108966
Conversation
…tes, doctor opt-out Three verified codex-cluster fixes: - Tool-call-terminal turns (stopReason=toolUse, no final assistant message) now get one bounded continuation instead of dying with an incomplete-turn error. Completion is proven per tool-call id (every terminal toolCall needs a non-error toolResult in the snapshot) so undispatched or partially dispatched batches keep failing closed; the continuation appends a fresh native-thread turn and never replays completed tools. (#108517) - Simple-completion (narration/titles/labels) resolves auth before finalizing official dual-route OpenAI models and re-materializes the auth-compatible physical route through the runtime-plan seam; api-key credentials are no longer legal for the ChatGPT-backend codex transport (fail closed), ending silent 401s. Custom base URLs and explicit refs stay honored. (#104779) - openclaw doctor --fix no longer flips an explicit plugins.entries.codex enabled:false; explicit user opt-out now warns with a fix hint like the deny-list case, while missing-entry and allowlist repairs stay automatic. (#97180) Fixes #108517 Fixes #104779 Fixes #97180
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d5680f408c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (openAICodexTransportRequiresOAuth(params)) { | ||
| return params.mode === "oauth"; | ||
| } |
There was a problem hiding this comment.
Accept token profiles for Codex subscription routes
When a user has an OpenAI token profile, this check now rejects it for openai-chatgpt-responses even though OpenClaw treats oauth and token as subscription auth (src/agents/provider-model-route-auth.ts:107-109) and the Codex app-server contract explicitly accepts an OpenAI OAuth or token profile for prepared subscription routes (extensions/codex/src/app-server/run-attempt-connection.ts:185-187, covered by extensions/codex/src/app-server/auth-profile-runtime-contract.test.ts:428-456). This breaks users who authenticated with openclaw models auth paste-token --provider openai by failing getApiKeyForModel/simple-completion route materialization before the valid token profile can be handed to Codex; allow mode === "token" here while still rejecting API-key auth.
Useful? React with 👍 / 👎.
… messagesSnapshot, update sibling hints The symmetric codex-transport auth gate now accepts both subscription credential classes (oauth and ChatGPT token) instead of oauth only, so prepared-candidate fallback keeps skipping to a valid subscription profile. attempt-result threads messagesSnapshot into the incomplete-turn attempt shape, and the doctor-lint sibling test expects the manual hint now that an explicit plugins.entries.codex.enabled=false blocks auto-repair.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8269b96d7b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const routeAuthDecision = selectOpenAIModelRouteAuth({ | ||
| resolution: routeResolution, | ||
| sourcePlan: buildProviderModelAuthSourcePlan({ | ||
| ownership: { reason: "provider-binding", source }, | ||
| profiles: [], |
There was a problem hiding this comment.
Respect configured OpenAI auth before locking the route
With models.providers.openai.auth: "api-key" and only an ambient OPENAI_API_KEY plus an automatic ChatGPT/OAuth profile, the earlier resolveApiKeyForProvider call has no modelApi or configured-auth filter, so it can return the OAuth profile; this then wraps that first credential as a required provider-binding, and selectOpenAIModelRouteAuth can no longer apply the configured API-key route the main runtime applies in src/agents/runtime-plan/prepare-auth.ts. Simple completions can therefore keep using the subscription/Codex route despite explicit API-key config, so keep automatic route selection aligned with the runtime plan instead of binding the first unfiltered credential.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7d1d83480d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| (params.attempt.messagesSnapshot ?? []).flatMap((message) => { | ||
| const result = message as { role?: unknown; toolCallId?: unknown; isError?: unknown }; | ||
| return result.role === "toolResult" && | ||
| result.isError !== true && | ||
| typeof result.toolCallId === "string" | ||
| ? [result.toolCallId] |
There was a problem hiding this comment.
Scope tool-result completion to the terminal assistant
Here the completion proof accepts any non-error toolResult in messagesSnapshot, but that snapshot is the full session transcript (attempt-stream-settle.ts snapshots activeSession.messages) rather than just results following the terminal assistant. If a provider/runtime repeats a toolCall id used in an earlier turn, a stale successful result can satisfy completedToolCallIds even when the current terminal toolUse request never ran, so the new continuation prompts the model not to repeat the tool and can skip a side-effecting action. Scope the result lookup to the current assistant's following span, or otherwise verify the result belongs to this terminal turn.
Useful? React with 👍 / 👎.
|
Merged via squash.
|
…completion routes, doctor opt-out (openclaw#108966) * fix(agents): safe codex continuation, auth-true simple-completion routes, doctor opt-out Three verified codex-cluster fixes: - Tool-call-terminal turns (stopReason=toolUse, no final assistant message) now get one bounded continuation instead of dying with an incomplete-turn error. Completion is proven per tool-call id (every terminal toolCall needs a non-error toolResult in the snapshot) so undispatched or partially dispatched batches keep failing closed; the continuation appends a fresh native-thread turn and never replays completed tools. (openclaw#108517) - Simple-completion (narration/titles/labels) resolves auth before finalizing official dual-route OpenAI models and re-materializes the auth-compatible physical route through the runtime-plan seam; api-key credentials are no longer legal for the ChatGPT-backend codex transport (fail closed), ending silent 401s. Custom base URLs and explicit refs stay honored. (openclaw#104779) - openclaw doctor --fix no longer flips an explicit plugins.entries.codex enabled:false; explicit user opt-out now warns with a fix hint like the deny-list case, while missing-entry and allowlist repairs stay automatic. (openclaw#97180) Fixes openclaw#108517 Fixes openclaw#104779 Fixes openclaw#97180 * fix(agents): accept ChatGPT token profiles on codex transport, thread messagesSnapshot, update sibling hints The symmetric codex-transport auth gate now accepts both subscription credential classes (oauth and ChatGPT token) instead of oauth only, so prepared-candidate fallback keeps skipping to a valid subscription profile. attempt-result threads messagesSnapshot into the incomplete-turn attempt shape, and the doctor-lint sibling test expects the manual hint now that an explicit plugins.entries.codex.enabled=false blocks auto-repair. * chore(agents): route test mock casts through unknown, un-export unused doctor helper
What Problem This Solves
Three verified codex-cluster defects on current main:
stopReason=toolUsewith no final assistant message dies with "Agent stopped before producing a final response… some tool actions may have already been executed" and no recovery, even though codex-rs treats that turn as a clean completion (handle_turn_completeemitsturn/completedstatus=completed without requiring a final agent message).openai:api-keyprofile → silent 401s. Upstream contract: codex-rsto_api_provider(auth_mode)derives the endpoint from the auth mode.codexplugin (routing-altering) whenopenaiis enabled #97180 (residual) —openclaw doctor --fixflipped an explicitplugins.entries.codex.enabled: falsetotrue. Deny-list and global disable were respected; an explicit per-entry opt-out was not.Why This Change Was Made
toolCallid in the terminal assistant has a matching non-errortoolResultin the message snapshot. Lifecycle counters are attempt-cumulative and alias across batches, so completion is proven per tool-call id; undispatched or partially dispatched batches keep the existing fail-closed error. The continuation appends a fresh native-thread turn (codexturn/startappends a new user turn; completed tools are never re-sent), soreplaySafedeliberately does not gate it — invariant documented inline.selectOpenAIModelRouteAuth+buildAgentRuntimeAuthPlan+materializePreparedRuntimeModelseam (no parallel resolver, no URL swaps), then re-validates auth against the materialized route.isAuthModeAllowedForModelis now symmetric: the codex transport requires subscription OAuth, so api-key profiles fail closed with a clear error instead of a 401. Custom base URLs and explicit refs are untouched.plugins.entries.codex.enabled: falselike the deny case — warn with a fix hint, never mutate. Missing-entry and restrictive-allowlist repairs stay automatic (the managed-harness contract from Fix doctor repair for disabled Codex runtime plugin #82502 is preserved).User Impact
Evidence
run.incomplete-turn.test.ts(136, incl. new continuation success / exhaustion / never-started / partial-dispatch cases),simple-completion-runtime.test.ts(26: api-key→platform route, oauth→codex route kept, custom baseUrl untouched, explicit ref honored),model-auth.profiles.test.ts(22), doctor route-warnings + core-checks (234+126).800715d201:bespoke_event_handling.rs(clean completion without final message),turn_processor.rs+core/src/session/mod.rs(turn/startappends, never replays),model-provider-info/src/lib.rs(endpoint = f(auth mode)).pnpm tsgo:coregreen on the pre-hardening state; hosted CI validates the final head.Fixes #108517
Fixes #104779
Fixes #97180