* fix(agents): allow required-preflight native Codex compaction
Required reply-preflight compaction on a Codex app-server-backed session
returns the intentional `ok: true, compacted: false` "codex app-server owns
automatic compaction" no-op because the preflight caller never passes
`allowNonManualNativeRequest`. The reply/preflight path then misclassifies
that successful skip as a failure and throws, dropping the user's turn with
"Context is too large and auto-compaction could not recover this turn." The
equivalent CLI path was fixed by #88207; this is the second, unpatched caller.
Route required-preflight through the existing private
`compactAfterContextEngine` harness capability (which already passes
`allowNonManualNativeRequest: true`) by adding a typed
`nativeCompactionRequest: "required_preflight" | "after_context_engine"`
origin on `maybeCompactAgentHarnessSession` and the Codex compact bridge.
The non-manual skip guard is bypassed for preflight, so Codex actually
compacts the thread.
A binding change between the initial read and the native request is a
stale-binding race, not a benign skip. For `required_preflight` (and the
non-manual CLI path) it now surfaces as the canonical recoverable
`stale_thread_binding` failure so the queued harness falls back to the
context engine instead of treating an uncompacted `ok: true` result as a
completed turn. A genuine post-context-engine request may still skip,
because the context engine has already compacted. Required-preflight is also
the one scoped exception to the model-locked terminal rule: missing or stale
Codex thread bindings recover via the shared context-engine fallback while
the persisted harness lock stays intact; other locked failures remain
terminal.
Rebased onto main after #120740 restructured the guarded native compaction
block; the recoverable-binding semantics are reintroduced on the new
structure and scoped by `nativeCompactionRequest` so #120740's
post-context-engine skip behavior is preserved.
Closes#119971.
* test(evidence): commit inspectable required-preflight live proof scripts for #119971
Adds the two live codex app-server proof scripts (binding-race +
locked-preflight) so the redacted terminal traces in the PR body are
inspectable on the exact head. Both drive the real codex binary and real
maybeCompactCodexAppServerSession with nativeCompactionRequest:
"required_preflight"; neither runs in CI (no codex binary).
* fix(agents): scope locked-preflight compaction fallback to Codex
Restrict the required-preflight model-lock exception to the Codex harness
so missing/stale thread bindings in other locked native harnesses (e.g.
Copilot) stay terminal instead of escaping the persisted model-lock
boundary via context-engine fallback. Add a model-locked Copilot
required-preflight regression covering both missing and stale thread
bindings.
* fix(codex): require native preflight compaction
* chore(plugin-sdk): account for native compaction exports
* test(codex): use complete cron authority fixtures
* chore(lint): shrink compaction assertion baseline
* fix(lint): honor root boundary timeout
* fix(lint): extend package boundary timeout
* fix(plugins): verify native compaction owner
---------
Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
The plugin re-resolved its own enable state with supervision.enabled as
enabledByDefault, so an operator who only declared plugins.entries.codex.config
(auto-enabled by core) silently lost every codex setting, including
appServer.homeScope=user, whenever supervision was absent or off.
Use a live plugin config block as the plugin-side default, matching core's
plugin-tool-configured auto-enable and the onepassword convention. Supervision
stays gated in its own tool surface.
The deleted text provider's usage hook was the only source for the /status
Codex subscription usage line. Harnesses can now contribute an optional
usage snapshot (provider hooks keep priority; only distinct synthetic hook
owners fall through), and the codex harness reports app-server rate limits
via account/rateLimits/read with the same conversion and account identity
as before. No text provider resurrected; deadcode and SDK surface gates
clean.
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
* fix(codex): settle plugin activation, align plugin/list with codex 0.144, bound discovery per turn
- plugin/list curated queries omit cwds and marketplaceKinds (0.144 semantics:
explicit kinds disable the automatic global remote catalog; cwds:[] never
suppressed it), and the missing-marketplace check recognizes the current
openai-curated-remote wire name via the shared predicate (#107305)
- curated plugin/list snapshots settle in a process-local metadata cache
(coalesced, invalidated on install/identity change/restart, 1h freshness
window matching the app-inventory cache) so a missing marketplace or plugin
no longer re-runs blocking discovery RPCs on every embedded-Codex turn;
fail-open local-only responses (upstream warns without a load error when the
remote catalog fetch fails) are never cached, and workspace-directory
queries stay live because external activation has no invalidation signal
- the whole plugin-config build shares one bounded startup deadline with
remaining-budget propagation per RPC and a deny-all apps fallback, so a hung
plugin/list cannot consume the turn (#107305)
- guarded thread requests (start/resume/fork under the native-config fence)
must carry a finite timeout or abort signal, closing the unbounded-fence-hold
window for raw callers (#106719 hardening)
* chore(codex): keep plugin metadata types and deadline builder module-local
Deadline behavior tests exercise the production provider composition instead
of a test-only export.
- session_end with a provably different nextSessionKey (dashboard "New Chat",
fork child) no longer permanently retires the still-live parent Codex
binding; same-key rollovers, unknown-key ends, and terminal ends retire as
before (#106778)
- mirrored session-history reads treat well-formed non-Codex transcripts as an
empty mirror instead of a read failure, so codex harness hooks stop warning
on every non-Codex model run; corrupted session headers stay on the warn
path (#106556)
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.