feat(webhook): let a codex session RECEIVE deliveries, not just subscribe - #489
feat(webhook): let a codex session RECEIVE deliveries, not just subscribe#489defangdevs wants to merge 2 commits into
Conversation
…ribe A codex session on this box could manage subscriptions (that is what `agent-box-webhook` is for) but nothing could deliver into one, so its agent polled or learned late — the cost the webhook wiring exists to remove. The missing piece was never in the box: codex cannot host a channel plugin, and its MCP servers are spawned with a scrubbed environment, so nothing on that side could even learn which thread it belongs to. local-webhook 0.26.0 (defangdevs/local-channels#52) closes it from the other end. `subscribe` run INSIDE a codex session starts a small detached peer for that session's own thread — codex exports CODEX_THREAD_ID into its shell tool's environment, which is where the agent's own CLI call runs — and each delivery is handed over with `codex queue`, landing as a message at that session's next turn boundary. Everything before that last inch is the code the claude path already uses, including the peer socket, so a codex session's `--claim` suppresses a standing-watch spawn exactly like a claude session's does. This change is the pin bump plus the guide line, because the box needs nothing else: the peer is started by the session's own CLI call, resolves `codex` from that session's PATH, and stops itself when the last subscription goes. The `webhook.rev` option description records what came with the two intermediate versions (0.24.0's foreign-sender shape, 0.25.0's per-watch spawnConfig), since the pin had been bumped past 0.23.0 without it. The shipped guide's webhook section now tells a codex session that subscribing also wires delivery, and — the one sharp edge — that a subscription seeded from OUTSIDE the session (a spawn wrapper, a script) writes the claim but delivers nothing until the session subscribes once itself. Checks run (aarch64-linux): module-generated-up-to-date, golden-snapshot, one-spec-both-backends, multi-user, module-single-file — all pass; check_backend_parity.py OK, check_one_spec.py PARTIAL (its Nix half is the flake check above). The golden diff is the guide paragraph and nothing else. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PBDHCBttvBEHQJJH24MfxF
|
Warning Review limit reachedNext included review available in 4 minutes. View limit detailsLimit details: You’ve used all 8 included reviews currently available. Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. Review configuration: ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (2)
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (5)
Included review availability: Your plan provides up to 8 included reviews per hour; 0 remain after this review. 📝 WalkthroughWalkthroughThe change updates pinned webhook sources and documents local-webhook version history and Codex session delivery. The documentation covers subscriptions, delivery peers, queued events, peer lifecycle, external subscriptions, inherited subscriptions, and status diagnostics. ChangesWebhook updates
Estimated code review effort: 1 (Trivial) | ~5 minutes Merge Risk: ⚪ Minimal · up to This localized change enables webhook delivery for codex sessions and updates the shipped documentation and generated artifacts; no actionable merge-blocking risk remains beyond normal checks and review. Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Full details: Docstring CoverageExplanation No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0 files. (5 skipped: 5 unsupported.) ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
The shipped guide is embedded by BOTH renderers, and only the web golden had been updated: the native fixture check (tests/test_agentbox.py, run by CI as agentbox-render-ok) failed on etc/agent-box-guides/AGENTS.agent.md. Regenerated with `python3 tests/test_agentbox.py --update`; the diff is the same codex paragraph in AGENTS.agent.md and AGENTS.robot.md and nothing else. Local: tests/test_agentbox.py 77 tests OK (1 skipped); one-spec-both-backends and golden-snapshot pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PBDHCBttvBEHQJJH24MfxF
Problem
A codex session on this box could manage subscriptions — that is what
agent-box-webhookis for — but nothing could deliver into one. Its agentpolled, or learned late: the exact cost the webhook wiring exists to remove.
The missing piece was never in the box. codex cannot host a channel plugin (no
channel notification to receive), and it spawns its MCP servers with a scrubbed
environment, so nothing on that side could even learn which thread it belongs to.
What changed
local-webhook0.26.0 (defangdevs/local-channels#52) closes it from the otherend:
subscriberun inside a codex session starts a small detached peer forthat session's own thread, and each delivery is handed over with
codex queue --thread <id> --message <text>, landing as a message at thatsession's next turn boundary. The thread id comes from
CODEX_THREAD_ID, whichcodex exports into its shell tool's environment — where the agent's own CLI call
runs.
Everything before that last inch is the code the claude path already uses,
peer socket included, so a codex session's
--claimsuppresses a standing-watchspawn exactly like a claude session's does.
So this PR is only:
nix/webhook-pin.nix→ 0.26.0 (789b374), plus awebhook.revdescription paragraph. That description had been left at 0.23.0 while the pin
moved on, so it now also records 0.24.0's foreign-sender shape and 0.25.0's
per-watch
spawnConfig.modules/src/default-agents-webhook.md→ the shipped guide tells a codexsession that subscribing also wires delivery, and names the one sharp edge: a
subscription seeded from outside the session (a spawn wrapper, a script)
writes the claim but delivers nothing until the session subscribes once itself.
modules/agent-box.nixand the golden fixture.Nothing else is needed: the peer is started by the session's own CLI call,
resolves
codexfrom that session's PATH, and stops itself when the lastsubscription goes.
agent-box-webhook statusalready passes local-webhook'sJSON through, so the new
codexThread/codexPeerfields show up there.User-visible and security effects
A codex session now gets webhook deliveries as ordinary queued messages, carrying
the same
[UNTRUSTED webhook:<source>] … ⟪UNTRUSTED:…⟫framing a claude sessionsees; the delivery text is passed as one argv element with no shell involved. No
new listener, no new endpoint, no new sudo grant, no AWS/IAM/networking change.
A claude session is unaffected (asserted upstream).
Checks run (aarch64-linux)
module-generated-up-to-dategolden-snapshotone-spec-both-backendsmulti-usermodule-single-filescripts/check_backend_parity.pyscripts/check_one_spec.pyThe golden diff is the guide paragraph and nothing else.
Upstream verification for the mechanism itself (real codex session, real signed
delivery, 177 tests) is in defangdevs/local-channels#52.