Skip to content

codex adapter: detached session send prompt dropped — session replays a stale rollout, wait-idle never completes #56

Description

@vu1n

Summary

Driving a detached codex session headlessly (run --detachsession sendwait-idle) does not deliver the injected prompt to a fresh turn. The agent instead replays a stale prior conversation, does no work, and wait-idle times out. The full session/score surface works for the lifecycle; the codex adapter just doesn't consume the fresh session send correctly. (claude/opencode are "full" fidelity; codex is "lifecycle-only" per docs/agent-io-contract.md — this is the gap biting.)

Repro (Docker backend)

mkdir /tmp/pb-codex-smoke && cd /tmp/pb-codex-smoke
pillbox new --agent codex --name pb-codex-smoke
SID=$(pillbox run --agent codex --detach --json --workspace /tmp/pb-codex-smoke --label smoke --ttl 1h | jq -r .session.id)
pillbox session send "$SID" $'Create a file named hello.txt whose contents are exactly the text PILLBOX_OK (no trailing newline). Then stop.\n'
pillbox session wait-idle "$SID" --timeout 280
cat /tmp/pb-codex-smoke/hello.txt   # expected: PILLBOX_OK

Expected

Codex receives the injected prompt, creates hello.txt = PILLBOX_OK, goes idle; wait-idle exits 0; session score --cmd 'test "$(cat hello.txt)" = PILLBOX_OK'passed: true.

Actual

  • wait-idle timed out (280s, never idle); no hello.txt; scorepassed:false.
  • The durable §0 log shows the injected prompt was logged as input, but the agent processed a different, stale user message and answered that:
seq 1  actor=u:vuln   payload.text="Create a file named hello.txt ... Then stop.\n"   // my input, received
seq 5  actor=a:codex  role=user  text="what is websocket"                            // STALE — never sent this run
seq 6/8 actor=a:codex assistant  text="A **WebSocket** is a network protocol ..."    // answered the stale prompt

Its <environment_context> reported cwd=/workspace/cereal — a mount name from an unrelated prior session, not this run's pb-codex-smoke workspace. So the codex turn is running against a resumed/stale rollout, not the fresh detached session.

Hypothesis

The codex agent harness, on a detached run, resumes a previous codex rollout (or a codex exec/proto session) instead of starting fresh — so the session send text either lands in a session that's mid-replay or is dropped by a PTY-readiness race at startup, and an older buffered/persisted conversation ("what is websocket", /workspace/cereal) executes instead. Worth checking how src/agents/harness launches codex for a detached session (fresh vs --resume/persisted session id) and whether the first session send races the codex TUI/proto becoming ready to accept input.

Impact

Blocks headless/orchestrated codex runs (the send → wait-idle → score drive loop): any orchestrator (incl. a parallel multi-workspace "ship an issue with codex" flow) gets stale/empty results. claude is unaffected and is the working drive path today.

Environment

  • pillbox: local debug build (target/debug/pillbox, built 2026-06-13), version/runner ghcr.io/vu1n/pillbox-runner:latest
  • codex: codex-cli 0.139.0, ChatGPT auth (verified via pillbox auth list)
  • backend: Docker 29.4.0; pillbox doctor all green

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions