Skip to main content
Status: draft · Version 0.1 · Filed 2026-04-26

Title: Codex App-Server Signal Injection — local receiver bridge for Prism signals

Version

0.1

Status

draft

Supersedes

SPEC-047

Note

This spec replaces Texi’s earlier SPEC-047 draft after Donna reported a spec-number collision. SPEC-047 is marked rejected/superseded; SPEC-048 is the canonical implementation target.

Problem

Codex sessions currently receive Prism signals through piggyback delivery: a signal becomes visible only when the receiving Codex agent calls another Prism verb or explicitly polls prism_signals_pending. The operator goal is stronger: if Desiree sends Texi a signal, Texi should see it in the Codex session without manual polling. Claude Code has channel-based push. Codex does not have the same channel API. OpenAI Codex provides an experimental local app-server with JSON-RPC methods including thread/inject_items, which can inject items into a loaded Codex thread. That makes app-server the local Codex UI adapter, not Prism’s LAN/global signal router.

Goal

Implement fallback-safe Codex signal push: Sender MCP -> Prism FastAPI -> Receiver MCP -> local Codex app-server -> Codex thread Prism FastAPI remains the global authenticated signal router. The receiver MCP process bridges from Prism’s session stream to the local Codex app-server.

Non-Goals

  • Do not reimplement Codex app-server in Prism FastAPI.
  • Do not expose Codex app-server directly on LAN in v1.
  • Do not route Prism FastAPI directly to another machine’s 127.0.0.1:<port>.
  • Do not replace piggyback delivery; it remains the reliability fallback.
  • Do not create a general chat proxy between Codex and Prism.

Communication Paths

Texi -> Desiree

  1. Texi calls prism_signal(to="Desiree", ...).
  2. Texi MCP sends to Prism FastAPI.
  3. Prism FastAPI resolves Desiree. If offline, it queues the signal.
  4. Desiree receives on next bootstrap or Prism verb according to her surface strategy.

Desiree -> Texi Today

  1. Desiree calls prism_signal(to="Texi", ...).
  2. Desiree MCP sends to Prism FastAPI.
  3. Prism resolves Texi’s active session.
  4. Codex has no push adapter yet, so Texi sees it through piggyback/poll only.

Desiree -> Texi Target v1

  1. Desiree calls prism_signal(to="Texi", ...).
  2. Desiree MCP sends to Prism FastAPI.
  3. Prism FastAPI resolves Texi and publishes onto the existing Prism session stream.
  4. Texi’s local MCP process receives the signal.
  5. Texi MCP sees PRISM_AGENT_SURFACE=codex and PRISM_CODEX_APP_SERVER_URL.
  6. AppServerInjectStrategy calls local Codex app-server thread/inject_items.
  7. Codex app-server injects a model-visible item into Texi’s loaded Codex thread.
  8. If injection fails, the strategy falls back to piggyback buffering.

Launcher Contract

coder -agent codex gains optional app-server management. Flags:
  • -codex-app-server off|local|auto default: auto
  • -codex-app-server-url URL explicit override for advanced/debug use
  • Optional future: -codex-app-server-port PORT
Modes:
  • off: never start or use Codex app-server; piggyback only.
  • local: require a local app-server path; fail or clearly warn if unavailable.
  • auto: try to start/discover local app-server; fall back to piggyback if unavailable.
For v1, app-server listens on loopback only:
codex app-server --listen ws://127.0.0.1:<port>
codex --remote ws://127.0.0.1:<port> --cd <project> ...
The launcher exports:
PRISM_CODEX_APP_SERVER_MODE=auto|local|off
PRISM_CODEX_APP_SERVER_URL=ws://127.0.0.1:<port>
Port policy:
  • Prefer a deterministic local port range for operator/debug visibility.
  • If occupied, reuse only when verified as the matching app-server/session, otherwise increment to the next free port.
  • Port collision must not break auto; it falls back or selects another port.

MCP Surface Contract

mcp/agent_surfaces/codex.py owns Codex bootstrap/wrap behavior. CodexSurface.on_bootstrap(ctx):
  • Reads PRISM_CODEX_APP_SERVER_MODE and PRISM_CODEX_APP_SERVER_URL.
  • Connects only when mode is not off and a URL is present/discoverable.
  • Discovers/records the active loaded thread id if required.
  • Stores connection/thread metadata on the surface instance.
  • Is non-fatal on failure; logs and stays piggyback.
CodexSurface.signal_strategy():
  • Returns AppServerInjectStrategy when app-server connection and thread metadata are available.
  • Otherwise returns PiggybackStrategy.
CodexSurface.on_wrap(ctx):
  • Closes any app-server client handle.
  • Clears local thread metadata.
  • Does not kill an app-server it did not start.

Strategy Contract

Add mcp/strategies/app_server_inject.py. AppServerInjectStrategy.deliver(signal):
  • Converts Prism signal envelope into a concise Codex-visible item.
  • Calls Codex app-server thread/inject_items against the active thread.
  • Returns True on successful injection.
  • On connection failure, no loaded thread, protocol rejection, or timeout: buffers via piggyback fallback and returns False.
Injected item must identify:
  • source: Prism
  • signal type
  • sender identity
  • signal id
  • concise payload summary
  • instruction to call prism_signals_pending only if full structured payload is needed

Backend/FastAPI Contract

Prism FastAPI remains the global router. v1 does not require FastAPI to talk directly to Codex app-server. Optional metadata registration may be added later for observability, but signal delivery v1 should not depend on FastAPI-initiated app-server calls.

Install Contract

Install changes are capability checks only:
  • Verify codex binary exists when installing Codex support.
  • Verify codex app-server --help includes --listen before advertising app-server injection support.
  • Continue writing ~/.codex/config.toml MCP config.
  • Continue installing coder launcher.
  • Do not start app-server during install.
  • Do not reserve ports during install.
Runtime launcher owns app-server start/discovery.

Security

  • v1 uses loopback only: ws://127.0.0.1:<port>.
  • No LAN exposure of Codex app-server in v1.
  • Future LAN mode requires explicit operator opt-in and WebSocket auth.
  • Prism API key is never passed as a URL query parameter.
  • App-server auth tokens/secrets, if used, are local-only and not persisted into Prism logs.

Acceptance Tests

Launcher:
  1. coder -agent codex -codex-app-server off -dry-run shows no --remote and exports mode off.
  2. coder -agent codex -codex-app-server auto -dry-run shows app-server discovery/start plan and fallback behavior.
  3. Fake codex smoke verifies child process receives PRISM_CODEX_APP_SERVER_URL when enabled.
  4. Existing Codex CLI mappings remain green: --cd, --sandbox, --ask-for-approval, --full-auto.
MCP surface:
  1. PRISM_AGENT_SURFACE=codex, no app-server URL -> PiggybackStrategy.
  2. PRISM_AGENT_SURFACE=codex, fake app-server reachable -> AppServerInjectStrategy.
  3. Fake inject success -> delivered without piggyback buffer.
  4. Fake inject failure -> buffered for piggyback.
  5. on_bootstrap and on_wrap are idempotent.
End-to-end:
  1. Desiree -> Texi with no app-server still arrives through piggyback/poll.
  2. Desiree -> Texi with fake/local app-server inject path causes Texi’s Codex thread to receive an injected Prism signal item without manual prism_signals_pending.

Open Questions

  1. Whether v1 should launch one app-server per Codex session or share a per-machine app-server. Recommendation: one per coder-launched Codex session.
  2. Exact app-server JSON-RPC request/response shape for thread/inject_items; verify against installed Codex CLI generated schema or official docs.
  3. Whether future LAN mode should exist at all, given Prism already has authenticated session stream plus local MCP bridge.
Last modified on May 18, 2026