Allow async Stop to start a same-session continuation (KV cache)
What we are trying
knowcards captures durable repo facts at end of turn. The reflect step must run on the same session id and message history so the model reuses KV cache. A separate worker/LLM (truncated transcript) throws that cache away.
We want that continuation after the user-facing turn ends (background / idle wake), not as a fake user message in the middle of the answer.
What Codex does today
- Sync
Stop+decision: \"block\"+reasonworks: Codex creates a new user prompt fromreasonand continues the same session. That keeps KV cache, but it is a user-channel message and it runs immediately. - Docs: a background hook that finishes while no turn is active waits for the next user turn and does not start a new turn.
- Related: async command hooks may still be skipped at load (#34694).
So we cannot both (1) stay on this session for KV cache and (2) defer reflect until the UI has stopped.
Request
Let an async Stop handler start a same-session continuation when it returns decision: \"block\" + reason (or hookSpecificOutput.additionalContext as developer context, not a user prompt).
Ideal shape:
- Same
session_id/ transcript prefix (KV cache hits). - Not a user-visible chat bubble (developer / system reminder).
- May start after the user-facing turn is idle (wake), instead of only attaching to the next human prompt.
Workaround in knowcards
knowcards install codex keeps Stop synchronous for now. Adapter: src/adapters/codex-reflect.ts
Claude Code has asyncRewake (exit 2 wakes the idle session). We use that there. Codex has no equivalent.
1 Comment
The runtime primitive you need already exists — it's just not reachable from hook results. Core sessions have a mailbox whose items can be flagged
trigger_turn; when the thread goes idle,maybe_start_turn_for_pending_workstarts a synthetic same-session turn to drain them — no user prompt involved, same thread, same transcript prefix, so KV cache is preserved (https://github.com/openai/codex/blob/1f41cc5d92/codex-rs/core/src/tasks/mod.rs#L442-L492). That is exactly "wake the idle session with developer-channel input": it's how inter-agent mail and the goal extension's idle continuation (ext/goal/src/runtime.rs,continue_if_idle) already work.So the feature could be narrow: let an async
Stophandler'sdecision: "block"(or a newcontinuationfield) enqueue a mailbox item withtrigger_turn = trueand developer-channel provenance, instead of today's behavior of synthesizing a user prompt inline. All three of your requirements fall out of the existing machinery — same session id, non-user-visible channel, and deferred start on idle rather than attached to the next human prompt.