Skip to content

[codex] Store pending response items directly#24865

Merged
pakrym-oai merged 5 commits into
mainfrom
pakrym/responseitem-pending-input
May 28, 2026
Merged

[codex] Store pending response items directly#24865
pakrym-oai merged 5 commits into
mainfrom
pakrym/responseitem-pending-input

Conversation

@pakrym-oai
Copy link
Copy Markdown
Collaborator

Why

Pending injected items were stored as ResponseInputItem even though session history and raw item injection already operate on ResponseItem. That forced record-time conversions and split injection behavior across several caller-specific paths.

What Changed

  • store pending turn items and idle queued response items as ResponseItem
  • extract Session injection behavior into core/src/session/inject.rs, with explicit helpers for active-only, starts-turn, and no-new-turn delivery
  • route shell output, raw thread item injection, goal context, mailbox delivery, and related expectations through the unified representation
  • inline history recording into record_conversation_items and update interrupt-marker expectations for its raw-item notification

Validation

  • just fix -p codex-core
  • just test -p codex-core session::tests::interrupting_regular_turn_waiting_on_startup_prewarm_emits_turn_aborted
  • Attempted just test -p codex-core; before the final expectation follow-up, the run failed primarily in MCP/code-mode cases that could not locate target/debug/test_stdio_server, plus one timeout. The affected startup-prewarm expectation was corrected and passed in isolation.

@pakrym-oai pakrym-oai marked this pull request as ready for review May 28, 2026 06:08
@pakrym-oai pakrym-oai requested a review from a team as a code owner May 28, 2026 06:08
Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 128194f473

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines 414 to 417
self.codex
.session
.record_conversation_items(turn_context.as_ref(), &items)
.inject_no_new_turn(items, Some(turn_context.as_ref()))
.await;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Badge Preserve raw injected items immediately

When CodexThread::inject_response_items is called while a turn is active, this now routes through inject_no_new_turn, whose inject_if_running success path only appends the items to the active turn's pending-input queue and returns without recording them. The method still returns Ok after flush_rollout, but the items are not in history/rollout until a later pending-input drain; if the active turn is interrupted or aborted first, abort_all_tasks clears that pending input, so an API call that previously durably appended raw items can silently lose them.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Temporary cost of unification for an advanced-use method.

Comment on lines 570 to 572
Ok(ResponseEvent::OutputItemDone(item)) => {
sess.record_into_history(std::slice::from_ref(&item), turn_context)
sess.record_conversation_items(turn_context, std::slice::from_ref(&item))
.await;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge Do not persist unfinished compact outputs

In local compaction, drain_to_completed explicitly treats a stream that closes before response.completed as an error, but this call now persists every OutputItemDone to rollout before that completion is observed. If the compact model emits the summary item and then the stream disconnects before response.completed, the compaction fails/retries without installing replace_compacted_history, yet the unfinished compact output is already durable and will pollute reconstructed history after a restart; the old record_into_history path did not write these provisional items to rollout.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

@pakrym-oai pakrym-oai May 28, 2026

Choose a reason for hiding this comment

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

replace_compacted_history "will" wipe these extra items anyway.

@pakrym-oai pakrym-oai force-pushed the pakrym/responseitem-pending-input branch 6 times, most recently from 0379ef7 to 7aa4197 Compare May 28, 2026 06:26
@pakrym-oai pakrym-oai force-pushed the pakrym/responseitem-pending-input branch from 7aa4197 to 0a683cc Compare May 28, 2026 06:28
@jif-oai
Copy link
Copy Markdown
Collaborator

jif-oai commented May 28, 2026

@codex review

Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7a37149b24

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +37 to +38
let Err(items) = self.inject_if_running(items).await else {
return;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Badge Preserve silent history injection during active turns

When thread/inject_items is called while a turn is active, this early return queues the items as pending turn input instead of recording them immediately. That violates the app-server contract in app-server/README.md that injected items are persisted to rollout and included in subsequent model requests without starting a user turn: the JSON-RPC response can return before the items are durably written, and the active turn will treat them as pending input and issue an extra follow-up model request even if the model was otherwise done. CodexThread::inject_response_items passes a current_turn_context specifically for appending raw history; this path should record to that context rather than always preferring inject_if_running.

Useful? React with 👍 / 👎.

Comment on lines +571 to 572
sess.record_conversation_items(turn_context, std::slice::from_ref(&item))
.await;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge Keep local compaction output transient until completion

For local compaction, this now persists and emits each compaction model OutputItemDone, whereas the previous record_into_history call kept these synthesized summary outputs only in memory until replace_compacted_history runs after response.completed. If the compaction stream emits an output item and then fails or is interrupted before completion, the rollout now contains that partial compaction response as ordinary conversation history, so a resumed session can include a summary the compaction never successfully installed.

Useful? React with 👍 / 👎.

Comment on lines +846 to +849
self.record_conversation_items(
task.turn_context.as_ref(),
std::slice::from_ref(&marker),
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge Keep interrupt markers out of the client event stream

For interrupted turns, this changed the internal <turn_aborted> marker from history+rollout-only persistence to record_conversation_items, which also sends a RawResponseItem before TurnAborted. That marker is model-visible recovery context, not model output; clients that subscribe to raw response items can now render or process an internal control message for every interrupted regular/review turn, whereas the previous path deliberately persisted it without a separate client-visible raw item.

Useful? React with 👍 / 👎.

@pakrym-oai pakrym-oai merged commit 1c7832f into main May 28, 2026
31 checks passed
@pakrym-oai pakrym-oai deleted the pakrym/responseitem-pending-input branch May 28, 2026 14:23
@github-actions github-actions Bot locked and limited conversation to collaborators May 28, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants