Skip to content

feat(codex): expose box system-prompt to Codex via ~/.codex/AGENTS.override.md#119

Merged
madarco merged 3 commits into
nightlyfrom
feat/codex-agents-override
Jun 26, 2026
Merged

feat(codex): expose box system-prompt to Codex via ~/.codex/AGENTS.override.md#119
madarco merged 3 commits into
nightlyfrom
feat/codex-agents-override

Conversation

@madarco

@madarco madarco commented Jun 26, 2026

Copy link
Copy Markdown
Owner

What

The box "system prompt" baked at `/etc/claude-code/CLAUDE.md` (sandbox facts: DinD, per-box git worktree, push/PR/cp via the host relay, identity in `/etc/agentbox/box.env`) previously only benefited Claude. Codex got none of it. This wires the same facts into Codex's global-instructions slot, composed with the user's own Codex global, across all providers.

How

Codex loads a global personal-instructions file from `CODEX_HOME`, first-match of `/.codex/AGENTS.override.md` then `/.codex/AGENTS.md` (verified in `openai/codex` source — pure whole-file concat, no `@import`). At create time we regenerate `~/.codex/AGENTS.override.md` = sentinel line + box facts (read fresh) + the user's own `AGENTS.md`/authored-override folded in beneath.

  • A line-1 sentinel makes it idempotent and prevents folding our own output back in / destroying authored content.
  • The host `~/.codex` is re-synced before each seed (docker), so the user source is restored every create and the fold stays stable.
  • One shared generator `buildCodexAgentsOverrideScript()` drives both paths:
    • docker — `seedCodexAgentsOverride()` seeds the codex-config volume, called after `seedCodexHooks()` (post host rsync).
    • cloud (daytona/hetzner/vercel/e2b) — `ensureCodexAgentsOverride()` runs the same script in-box via `backend.exec`, wired into `cloud-provider.ts`.

Why a generated file (not a symlink or `@import`): Codex's loader is first-match-wins, so a symlink would shadow a user's `AGENTS.md`, and `@AGENTS.md` would reach the model as dead text. Auto-concatenation is the faithful "compose without hand-maintaining a copy."

Verification

  • Real docker box: override is `vscode:vscode 644`, sentinel line 1, body byte-matches `/etc/claude-code/CLAUDE.md`.
  • `codex debug prompt-input` shows the box facts in Codex's model-visible prompt ("You are running inside an AgentBox sandbox…").
  • Compose / authored-override / facts-only / facts-missing-no-op cases all verified; 385 package tests + full lint green.

Known boundary (documented in the docstring): an override hand-authored inside the box's shared volume with no host counterpart isn't a durable surface — the supported source is the host `~/.codex`, re-synced each create.

https://claude.ai/code/session_01PTY4KwAeZdAVvgSWxjpYfs


Note

Low Risk
Create-time, best-effort file generation in agent config only; no auth, relay, or credential path changes.

Overview
Codex now receives the same sandbox “box facts” as Claude by folding /etc/claude-code/CLAUDE.md into ~/.codex/AGENTS.override.md at box create time on Docker and cloud providers.

A shared shell generator (buildCodexAgentsOverrideScript) writes a sentinel line, the box-facts file, then the user’s own AGENTS.md or hand-authored override beneath—because Codex uses first-match-wins for global instructions, not concatenation. Docker seeds via seedCodexAgentsOverride on the codex-config volume after the host ~/.codex rsync; cloud runs the same script in-box through ensureCodexAgentsOverride (with chown to vscode). Failures are best-effort and do not block create.

Docs and Dockerfile.box comments are updated to describe Codex loading this path versus Claude’s env-only discovery today.

Reviewed by Cursor Bugbot for commit f3ff461. Configure here.

…erride.md

The box "system prompt" baked at /etc/claude-code/CLAUDE.md (sandbox facts:
DinD, per-box worktree, push/PR/cp via the host relay, identity in
/etc/agentbox/box.env) previously only reached Claude. Codex got none of it.

Codex loads a global personal-instructions file from CODEX_HOME, first-match
of ~/.codex/AGENTS.override.md then ~/.codex/AGENTS.md (no concat, no @import).
At create time we now regenerate ~/.codex/AGENTS.override.md = sentinel + box
facts (read fresh) + the user's own AGENTS.md / authored override folded in
beneath, so the in-box Codex agent reads the same facts. A line-1 sentinel
makes it idempotent and preserves user content (the host ~/.codex is re-synced
before each seed, restoring the source). No-op when the facts file is absent.

One shared generator (buildCodexAgentsOverrideScript) drives both paths:
- docker: seedCodexAgentsOverride() seeds the codex-config volume, called
  after seedCodexHooks() in create.ts (post host rsync).
- cloud (daytona/hetzner/vercel/e2b): ensureCodexAgentsOverride() runs the same
  script in-box via backend.exec, wired into cloud-provider.ts.

