From cbc5fb9acf9d027646fa7ae86ff601976015673b Mon Sep 17 00:00:00 2001 From: Anton Panasenko Date: Mon, 22 Dec 2025 19:51:07 -0800 Subject: [PATCH] chore: save more about turn context in rollout log file (#8458) ### Motivation - Persist richer per-turn configuration in rollouts so resumed/forked sessions and tooling can reason about the exact instruction inputs and output constraints used for a turn. ### Description - Extend `TurnContextItem` to include optional `base_instructions`, `user_instructions`, and `developer_instructions`. - Record the optional `final_output_json_schema` associated with a turn. - Add an optional `truncation_policy` to `TurnContextItem` and populate it when writing turn-context rollout items. - Introduce a protocol-level `TruncationPolicy` representation and convert from core truncation policy when recording. ### Testing - `cargo test -p codex-protocol` (pass) --- codex-rs/core/src/codex.rs | 5 +++++ codex-rs/core/src/compact.rs | 5 +++++ codex-rs/core/src/truncate.rs | 10 ++++++++++ codex-rs/core/tests/suite/resume_warning.rs | 5 +++++ codex-rs/protocol/src/protocol.rs | 17 +++++++++++++++++ 5 files changed, 42 insertions(+) diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs index 5e039b13a..92ce74e3f 100644 --- a/codex-rs/core/src/codex.rs +++ b/codex-rs/core/src/codex.rs @@ -2514,6 +2514,11 @@ async fn try_run_turn( model: turn_context.client.get_model(), effort: turn_context.client.get_reasoning_effort(), summary: turn_context.client.get_reasoning_summary(), + base_instructions: turn_context.base_instructions.clone(), + user_instructions: turn_context.user_instructions.clone(), + developer_instructions: turn_context.developer_instructions.clone(), + final_output_json_schema: turn_context.final_output_json_schema.clone(), + truncation_policy: Some(turn_context.truncation_policy.into()), }); sess.persist_rollout_items(&[rollout_item]).await; diff --git a/codex-rs/core/src/compact.rs b/codex-rs/core/src/compact.rs index 1a90b7b22..77b9303e9 100644 --- a/codex-rs/core/src/compact.rs +++ b/codex-rs/core/src/compact.rs @@ -86,6 +86,11 @@ async fn run_compact_task_inner( model: turn_context.client.get_model(), effort: turn_context.client.get_reasoning_effort(), summary: turn_context.client.get_reasoning_summary(), + base_instructions: turn_context.base_instructions.clone(), + user_instructions: turn_context.user_instructions.clone(), + developer_instructions: turn_context.developer_instructions.clone(), + final_output_json_schema: turn_context.final_output_json_schema.clone(), + truncation_policy: Some(turn_context.truncation_policy.into()), }); sess.persist_rollout_items(&[rollout_item]).await; diff --git a/codex-rs/core/src/truncate.rs b/codex-rs/core/src/truncate.rs index 9f0672363..7cced9970 100644 --- a/codex-rs/core/src/truncate.rs +++ b/codex-rs/core/src/truncate.rs @@ -6,6 +6,7 @@ use crate::config::Config; use codex_protocol::models::FunctionCallOutputContentItem; use codex_protocol::openai_models::TruncationMode; use codex_protocol::openai_models::TruncationPolicyConfig; +use codex_protocol::protocol::TruncationPolicy as ProtocolTruncationPolicy; const APPROX_BYTES_PER_TOKEN: usize = 4; @@ -15,6 +16,15 @@ pub enum TruncationPolicy { Tokens(usize), } +impl From for ProtocolTruncationPolicy { + fn from(value: TruncationPolicy) -> Self { + match value { + TruncationPolicy::Bytes(bytes) => Self::Bytes(bytes), + TruncationPolicy::Tokens(tokens) => Self::Tokens(tokens), + } + } +} + impl From for TruncationPolicy { fn from(config: TruncationPolicyConfig) -> Self { match config.mode { diff --git a/codex-rs/core/tests/suite/resume_warning.rs b/codex-rs/core/tests/suite/resume_warning.rs index 5369398a3..92acf7de0 100644 --- a/codex-rs/core/tests/suite/resume_warning.rs +++ b/codex-rs/core/tests/suite/resume_warning.rs @@ -28,6 +28,11 @@ fn resume_history( model: previous_model.to_string(), effort: config.model_reasoning_effort, summary: config.model_reasoning_summary, + base_instructions: None, + user_instructions: None, + developer_instructions: None, + final_output_json_schema: None, + truncation_policy: None, }; InitialHistory::Resumed(ResumedHistory { diff --git a/codex-rs/protocol/src/protocol.rs b/codex-rs/protocol/src/protocol.rs index 1e03f5ce1..f2e31c903 100644 --- a/codex-rs/protocol/src/protocol.rs +++ b/codex-rs/protocol/src/protocol.rs @@ -1324,6 +1324,23 @@ pub struct TurnContextItem { #[serde(skip_serializing_if = "Option::is_none")] pub effort: Option, pub summary: ReasoningSummaryConfig, + #[serde(skip_serializing_if = "Option::is_none")] + pub base_instructions: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub user_instructions: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub developer_instructions: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub final_output_json_schema: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub truncation_policy: Option, +} + +#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, JsonSchema, TS)] +#[serde(tag = "mode", content = "limit", rename_all = "snake_case")] +pub enum TruncationPolicy { + Bytes(usize), + Tokens(usize), } #[derive(Serialize, Deserialize, Clone, JsonSchema)]