* fix: surface Codex input prompts across runtimes
Codex structured input now reaches bounded Gateway questions in native and ACP runs, with exact turn ownership, explicit unsupported outcomes, and cancellation fencing. Consume the published ACPX elicitation support.
* chore: align elicitation helpers with current guards
Use protocol-specific helper names required by current main and update the reservation regression to the generalized input owner.
* fix: formalize structured input SDK surface
Expose one documented, frozen agent-harness structured-input contract with runtime and subpath coverage, replacing the accidental function-property API.
* fix: satisfy elicitation architecture gates
Register the real-process ACPX fixture as an executable test root and move shared structured-input types into the boundary leaf to keep Knip and Madge clean.
* fix: remove structured input lint suppression
Preserve the rejected control and invisible-character ranges with an explicit code-point check so the production suppression inventory stays closed.
* fix(codex): preserve model effort capabilities
Keep public model identities separate from app-server execution routing, and retain provider-owned complete effort metadata when account discovery is partial.
Fixes#126005
* refactor(codex): avoid redundant thread rotation
* fix(codex): preserve model fallbacks without leaking wire ids
* fix: distinguish image inspection from delivery
Clarify that image-reading tools only load private model context, prefer native Codex image inspection when available, and preserve explicit outbound tool media across Claude CLI loopback runs.
Co-authored-by: pash <pash@openai.com>
* fix: propagate CLI vision capability
* chore: refresh CI after main fix
---------
Co-authored-by: pash <pash@openai.com>
* feat(gateway): add remote-exec cloud placements
* feat(codex): run cloud turns through remote exec
* fix(sandbox): quote ssh_config path directives containing whitespace
Crabbox lease keys default to ~/Library/Application Support/... on macOS;
unquoted IdentityFile/UserKnownHostsFile/CertificateFile arguments tokenize
on the space and fail as 'extra arguments'. Found via live remote-exec
cloud-worker proof.
* test: consolidate gateway maintenance schedule coverage
* fix(ci): invalidate plugin sdk declarations on state changes
A user-home app-server keeps its native Codex account, and OpenClaw verified only
half of that contract. A subscription route was checked against the native
account, but a Platform (API-key) route was not: an operator signed in to Codex
with a ChatGPT subscription who selected an API-billed model silently spent their
plan. Both directions of the same billing boundary now share one account/read
check. An absent account is left alone, since a native home may serve a custom
model provider that reports no OpenAI account at all.
The home-scope rule behind #114397 was also expressed four times: two
prepared-auth call sites spread it in conditionally from raw plugin config, two
others read resolved start options, and the supervision connection applied its
own default inline. resolveCodexAppServerHomeScope now owns it, and homeScope is
a required argument of resolveCodexAppServerPreparedAuthHandoff, so a new call
site that forgets it fails to compile instead of failing every turn.
Docs dropped the stale user-home paragraph that still described the startup
failure #114397 removed and told operators to delete a working OpenAI profile.
A prepared OpenClaw auth handoff was still resolved for connections that
target the operator's native Codex home, so every turn failed with
"Prepared Codex auth requires an isolated app-server home." once a stored
OpenAI profile produced a prepared model route. User-home mode now keeps
its native account for both subscription and Platform routes, matching the
documented contract; the shared-client guard stays as the backstop.
The live codex text provider was a redundant projection of the openai
catalog (exclusive provider ownership; the openai plugin's ChatGPT OAuth
discovery already serves gpt-5.6-* route-aware). Folding it:
- extensions/codex no longer registers a text provider, catalog entry, or
synthetic text auth; provider.ts/provider-catalog.ts/provider-discovery.ts
and the route-blind model-name heuristics are deleted; the narrow
post-harness reasoning fallback moves to an app-server-owned module
- openai thinking policy keys on explicit selected-route provenance
(api === openai-chatgpt-responses) instead of value-shape inference
- models.list gains an optional additive agentRuntime field (configured
intent); session agentHarnessId remains the execution proof
- doctor --fix migrates the shipped codex/* config shape end to end:
every model slot, provider-config merge with blocker-aware conflict
handling, sessions, cron payloads (two-phase: runtime policy persists
before cron refs rewrite), transcripts; migrated refs carry model-scoped
agentRuntime.id=codex preserving the shipped wizard semantics; auto
runtime policies normalize to codex with sibling fields preserved;
blocked provider conflicts retain the whole legacy namespace fail-closed
with an actionable warning
- the stale openai:default profile cleanup (#91352) was deliberately
deferred to a follow-up after review showed it needs per-agent identity
proofs; doctor keeps warning about unusable profiles
Fixes#105561Fixes#84637Fixes#90420
Move the Codex-specific PreToolUse loop relay switch into the Codex
plugin, preserve policy relays, and cover both normal and side turns.
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: marchpure <marchpure@users.noreply.github.com>
* test(codex): use allowlisted placeholder for auth-bridge api-key fixture
* fix(codex): model-scoped usage-limit blocks, structural 429 classification, no silent API-key billing
- usage-limit blocks written from Codex rate-limit resets are model-scoped via
a persisted blockedScope marker: healthy sibling models on the same auth
profile stay usable; a different/unknown model failing widens the block
profile-wide and never narrows back; legacy rows without the marker stay
profile-wide until they expire (#100556)
- Codex usage-limit failures now surface as status-429 Error objects at both
ingress paths (turn-start and streamed turn failure, wrapped at the event
projector), so core failover classification is structural instead of
matching message wording; profile blocking uses only rate-limit data whose
revision advanced during the turn
- usage-limit detection requires the structured codexErrorInfo signal; the
over-broad "usage limit" substring match that misclassified unrelated
errors as subscription limits is gone (#96815)
- subscription/OAuth routes can no longer silently fall back to env/auth.json
API keys: ambient key fallback is restricted to explicit api-key routes,
native-auth subscription routes verify the account is chatgpt-backed via
account/read, and shared app-server clients are partitioned by auth
requirement so pooled clients cannot cross billing modes (#106375)
- integrated e2e regression: usage-limit promptError -> same-model sibling
profile rotation -> model fallback with reason rate_limit
* chore(codex): keep CodexUsageLimitErrorResult type local
* feat(codex): scope app-server rate limits to the physical client
Replace the process-global rate-limit cache with a WeakMap keyed by the
physical app-server client, tracking per-limitId revisions. Rolling
account/rateLimits/updated notifications merge sparsely per the protocol
contract (credits/individualLimit/planType survive nulls), and
usage-limit errors only trust a snapshot for auth-profile blocking when
the same client observed a primary update during the failing turn's
startup. Fixes cross-client rate-limit bleed in usage-limit error
messages. A new client-runtime module installs the
account/chatgptAuthTokens/refresh handler and the rate-limit observer
once per physical client, replacing per-start inline handlers in
shared-client, run-attempt, and side-question.
* refactor(codex): split thread/resume subscription safety into thread-resume
Move the thread/resume request out of thread-lifecycle into a dedicated
thread-resume module that retires the exact physical client when resume
acceptance is indeterminate: only a structured RPC rejection proves
Codex holds no subscription, so any other failure abandons the client
instead of returning it to the shared pool. Resume responses naming a
different thread now fail closed (assertCodexThreadResumeSubscription),
and the fresh-thread fallback requires a released subscription unless
the resume was a proven RPC rejection.
* refactor(codex): replace client-factory positional DI with shared-client factory
Delete the lazy positional-argument CodexAppServerClientFactory and use
the options-object factory type exported from shared-client. Callers in
run-attempt, compact, bounded-turn, provider-capabilities, and the
web-search provider now default to getLeasedSharedCodexAppServerClient
directly; the lazy indirection was ineffective because those modules
already import shared-client statically.
* feat(codex): route app-server turn traffic through a keyed turn router
Install one turn router per physical app-server client and replace the
broad per-attempt notification/request fanout with explicit per-thread
routes. Attempt startup reserves the thread route (before thread/resume
on the resume path, so early notifications buffer instead of racing),
run-attempt activates it with receive-time, queued, and request
handlers, arms the route before turn/start, binds the accepted turn id
to flush buffered traffic in wire order, and releases the route on
cleanup. Requests for a pending turn wait for binding instead of being
auto-declined, native turn completion waits use route state instead of
scanning buffered notifications, and correlation readers now match the
canonical v2 wire shapes only (top-level threadId, nested turn.id). The
unscoped response-delta lease-count attribution and its client API are
deleted along with the retired correlation predicates.
* test(codex): reset the shared binding store between thread-lifecycle tests
SQLite bindings are keyed by session identity rather than the per-test
temp dir, so earlier tests leaked resumable threads into fresh-start
expectations. The old silent resume-failure fallback masked the leak;
subscription safety surfaces it.
* test(codex): reset the binding store between delivery-hint iterations
The loop reuses one session identity across iterations, so the previous
iteration's thread would resume against a harness that cannot serve it.
* feat: correlate native search outcomes in audit history
Metadata-only audit ledger for agent runs and tool actions in the shared
state DB: stable event identity, closed action/status/error vocabularies,
one-way-hashed tool-call ids, never-inferred terminal outcomes for native
web-search (explicit completed/failed only; otherwise unknown), bounded
retention, audit.list gateway RPC and openclaw audit CLI. Squashed from
the 82-commit audit stack for replay onto current main.
* feat(audit): add audit.enabled config gate (default on)
The metadata-only audit ledger records by default: an audit trail enabled
only after an incident cannot explain the incident, and the rows are
strictly less sensitive than the transcripts every install already
stores. audit.enabled=false stops new writes at the gateway subscription
seam; audit.list and openclaw audit keep serving existing records until
they expire. Documented in the configuration reference, protocol page,
and CLI reference.
* fix: repair full-matrix CI findings after rebase
- break the dynamic-tools/dynamic-tool-execution import cycle by
extracting resolveCodexToolAbortTerminalReason into a leaf module
- restore main's session-worktree protocol exports lost in the
index.ts auto-merge
- register the audit event writer worker as a knip entry point
- docs table formatting; subagent wait-cancellation test scoped to its
audit intent (outcome + timing) and advanced past main's new
lifecycle-timeout retry grace
* feat(openai): add GPT-5.6 series support
* docs: refresh map for GPT-5.6
* fix(openai): preserve GPT-5.6 thinking metadata
* fix(codex): sync managed app server version
* fix(codex): sync managed app server version
* fix(openai): account for GPT-5.6 cache writes
---------
Co-authored-by: Peter Steinberger <steipete@golden-gate.local>
Adds Codex as a selectable hosted web-search provider, routes native Codex search safely across model overrides, and isolates bounded hosted-search workers from configured tools.\n\nVerification: focused post-merge regression suite passed 202/202 tests on exact head 23824af49a.
* fix(reply): deliver final reply when queued follow-up claims session; scope dedupe to routed thread
Two core bugs caused composed replies to be silently dropped (no delivery,
no error) when a second message arrived in the same thread mid-run:
1. dispatch-from-config: ensureDispatchReplyOperation only kept the
dispatch-owned operation authoritative while it had no result. Once
runReplyAgent completed the operation to drain queued follow-ups, a
second same-thread inbound could claim the session and the first final
reply would try to re-acquire the lane instead of finishing delivery,
deadlocking behind the queued work. Keep the dispatch-owned operation
authoritative through final delivery.
2. reply-payloads-dedupe: messaging-tool reply dedupe compared only the
channel target, not the routed thread, so a send in one thread could
suppress a later reply in a different thread. Thread the routed thread
id through buildReplyPayloads + follow-up delivery and only fall back to
channel-only matching for providers without a thread-aware suppression
matcher when neither side carries thread evidence.
Adds regression tests; existing Telegram topic-suppression behavior is
preserved by gating the thread guard to providers lacking a plugin matcher.
* fix(reply): preserve threaded message delivery evidence
* fix(reply): dedupe final payloads by delivery route
* fix(slack): preserve native send thread evidence
* fix(reply): preserve explicit reply thread evidence
* fix(reply): align explicit reply route dedupe
* fix(reply): preserve delivery lane through final dispatch
* fix(mattermost): preserve threaded tool send routes
* chore(plugin-sdk): refresh API baseline
* fix(reply): align final delivery route dedupe
* fix(reply): gate followups on final delivery
* fix(reply): keep send receipts private
* fix(reply): infer implicit message provider
* fix(reply): align routed threading policy
* fix(reply): preserve queued delivery context
* fix(reply): hydrate queued system event routes
* fix(reply): hydrate queued execution routes
* fix(reply): scope final delivery barriers
* fix(slack): preserve DM target aliases
* fix(reply): mirror resolved source thread routes
* fix(mattermost): retain delayed delivery barrier
* fix(codex): separate message routing from tool policy
* fix(reply): consume normalized Slack DM targets once
* fix(slack): remove stale target alias
* style(reply): satisfy changed lint gates
* fix(mattermost): preserve explicit reply targets
* test: align Slack reply branch checks
* fix(reply): persist overflow summaries to admitted session
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Behavior addressed: Native Codex app-server threads now disable Codex's built-in personality on thread/start, thread/resume, turn/start, bound conversation turns, and /btw side-thread forks so OpenClaw agent workspace identity stays authoritative.
Real environment tested: Local OpenClaw source checkout plus GitHub CI on PR #85891.
Exact steps or command run after this patch: pnpm test extensions/codex/src/app-server/thread-lifecycle.test.ts extensions/codex/src/app-server/side-question.test.ts extensions/codex/src/conversation-binding.test.ts extensions/codex/src/app-server/schema-normalization-runtime-contract.test.ts; pnpm check:docs; pnpm prompt:snapshots:check; OPENCLAW_ADDITIONAL_BOUNDARY_SHARD=1/4 OPENCLAW_ADDITIONAL_BOUNDARY_CONCURRENCY=4 node scripts/run-additional-boundary-checks.mjs.
Evidence after fix: Focused Codex test shard passed 4 files / 79 tests; docs check passed; prompt snapshots are current; CI passed all code/quality checks, with only Real behavior proof failing as unrelated proof-bot gating for this non-channel change.
Observed result after fix: App-server request snapshots and unit tests include personality: "none" on native Codex start/resume/turn/fork paths.
What was not tested: A live Codex app-server model run was not executed.
Co-authored-by: Beru <beru@lastguru.lv>