Verified: codex debug prompt-input shows the box facts in Codex's model-visible
prompt; compose + authored-override + facts-only + no-op cases all check out.

Claude-Session: https://claude.ai/code/session_01PTY4KwAeZdAVvgSWxjpYfs
@vercel

vercel Bot commented Jun 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
agentbox-web Skipped Skipped Jun 26, 2026 2:44pm

Request Review

`\nchown vscode:vscode "$OVR" 2>/dev/null || true` +
`\nchmod 0644 "$OVR" 2>/dev/null || true`;
try {
await backend.exec(handle, script);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cloud reads stale box facts

Medium Severity

On cloud create, ensureCodexAgentsOverride runs the shared shell script inside the live sandbox and reads box facts from that filesystem’s /etc/claude-code/CLAUDE.md. Docker seedCodexAgentsOverride reads the same path from the current base image in a throwaway container. After a base-image or checkpoint boot, cloud Codex can keep outdated sandbox facts while Docker picks up updates on the next create.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 94e5515. Configure here.

@madarco

madarco commented Jun 26, 2026

Copy link
Copy Markdown
Owner Author

bugbot run

Comment thread packages/sandbox-cloud/src/codex-agents-override.ts Outdated
Cloud backends signal script failure via a non-zero CloudExecResult.exitCode
rather than throwing, so the prior try/await/log('seeded') reported success even
when the `set -e` script aborted (perms, missing paths) — a box could boot
without box facts while create logs looked healthy. Read the exitCode and log
the failure (still best-effort, never fails create). Found by Cursor Bugbot.

Claude-Session: https://claude.ai/code/session_01PTY4KwAeZdAVvgSWxjpYfs
@madarco

madarco commented Jun 26, 2026

Copy link
Copy Markdown
Owner Author

Thanks Bugbot. Triage:

1. "Cloud seed ignores exec exit" — fixed in f3ff461. Cloud backend.exec returns a non-zero CloudExecResult.exitCode on script failure rather than throwing, so the helper now reads res.exitCode and logs the failure instead of unconditionally reporting success. Still best-effort (never fails create).

2. "Cloud reads stale box facts" — by design, not changing. At create time the box-facts file (/etc/claude-code/CLAUDE.md) exists only inside the box's own filesystem — it's a baked artifact and the host has no copy — so reading the live sandbox is the only source available to the cloud path, and it's exactly what every other in-box tool sees (as fresh as the snapshot). Docker reads from ensureRef only because it seeds the codex-config volume in a throwaway container before the box exists. Re-uploading the facts file from the repo on every cloud create to chase the docker-vs-snapshot freshness asymmetry isn't worth it for content that rarely changes; this matches the existing docker-rebuilds-vs-cloud-snapshots model documented in docs/cloud-create-flow.md.

bugbot run

@madarco

madarco commented Jun 26, 2026

Copy link
Copy Markdown
Owner Author

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit f3ff461. Configure here.

// throwing, so a `set -e` abort (perms, missing paths) must be read off the
// result — otherwise we'd log success while the box booted without facts.
const res = await backend.exec(handle, script);
if (res.exitCode === 0) log('seeded Codex AGENTS.override.md');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cloud logs seed without file

Medium Severity

ensureCodexAgentsOverride logs that Codex AGENTS.override.md was seeded whenever backend.exec returns exit code 0. The shared buildCodexAgentsOverrideScript also exits 0 when /etc/claude-code/CLAUDE.md is absent, without writing the override, so cloud create can report success while Codex never received box facts and any prior override is left unchanged.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f3ff461. Configure here.

…ritten

The shared generator exits 0 in the no-op case (box-facts file absent) without
writing the override, so a bare exitCode===0 check logged a false "seeded" on
the cloud path. Move the success signal into the script as a stdout marker
(CODEX_OVERRIDE_WROTE_MARKER) printed only after the write; both docker and
cloud now key their "seeded" log off the marker, and cloud logs an explicit
"skipped: box-facts file absent" otherwise. Found by Cursor Bugbot.

Claude-Session: https://claude.ai/code/session_01PTY4KwAeZdAVvgSWxjpYfs
@madarco

madarco commented Jun 26, 2026

Copy link
Copy Markdown
Owner Author

"Cloud logs seed without file" — fixed in f642533. The generator now prints a stdout marker (agentbox:codex-override-written) only after it actually writes the override; the early exit 0 (box-facts file absent) skips it. Both docker and cloud key their "seeded" log off the marker, and cloud logs skipped: box-facts file absent on a 0-exit no-op. In practice the file is always present (installed at prepare time on every provider), so this is defensive log accuracy.

The remaining "stale box facts" item is intentional/by-design as explained above (the box's own fs is the only source available to the cloud seed at create time).

bugbot run

@madarco madarco merged commit f642533 into nightly Jun 26, 2026
3 checks passed
@madarco madarco deleted the feat/codex-agents-override branch June 26, 2026 14:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant