Verify and repair the shell tool for native-default Codex runs#175
Merged
Conversation
… runs Codex CLI >= 0.144.0 (published 2026-07-09; 0.143.0 is the last good version) exposes no shell/exec tool to the model when config.toml uses a custom model_provider and omits the model key — exactly the shape native-default gateway runs write. The model answers, but cannot run commands, read files, or use installed skills, and it was observed fabricating command output instead of reporting the missing tool. run.mjs now pre-verifies native-default runs with a fabrication-proof shell canary (a command_execution item must carry a random nonce), repairs by re-stating the CLI's own resolved default model as an explicit top-level model key in the profile config (prepended — a line appended after a [table] header would join that table), re-verifies, and fails loudly if the tool is still unavailable. The repair is surfaced as an optional modelRepair field on RunnerResult. Verified end to end against the AI Gateway with codex-cli 0.145.0: canary fails -> repair writes model = "openai/gpt-5.6-sol" -> canary passes -> real task runs with a genuine command_execution.
gaojude
approved these changes
Jul 22, 2026
…e canary per sandbox - modelRepair now flows RunnerResult -> AgentRunResult -> EvalRunResult (mirroring observedModel) so persisted results record which runs needed the shell-tool repair; repairs dropping to zero is the workaround's removal signal. - The canary fail-loud path now returns the captured canary CLI output and transcript instead of discarding them, matching the login and real-exec failure paths. - The verified canary outcome is memoized in ~/.codex/agent-eval-canary.json for the sandbox's lifetime: judge assertions re-invoke run.mjs once per assertion and previously would each have paid a canary exec. The marker re-reports the original repairedModel so every invocation carries the same evidence. Verified live against the gateway: first invocation repairs and records modelRepair (14.6s); second invocation skips the canary via the marker (1.7s) and still reports the repair. 279 tests green (2 new for the marker parser).
Contributor
Author
|
Addressed three findings from a multi-agent review pass (f820e33):
Live verification against the gateway: first invocation repairs + records Known residuals (from the review, below reporting threshold): the pre-existing lexicographic session-file fallback could attribute a canary session to a real exec that dies before |
…e fallback channel, tighten canary timeout - results.test.ts/runner.test.ts now pin modelRepair propagation at the same layers where observedModel (its precedent) is tested, so a refactor cannot silently drop the repair evidence that serves as the workaround's removal signal. - The __AGENT_RESULT__ status line and readRunnerResult's fallback reconstruction now carry modelRepair, matching observedModel on that channel (previously the field was lost whenever the result file could not be read back). - Canary spawnSync timeout 180s -> 60s: a single echo round-trip never needs more, and the worst-case pre-task budget drops from 360s to 120s of the 600s default sandbox lifetime. - Cross-reference comments at the orchestrator's sandbox-creation step and eval-helper's header document the canary marker's dependence on the shared per-run sandbox lifetime.
Contributor
Author
|
Second review pass addressed (dd84f36):
280 tests green, build clean. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TLDR is that new versions of codex cli seem to have had a bug in our harness. If we didnt pass an explicit model to it - running as native default, then it couldnt access tools, and even made up output. It could still respond though, and so our research runs have been semi-broken since July 9th.
Problem
Codex CLI >= 0.144.0 (published 2026-07-09; bisected — 0.143.0 is the last good version) exposes no shell/exec tool to the model when
config.tomluses a custommodel_provider(e.g. the AI Gateway) and omits themodelkey — exactly the shape our native-default runs write. The model still answers, so runs "complete", but it cannot run commands, read files, or use installed skills. Worse, it was observed fabricating command output for predictable commands instead of reporting the missing tool (claimedechooutput it never ran; a random-nonce test proved no tool existed).Since agent CLIs are installed unpinned at run time, every native-default gateway Codex task since ~2026-07-09 has run toolless. Downstream impact observed in a0-local production data: codex
usedWebResearchis 0/180 in the two most recent research-baseline runs (gateway codex researches via shell/curl), and plugin-lane codex shards cannot have read their installed skills.The single deciding variable, verified with an isolated
CODEX_HOME: adding an explicitmodel = "<the same model the CLI resolves natively>"to the profile config fully restores the tool.Fix (run.mjs, native-default runs only)
echo <random nonce>. Proof requires acommand_executionitem carrying the nonce — anagent_messagemerely echoing it does NOT count (fabrication-proof).modelkey in~/.codex/default.config.toml(prepended — a line appended after a[table]header would join that table). Same semantics: the CLI chose the model; we only re-state it.modelRepairfield onRunnerResult(evidence, additive).Explicit-model runs already write
modelinto the config and are unaffected (verified).Verification
model = "openai/gpt-5.6-sol"→ canary passes → real task runs with a genuinecommand_executionreading a random nonce file.Cost note
Native-default runs pay one tiny canary exec per task (two on the repair path). Given the fabrication behavior, post-hoc transcript checks cannot substitute for pre-verification.