Files
codex/codex-rs/core/src/session_prefix.rs
T
pakrym-oai ce0323f2f2 Support typed content in contextual prompt fragments (#54776)
## Why

Contextual fragments and extension-owned world-state sections currently return strings, limiting them to text content.

## What changed

- Return `ContentItem` from fragment bodies and rendering, preserving classification and attribution. Apply text markers only to `InputText`; pass other content through unchanged.
- Allow extension prompt and world-state contributions to carry typed content, and preserve media in context-window hints.
- Hash non-text world-state fragments by their serialized content and role while retaining the existing text hashing behavior.
- Add shared content-to-tool-output conversion that preserves image references, image detail, and audio.

## Testing

Add coverage for typed world-state hashing and preservation of image content and attribution. Expand code-mode output tests to cover input text, output text, file-backed and inline images, and audio.

GitOrigin-RevId: 2ed00d466a3f169b151202f3eef71e667ea64be1
2026-10-17 04:02:21 +00:00

70 lines
2.7 KiB
Rust

use codex_protocol::AgentPath;
use codex_protocol::protocol::AgentStatus;
use codex_utils_output_truncation::TruncationPolicy;
use codex_utils_output_truncation::truncate_text;
use crate::context::ContextualUserFragment;
use crate::context::InterAgentCompletionMessage;
const COMPLETION_MESSAGE_MAX_TOKENS: usize = 1_000;
const COMPLETION_MESSAGE_ENVELOPE_TOKEN_RESERVE: usize = 100;
const ERROR_MAX_TOKENS: usize =
COMPLETION_MESSAGE_MAX_TOKENS - COMPLETION_MESSAGE_ENVELOPE_TOKEN_RESERVE;
const ERROR_NEXT_ACTION: &str = "This agent's turn failed. If you still need this agent, use the available collaboration tools to give it another task.";
const GUARDIAN_NEXT_ACTION: &str = "Tell the user this agent stopped after repeated Guardian denials. Do not resume this agent or retry its blocked work until the user explicitly confirms that it should continue.";
// Helpers for model-visible session state markers that are stored in user-role
// messages but are not user intent.
// TODO(jif) unify with structured schema
pub(crate) fn format_inter_agent_completion_message(
task_name: AgentPath,
sender: AgentPath,
status: &AgentStatus,
) -> Option<String> {
let payload = match status {
AgentStatus::Completed(Some(message)) => message.clone(),
AgentStatus::Completed(None) => String::new(),
AgentStatus::Errored(error) => {
let error = truncate_text(error, TruncationPolicy::Tokens(ERROR_MAX_TOKENS));
format!("Agent errored: {error}\n\n{ERROR_NEXT_ACTION}")
}
AgentStatus::Shutdown => "Agent shut down.".to_string(),
AgentStatus::NotFound => "Agent was not found.".to_string(),
AgentStatus::PendingInit | AgentStatus::Running | AgentStatus::Interrupted => return None,
};
Some(
InterAgentCompletionMessage::new(task_name, sender, payload)
.render()
.into_text()
.unwrap_or_default(),
)
}
pub(crate) fn format_guardian_interruption_message(
task_name: AgentPath,
sender: AgentPath,
error: &str,
) -> String {
let error = truncate_text(error, TruncationPolicy::Tokens(ERROR_MAX_TOKENS));
let payload = format!("Agent interrupted by Guardian: {error}\n\n{GUARDIAN_NEXT_ACTION}");
InterAgentCompletionMessage::new(task_name, sender, payload)
.render()
.into_text()
.unwrap_or_default()
}
#[cfg(test)]
#[path = "session_prefix_tests.rs"]
mod tests;
pub(crate) fn format_subagent_context_line(
agent_reference: &str,
agent_nickname: Option<&str>,
) -> String {
match agent_nickname.filter(|nickname| !nickname.is_empty()) {
Some(agent_nickname) => format!("- {agent_reference}: {agent_nickname}"),
None => format!("- {agent_reference}"),
}
}