Refactor tool output into trait implementations (#14152)
First state to making tool outputs strongly typed (and `renderable`).
This commit is contained in:
parent
a5af11211a
commit
aa04ea6bd7
24 changed files with 354 additions and 344 deletions
|
|
@ -2851,6 +2851,7 @@ impl Session {
|
|||
AskForApproval::Never => {
|
||||
return Some(RequestPermissionsResponse {
|
||||
permissions: PermissionProfile::default(),
|
||||
scope: PermissionGrantScope::Turn,
|
||||
});
|
||||
}
|
||||
AskForApproval::Reject(reject_config)
|
||||
|
|
@ -2858,6 +2859,7 @@ impl Session {
|
|||
{
|
||||
return Some(RequestPermissionsResponse {
|
||||
permissions: PermissionProfile::default(),
|
||||
scope: PermissionGrantScope::Turn,
|
||||
});
|
||||
}
|
||||
AskForApproval::OnFailure
|
||||
|
|
|
|||
|
|
@ -46,8 +46,8 @@ use crate::state::TaskKind;
|
|||
use crate::tasks::SessionTask;
|
||||
use crate::tasks::SessionTaskContext;
|
||||
use crate::tools::ToolRouter;
|
||||
use crate::tools::context::TextToolOutput;
|
||||
use crate::tools::context::ToolInvocation;
|
||||
use crate::tools::context::ToolOutput;
|
||||
use crate::tools::context::ToolPayload;
|
||||
use crate::tools::handlers::ShellHandler;
|
||||
use crate::tools::handlers::UnifiedExecHandler;
|
||||
|
|
@ -89,6 +89,13 @@ use std::time::Duration as StdDuration;
|
|||
#[path = "codex_tests_guardian.rs"]
|
||||
mod guardian_tests;
|
||||
|
||||
fn expect_text_tool_output(output: &dyn std::any::Any) -> String {
|
||||
let Some(output) = output.downcast_ref::<TextToolOutput>() else {
|
||||
panic!("unexpected tool output");
|
||||
};
|
||||
output.text.clone()
|
||||
}
|
||||
|
||||
struct InstructionsTestCase {
|
||||
slug: &'static str,
|
||||
expects_apply_patch_instructions: bool,
|
||||
|
|
@ -2293,6 +2300,7 @@ async fn request_permissions_emits_event_when_reject_policy_allows_requests() {
|
|||
}),
|
||||
..Default::default()
|
||||
},
|
||||
scope: codex_protocol::request_permissions::PermissionGrantScope::Turn,
|
||||
};
|
||||
|
||||
let handle = tokio::spawn({
|
||||
|
|
@ -2377,6 +2385,7 @@ async fn request_permissions_returns_empty_grant_when_reject_policy_blocks_reque
|
|||
Some(
|
||||
codex_protocol::request_permissions::RequestPermissionsResponse {
|
||||
permissions: codex_protocol::models::PermissionProfile::default(),
|
||||
scope: codex_protocol::request_permissions::PermissionGrantScope::Turn,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
|
@ -4133,13 +4142,7 @@ async fn rejects_escalated_permissions_when_policy_not_on_request() {
|
|||
})
|
||||
.await;
|
||||
|
||||
let output = match resp2.expect("expected Ok result") {
|
||||
ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(content),
|
||||
..
|
||||
} => content,
|
||||
_ => panic!("unexpected tool output"),
|
||||
};
|
||||
let output = expect_text_tool_output(&*resp2.expect("expected Ok result"));
|
||||
|
||||
#[derive(Deserialize, PartialEq, Eq, Debug)]
|
||||
struct ResponseExecMetadata {
|
||||
|
|
|
|||
|
|
@ -8,12 +8,12 @@ use crate::features::Feature;
|
|||
use crate::guardian::GUARDIAN_SUBAGENT_NAME;
|
||||
use crate::protocol::AskForApproval;
|
||||
use crate::sandboxing::SandboxPermissions;
|
||||
use crate::tools::context::TextToolOutput;
|
||||
use crate::turn_diff_tracker::TurnDiffTracker;
|
||||
use codex_app_server_protocol::ConfigLayerSource;
|
||||
use codex_execpolicy::Decision;
|
||||
use codex_execpolicy::Evaluation;
|
||||
use codex_execpolicy::RuleMatch;
|
||||
use codex_protocol::models::FunctionCallOutputBody;
|
||||
use codex_protocol::models::NetworkPermissions;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::permissions::FileSystemSandboxPolicy;
|
||||
|
|
@ -33,6 +33,13 @@ use std::fs;
|
|||
use std::sync::Arc;
|
||||
use tempfile::tempdir;
|
||||
|
||||
fn expect_text_output(output: &dyn std::any::Any) -> String {
|
||||
let Some(output) = output.downcast_ref::<TextToolOutput>() else {
|
||||
panic!("unexpected tool output");
|
||||
};
|
||||
output.text.clone()
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn guardian_allows_shell_additional_permissions_requests_past_policy_validation() {
|
||||
let server = start_mock_server().await;
|
||||
|
|
@ -152,13 +159,7 @@ async fn guardian_allows_shell_additional_permissions_requests_past_policy_valid
|
|||
})
|
||||
.await;
|
||||
|
||||
let output = match resp.expect("expected Ok result") {
|
||||
ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(content),
|
||||
..
|
||||
} => content,
|
||||
_ => panic!("unexpected tool output"),
|
||||
};
|
||||
let output = expect_text_output(&*resp.expect("expected Ok result"));
|
||||
|
||||
#[derive(Deserialize, PartialEq, Eq, Debug)]
|
||||
struct ResponseExecMetadata {
|
||||
|
|
|
|||
|
|
@ -6,10 +6,12 @@ use crate::tools::TELEMETRY_PREVIEW_TRUNCATION_NOTICE;
|
|||
use crate::turn_diff_tracker::TurnDiffTracker;
|
||||
use codex_protocol::mcp::CallToolResult;
|
||||
use codex_protocol::models::FunctionCallOutputBody;
|
||||
use codex_protocol::models::FunctionCallOutputContentItem;
|
||||
use codex_protocol::models::FunctionCallOutputPayload;
|
||||
use codex_protocol::models::ResponseInputItem;
|
||||
use codex_protocol::models::ShellToolCallParams;
|
||||
use codex_utils_string::take_bytes_at_char_boundary;
|
||||
use std::any::Any;
|
||||
use std::borrow::Cow;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
|
|
@ -61,65 +63,111 @@ impl ToolPayload {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ToolOutput {
|
||||
Function {
|
||||
// Canonical output body for function-style tools. This may be plain text
|
||||
// or structured content items.
|
||||
body: FunctionCallOutputBody,
|
||||
success: Option<bool>,
|
||||
},
|
||||
Mcp {
|
||||
result: Result<CallToolResult, String>,
|
||||
},
|
||||
pub trait ToolOutput: Any + Send {
|
||||
fn log_preview(&self) -> String;
|
||||
|
||||
fn success_for_logging(&self) -> bool;
|
||||
|
||||
fn into_response(self: Box<Self>, call_id: &str, payload: &ToolPayload) -> ResponseInputItem;
|
||||
}
|
||||
|
||||
impl ToolOutput {
|
||||
pub fn log_preview(&self) -> String {
|
||||
match self {
|
||||
ToolOutput::Function { body, .. } => {
|
||||
telemetry_preview(&body.to_text().unwrap_or_default())
|
||||
}
|
||||
ToolOutput::Mcp { result } => format!("{result:?}"),
|
||||
}
|
||||
pub type ToolOutputBox = Box<dyn ToolOutput>;
|
||||
|
||||
pub struct McpToolOutput {
|
||||
pub result: Result<CallToolResult, String>,
|
||||
}
|
||||
|
||||
impl ToolOutput for McpToolOutput {
|
||||
fn log_preview(&self) -> String {
|
||||
format!("{:?}", self.result)
|
||||
}
|
||||
|
||||
pub fn success_for_logging(&self) -> bool {
|
||||
match self {
|
||||
ToolOutput::Function { success, .. } => success.unwrap_or(true),
|
||||
ToolOutput::Mcp { result } => result.is_ok(),
|
||||
}
|
||||
fn success_for_logging(&self) -> bool {
|
||||
self.result.is_ok()
|
||||
}
|
||||
|
||||
pub fn into_response(self, call_id: &str, payload: &ToolPayload) -> ResponseInputItem {
|
||||
match self {
|
||||
ToolOutput::Function { body, success } => {
|
||||
// `custom_tool_call` is the Responses API item type for freeform
|
||||
// tools (`ToolSpec::Freeform`, e.g. freeform `apply_patch` or
|
||||
// `js_repl`).
|
||||
if matches!(payload, ToolPayload::Custom { .. }) {
|
||||
return ResponseInputItem::CustomToolCallOutput {
|
||||
call_id: call_id.to_string(),
|
||||
output: FunctionCallOutputPayload { body, success },
|
||||
};
|
||||
}
|
||||
|
||||
// Function-style outputs (JSON function tools, including dynamic
|
||||
// tools and MCP adaptation) preserve the exact body shape.
|
||||
ResponseInputItem::FunctionCallOutput {
|
||||
call_id: call_id.to_string(),
|
||||
output: FunctionCallOutputPayload { body, success },
|
||||
}
|
||||
}
|
||||
// Direct MCP response path for MCP tool result envelopes.
|
||||
ToolOutput::Mcp { result } => ResponseInputItem::McpToolCallOutput {
|
||||
call_id: call_id.to_string(),
|
||||
result,
|
||||
},
|
||||
fn into_response(self: Box<Self>, call_id: &str, _payload: &ToolPayload) -> ResponseInputItem {
|
||||
let Self { result } = *self;
|
||||
ResponseInputItem::McpToolCallOutput {
|
||||
call_id: call_id.to_string(),
|
||||
result,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TextToolOutput {
|
||||
pub text: String,
|
||||
pub success: Option<bool>,
|
||||
}
|
||||
|
||||
impl ToolOutput for TextToolOutput {
|
||||
fn log_preview(&self) -> String {
|
||||
telemetry_preview(&self.text)
|
||||
}
|
||||
|
||||
fn success_for_logging(&self) -> bool {
|
||||
self.success.unwrap_or(true)
|
||||
}
|
||||
|
||||
fn into_response(self: Box<Self>, call_id: &str, payload: &ToolPayload) -> ResponseInputItem {
|
||||
let Self { text, success } = *self;
|
||||
function_tool_response(
|
||||
call_id,
|
||||
payload,
|
||||
FunctionCallOutputBody::Text(text),
|
||||
success,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ContentToolOutput {
|
||||
pub content: Vec<FunctionCallOutputContentItem>,
|
||||
pub success: Option<bool>,
|
||||
}
|
||||
|
||||
impl ToolOutput for ContentToolOutput {
|
||||
fn log_preview(&self) -> String {
|
||||
telemetry_preview(
|
||||
&FunctionCallOutputBody::ContentItems(self.content.clone())
|
||||
.to_text()
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
}
|
||||
|
||||
fn success_for_logging(&self) -> bool {
|
||||
self.success.unwrap_or(true)
|
||||
}
|
||||
|
||||
fn into_response(self: Box<Self>, call_id: &str, payload: &ToolPayload) -> ResponseInputItem {
|
||||
let Self { content, success } = *self;
|
||||
function_tool_response(
|
||||
call_id,
|
||||
payload,
|
||||
FunctionCallOutputBody::ContentItems(content),
|
||||
success,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn function_tool_response(
|
||||
call_id: &str,
|
||||
payload: &ToolPayload,
|
||||
body: FunctionCallOutputBody,
|
||||
success: Option<bool>,
|
||||
) -> ResponseInputItem {
|
||||
if matches!(payload, ToolPayload::Custom { .. }) {
|
||||
return ResponseInputItem::CustomToolCallOutput {
|
||||
call_id: call_id.to_string(),
|
||||
output: FunctionCallOutputPayload { body, success },
|
||||
};
|
||||
}
|
||||
|
||||
ResponseInputItem::FunctionCallOutput {
|
||||
call_id: call_id.to_string(),
|
||||
output: FunctionCallOutputPayload { body, success },
|
||||
}
|
||||
}
|
||||
|
||||
fn telemetry_preview(content: &str) -> String {
|
||||
let truncated_slice = take_bytes_at_char_boundary(content, TELEMETRY_PREVIEW_MAX_BYTES);
|
||||
let truncated_by_bytes = truncated_slice.len() < content.len();
|
||||
|
|
@ -163,7 +211,6 @@ fn telemetry_preview(content: &str) -> String {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use codex_protocol::models::FunctionCallOutputContentItem;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
|
|
@ -171,10 +218,10 @@ mod tests {
|
|||
let payload = ToolPayload::Custom {
|
||||
input: "patch".to_string(),
|
||||
};
|
||||
let response = ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text("patched".to_string()),
|
||||
let response = Box::new(TextToolOutput {
|
||||
text: "patched".to_string(),
|
||||
success: Some(true),
|
||||
}
|
||||
})
|
||||
.into_response("call-42", &payload);
|
||||
|
||||
match response {
|
||||
|
|
@ -193,10 +240,10 @@ mod tests {
|
|||
let payload = ToolPayload::Function {
|
||||
arguments: "{}".to_string(),
|
||||
};
|
||||
let response = ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text("ok".to_string()),
|
||||
let response = Box::new(TextToolOutput {
|
||||
text: "ok".to_string(),
|
||||
success: Some(true),
|
||||
}
|
||||
})
|
||||
.into_response("fn-1", &payload);
|
||||
|
||||
match response {
|
||||
|
|
@ -215,8 +262,8 @@ mod tests {
|
|||
let payload = ToolPayload::Custom {
|
||||
input: "patch".to_string(),
|
||||
};
|
||||
let response = ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::ContentItems(vec![
|
||||
let response = Box::new(ContentToolOutput {
|
||||
content: vec![
|
||||
FunctionCallOutputContentItem::InputText {
|
||||
text: "line 1".to_string(),
|
||||
},
|
||||
|
|
@ -227,9 +274,9 @@ mod tests {
|
|||
FunctionCallOutputContentItem::InputText {
|
||||
text: "line 2".to_string(),
|
||||
},
|
||||
]),
|
||||
],
|
||||
success: Some(true),
|
||||
}
|
||||
})
|
||||
.into_response("call-99", &payload);
|
||||
|
||||
match response {
|
||||
|
|
@ -257,12 +304,10 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn log_preview_uses_content_items_when_plain_text_is_missing() {
|
||||
let output = ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::ContentItems(vec![
|
||||
FunctionCallOutputContentItem::InputText {
|
||||
text: "preview".to_string(),
|
||||
},
|
||||
]),
|
||||
let output = ContentToolOutput {
|
||||
content: vec![FunctionCallOutputContentItem::InputText {
|
||||
text: "preview".to_string(),
|
||||
}],
|
||||
success: Some(true),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -6,8 +6,9 @@ use crate::codex::TurnContext;
|
|||
use crate::config::Config;
|
||||
use crate::error::CodexErr;
|
||||
use crate::function_tool::FunctionCallError;
|
||||
use crate::tools::context::TextToolOutput;
|
||||
use crate::tools::context::ToolInvocation;
|
||||
use crate::tools::context::ToolOutput;
|
||||
use crate::tools::context::ToolOutputBox;
|
||||
use crate::tools::context::ToolPayload;
|
||||
use crate::tools::handlers::multi_agents::build_agent_spawn_config;
|
||||
use crate::tools::handlers::parse_arguments;
|
||||
|
|
@ -15,7 +16,6 @@ use crate::tools::registry::ToolHandler;
|
|||
use crate::tools::registry::ToolKind;
|
||||
use async_trait::async_trait;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::models::FunctionCallOutputBody;
|
||||
use codex_protocol::protocol::SessionSource;
|
||||
use codex_protocol::protocol::SubAgentSource;
|
||||
use codex_protocol::user_input::UserInput;
|
||||
|
|
@ -183,7 +183,7 @@ impl ToolHandler for BatchJobHandler {
|
|||
matches!(payload, ToolPayload::Function { .. })
|
||||
}
|
||||
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutput, FunctionCallError> {
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
let ToolInvocation {
|
||||
session,
|
||||
turn,
|
||||
|
|
@ -223,7 +223,7 @@ mod spawn_agents_on_csv {
|
|||
session: Arc<Session>,
|
||||
turn: Arc<TurnContext>,
|
||||
arguments: String,
|
||||
) -> Result<ToolOutput, FunctionCallError> {
|
||||
) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
let args: SpawnAgentsOnCsvArgs = parse_arguments(arguments.as_str())?;
|
||||
if args.instruction.trim().is_empty() {
|
||||
return Err(FunctionCallError::RespondToModel(
|
||||
|
|
@ -456,10 +456,10 @@ mod spawn_agents_on_csv {
|
|||
"failed to serialize spawn_agents_on_csv result: {err}"
|
||||
))
|
||||
})?;
|
||||
Ok(ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(content),
|
||||
Ok(Box::new(TextToolOutput {
|
||||
text: content,
|
||||
success: Some(true),
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -469,7 +469,7 @@ mod report_agent_job_result {
|
|||
pub async fn handle(
|
||||
session: Arc<Session>,
|
||||
arguments: String,
|
||||
) -> Result<ToolOutput, FunctionCallError> {
|
||||
) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
let args: ReportAgentJobResultArgs = parse_arguments(arguments.as_str())?;
|
||||
if !args.result.is_object() {
|
||||
return Err(FunctionCallError::RespondToModel(
|
||||
|
|
@ -505,10 +505,10 @@ mod report_agent_job_result {
|
|||
"failed to serialize report_agent_job_result result: {err}"
|
||||
))
|
||||
})?;
|
||||
Ok(ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(content),
|
||||
Ok(Box::new(TextToolOutput {
|
||||
text: content,
|
||||
success: Some(true),
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
use codex_protocol::models::FunctionCallOutputBody;
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::Path;
|
||||
|
||||
|
|
@ -13,8 +12,9 @@ use crate::codex::Session;
|
|||
use crate::codex::TurnContext;
|
||||
use crate::function_tool::FunctionCallError;
|
||||
use crate::tools::context::SharedTurnDiffTracker;
|
||||
use crate::tools::context::TextToolOutput;
|
||||
use crate::tools::context::ToolInvocation;
|
||||
use crate::tools::context::ToolOutput;
|
||||
use crate::tools::context::ToolOutputBox;
|
||||
use crate::tools::context::ToolPayload;
|
||||
use crate::tools::events::ToolEmitter;
|
||||
use crate::tools::events::ToolEventCtx;
|
||||
|
|
@ -107,7 +107,7 @@ impl ToolHandler for ApplyPatchHandler {
|
|||
true
|
||||
}
|
||||
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutput, FunctionCallError> {
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
let ToolInvocation {
|
||||
session,
|
||||
turn,
|
||||
|
|
@ -140,10 +140,10 @@ impl ToolHandler for ApplyPatchHandler {
|
|||
match apply_patch::apply_patch(turn.as_ref(), changes).await {
|
||||
InternalApplyPatchInvocation::Output(item) => {
|
||||
let content = item?;
|
||||
Ok(ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(content),
|
||||
Ok(Box::new(TextToolOutput {
|
||||
text: content,
|
||||
success: Some(true),
|
||||
})
|
||||
}))
|
||||
}
|
||||
InternalApplyPatchInvocation::DelegateToExec(apply) => {
|
||||
let changes = convert_apply_patch_to_protocol(&apply.action);
|
||||
|
|
@ -204,10 +204,10 @@ impl ToolHandler for ApplyPatchHandler {
|
|||
Some(&tracker),
|
||||
);
|
||||
let content = emitter.finish(event_ctx, out).await?;
|
||||
Ok(ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(content),
|
||||
Ok(Box::new(TextToolOutput {
|
||||
text: content,
|
||||
success: Some(true),
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -241,7 +241,7 @@ pub(crate) async fn intercept_apply_patch(
|
|||
tracker: Option<&SharedTurnDiffTracker>,
|
||||
call_id: &str,
|
||||
tool_name: &str,
|
||||
) -> Result<Option<ToolOutput>, FunctionCallError> {
|
||||
) -> Result<Option<ToolOutputBox>, FunctionCallError> {
|
||||
match codex_apply_patch::maybe_parse_apply_patch_verified(command, cwd) {
|
||||
codex_apply_patch::MaybeApplyPatchVerified::Body(changes) => {
|
||||
session
|
||||
|
|
@ -255,10 +255,10 @@ pub(crate) async fn intercept_apply_patch(
|
|||
match apply_patch::apply_patch(turn.as_ref(), changes).await {
|
||||
InternalApplyPatchInvocation::Output(item) => {
|
||||
let content = item?;
|
||||
Ok(Some(ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(content),
|
||||
Ok(Some(Box::new(TextToolOutput {
|
||||
text: content,
|
||||
success: Some(true),
|
||||
}))
|
||||
})))
|
||||
}
|
||||
InternalApplyPatchInvocation::DelegateToExec(apply) => {
|
||||
let changes = convert_apply_patch_to_protocol(&apply.action);
|
||||
|
|
@ -317,10 +317,10 @@ pub(crate) async fn intercept_apply_patch(
|
|||
tracker.as_ref().copied(),
|
||||
);
|
||||
let content = emitter.finish(event_ctx, out).await?;
|
||||
Ok(Some(ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(content),
|
||||
Ok(Some(Box::new(TextToolOutput {
|
||||
text: content,
|
||||
success: Some(true),
|
||||
}))
|
||||
})))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,9 @@ use crate::exec::StreamOutput;
|
|||
use crate::features::Feature;
|
||||
use crate::function_tool::FunctionCallError;
|
||||
use crate::protocol::ExecCommandSource;
|
||||
use crate::tools::context::TextToolOutput;
|
||||
use crate::tools::context::ToolInvocation;
|
||||
use crate::tools::context::ToolOutput;
|
||||
use crate::tools::context::ToolOutputBox;
|
||||
use crate::tools::context::ToolPayload;
|
||||
use crate::tools::events::ToolEmitter;
|
||||
use crate::tools::events::ToolEventCtx;
|
||||
|
|
@ -25,7 +26,6 @@ use crate::tools::events::ToolEventFailure;
|
|||
use crate::tools::events::ToolEventStage;
|
||||
use crate::tools::registry::ToolHandler;
|
||||
use crate::tools::registry::ToolKind;
|
||||
use codex_protocol::models::FunctionCallOutputBody;
|
||||
|
||||
const ARTIFACTS_TOOL_NAME: &str = "artifacts";
|
||||
const ARTIFACTS_PRAGMA_PREFIXES: [&str; 2] = ["// codex-artifacts:", "// codex-artifact-tool:"];
|
||||
|
|
@ -54,7 +54,7 @@ impl ToolHandler for ArtifactsHandler {
|
|||
true
|
||||
}
|
||||
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutput, FunctionCallError> {
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
let ToolInvocation {
|
||||
session,
|
||||
turn,
|
||||
|
|
@ -112,10 +112,10 @@ impl ToolHandler for ArtifactsHandler {
|
|||
)
|
||||
.await;
|
||||
|
||||
Ok(ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(format_artifact_output(&output)),
|
||||
Ok(Box::new(TextToolOutput {
|
||||
text: format_artifact_output(&output),
|
||||
success: Some(success),
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
use crate::codex::Session;
|
||||
use crate::codex::TurnContext;
|
||||
use crate::function_tool::FunctionCallError;
|
||||
use crate::tools::context::ContentToolOutput;
|
||||
use crate::tools::context::ToolInvocation;
|
||||
use crate::tools::context::ToolOutput;
|
||||
use crate::tools::context::ToolOutputBox;
|
||||
use crate::tools::context::ToolPayload;
|
||||
use crate::tools::handlers::parse_arguments;
|
||||
use crate::tools::registry::ToolHandler;
|
||||
|
|
@ -10,7 +11,6 @@ use crate::tools::registry::ToolKind;
|
|||
use async_trait::async_trait;
|
||||
use codex_protocol::dynamic_tools::DynamicToolCallRequest;
|
||||
use codex_protocol::dynamic_tools::DynamicToolResponse;
|
||||
use codex_protocol::models::FunctionCallOutputBody;
|
||||
use codex_protocol::models::FunctionCallOutputContentItem;
|
||||
use codex_protocol::protocol::DynamicToolCallResponseEvent;
|
||||
use codex_protocol::protocol::EventMsg;
|
||||
|
|
@ -31,7 +31,7 @@ impl ToolHandler for DynamicToolHandler {
|
|||
true
|
||||
}
|
||||
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutput, FunctionCallError> {
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
let ToolInvocation {
|
||||
session,
|
||||
turn,
|
||||
|
|
@ -67,12 +67,10 @@ impl ToolHandler for DynamicToolHandler {
|
|||
.into_iter()
|
||||
.map(FunctionCallOutputContentItem::from)
|
||||
.collect::<Vec<_>>();
|
||||
let body = FunctionCallOutputBody::ContentItems(body);
|
||||
|
||||
Ok(ToolOutput::Function {
|
||||
body,
|
||||
Ok(Box::new(ContentToolOutput {
|
||||
content: body,
|
||||
success: Some(success),
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
use codex_protocol::models::FunctionCallOutputBody;
|
||||
use std::path::Path;
|
||||
use std::time::Duration;
|
||||
|
||||
|
|
@ -8,8 +7,9 @@ use tokio::process::Command;
|
|||
use tokio::time::timeout;
|
||||
|
||||
use crate::function_tool::FunctionCallError;
|
||||
use crate::tools::context::TextToolOutput;
|
||||
use crate::tools::context::ToolInvocation;
|
||||
use crate::tools::context::ToolOutput;
|
||||
use crate::tools::context::ToolOutputBox;
|
||||
use crate::tools::context::ToolPayload;
|
||||
use crate::tools::handlers::parse_arguments;
|
||||
use crate::tools::registry::ToolHandler;
|
||||
|
|
@ -42,7 +42,7 @@ impl ToolHandler for GrepFilesHandler {
|
|||
ToolKind::Function
|
||||
}
|
||||
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutput, FunctionCallError> {
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
let ToolInvocation { payload, turn, .. } = invocation;
|
||||
|
||||
let arguments = match payload {
|
||||
|
|
@ -86,15 +86,15 @@ impl ToolHandler for GrepFilesHandler {
|
|||
run_rg_search(pattern, include.as_deref(), &search_path, limit, &turn.cwd).await?;
|
||||
|
||||
if search_results.is_empty() {
|
||||
Ok(ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text("No matches found.".to_string()),
|
||||
Ok(Box::new(TextToolOutput {
|
||||
text: "No matches found.".to_string(),
|
||||
success: Some(false),
|
||||
})
|
||||
}))
|
||||
} else {
|
||||
Ok(ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(search_results.join("\n")),
|
||||
Ok(Box::new(TextToolOutput {
|
||||
text: search_results.join("\n"),
|
||||
success: Some(true),
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,10 @@ use crate::exec::StreamOutput;
|
|||
use crate::features::Feature;
|
||||
use crate::function_tool::FunctionCallError;
|
||||
use crate::protocol::ExecCommandSource;
|
||||
use crate::tools::context::ContentToolOutput;
|
||||
use crate::tools::context::TextToolOutput;
|
||||
use crate::tools::context::ToolInvocation;
|
||||
use crate::tools::context::ToolOutput;
|
||||
use crate::tools::context::ToolOutputBox;
|
||||
use crate::tools::context::ToolPayload;
|
||||
use crate::tools::events::ToolEmitter;
|
||||
use crate::tools::events::ToolEventCtx;
|
||||
|
|
@ -21,7 +23,6 @@ use crate::tools::js_repl::JS_REPL_PRAGMA_PREFIX;
|
|||
use crate::tools::js_repl::JsReplArgs;
|
||||
use crate::tools::registry::ToolHandler;
|
||||
use crate::tools::registry::ToolKind;
|
||||
use codex_protocol::models::FunctionCallOutputBody;
|
||||
use codex_protocol::models::FunctionCallOutputContentItem;
|
||||
|
||||
pub struct JsReplHandler;
|
||||
|
|
@ -106,7 +107,7 @@ impl ToolHandler for JsReplHandler {
|
|||
)
|
||||
}
|
||||
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutput, FunctionCallError> {
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
let ToolInvocation {
|
||||
session,
|
||||
turn,
|
||||
|
|
@ -173,14 +174,17 @@ impl ToolHandler for JsReplHandler {
|
|||
)
|
||||
.await;
|
||||
|
||||
Ok(ToolOutput::Function {
|
||||
body: if items.is_empty() {
|
||||
FunctionCallOutputBody::Text(content)
|
||||
} else {
|
||||
FunctionCallOutputBody::ContentItems(items)
|
||||
},
|
||||
success: Some(true),
|
||||
})
|
||||
if items.is_empty() {
|
||||
Ok(Box::new(TextToolOutput {
|
||||
text: content,
|
||||
success: Some(true),
|
||||
}))
|
||||
} else {
|
||||
Ok(Box::new(ContentToolOutput {
|
||||
content: items,
|
||||
success: Some(true),
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -190,7 +194,7 @@ impl ToolHandler for JsReplResetHandler {
|
|||
ToolKind::Function
|
||||
}
|
||||
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutput, FunctionCallError> {
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
if !invocation.session.features().enabled(Feature::JsRepl) {
|
||||
return Err(FunctionCallError::RespondToModel(
|
||||
"js_repl is disabled by feature flag".to_string(),
|
||||
|
|
@ -198,10 +202,10 @@ impl ToolHandler for JsReplResetHandler {
|
|||
}
|
||||
let manager = invocation.turn.js_repl.manager().await?;
|
||||
manager.reset().await?;
|
||||
Ok(ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text("js_repl kernel reset".to_string()),
|
||||
Ok(Box::new(TextToolOutput {
|
||||
text: "js_repl kernel reset".to_string(),
|
||||
success: Some(true),
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
use codex_protocol::models::FunctionCallOutputBody;
|
||||
use std::collections::VecDeque;
|
||||
use std::ffi::OsStr;
|
||||
use std::fs::FileType;
|
||||
|
|
@ -11,8 +10,9 @@ use serde::Deserialize;
|
|||
use tokio::fs;
|
||||
|
||||
use crate::function_tool::FunctionCallError;
|
||||
use crate::tools::context::TextToolOutput;
|
||||
use crate::tools::context::ToolInvocation;
|
||||
use crate::tools::context::ToolOutput;
|
||||
use crate::tools::context::ToolOutputBox;
|
||||
use crate::tools::context::ToolPayload;
|
||||
use crate::tools::handlers::parse_arguments;
|
||||
use crate::tools::registry::ToolHandler;
|
||||
|
|
@ -52,7 +52,7 @@ impl ToolHandler for ListDirHandler {
|
|||
ToolKind::Function
|
||||
}
|
||||
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutput, FunctionCallError> {
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
let ToolInvocation { payload, .. } = invocation;
|
||||
|
||||
let arguments = match payload {
|
||||
|
|
@ -102,10 +102,10 @@ impl ToolHandler for ListDirHandler {
|
|||
let mut output = Vec::with_capacity(entries.len() + 1);
|
||||
output.push(format!("Absolute path: {}", path.display()));
|
||||
output.extend(entries);
|
||||
Ok(ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(output.join("\n")),
|
||||
Ok(Box::new(TextToolOutput {
|
||||
text: output.join("\n"),
|
||||
success: Some(true),
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,8 +3,11 @@ use std::sync::Arc;
|
|||
|
||||
use crate::function_tool::FunctionCallError;
|
||||
use crate::mcp_tool_call::handle_mcp_tool_call;
|
||||
use crate::tools::context::ContentToolOutput;
|
||||
use crate::tools::context::McpToolOutput;
|
||||
use crate::tools::context::TextToolOutput;
|
||||
use crate::tools::context::ToolInvocation;
|
||||
use crate::tools::context::ToolOutput;
|
||||
use crate::tools::context::ToolOutputBox;
|
||||
use crate::tools::context::ToolPayload;
|
||||
use crate::tools::registry::ToolHandler;
|
||||
use crate::tools::registry::ToolKind;
|
||||
|
|
@ -18,7 +21,7 @@ impl ToolHandler for McpHandler {
|
|||
ToolKind::Mcp
|
||||
}
|
||||
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutput, FunctionCallError> {
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
let ToolInvocation {
|
||||
session,
|
||||
turn,
|
||||
|
|
@ -54,11 +57,19 @@ impl ToolHandler for McpHandler {
|
|||
.await;
|
||||
|
||||
match response {
|
||||
ResponseInputItem::McpToolCallOutput { result, .. } => Ok(ToolOutput::Mcp { result }),
|
||||
ResponseInputItem::McpToolCallOutput { result, .. } => {
|
||||
Ok(Box::new(McpToolOutput { result }))
|
||||
}
|
||||
ResponseInputItem::FunctionCallOutput { output, .. } => {
|
||||
let success = output.success;
|
||||
let body = output.body;
|
||||
Ok(ToolOutput::Function { body, success })
|
||||
match output.body {
|
||||
codex_protocol::models::FunctionCallOutputBody::Text(text) => {
|
||||
Ok(Box::new(TextToolOutput { text, success }))
|
||||
}
|
||||
codex_protocol::models::FunctionCallOutputBody::ContentItems(content) => {
|
||||
Ok(Box::new(ContentToolOutput { content, success }))
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => Err(FunctionCallError::RespondToModel(
|
||||
"mcp handler received unexpected response variant".to_string(),
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
use codex_protocol::models::FunctionCallOutputBody;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
|
@ -25,8 +24,9 @@ use crate::protocol::EventMsg;
|
|||
use crate::protocol::McpInvocation;
|
||||
use crate::protocol::McpToolCallBeginEvent;
|
||||
use crate::protocol::McpToolCallEndEvent;
|
||||
use crate::tools::context::TextToolOutput;
|
||||
use crate::tools::context::ToolInvocation;
|
||||
use crate::tools::context::ToolOutput;
|
||||
use crate::tools::context::ToolOutputBox;
|
||||
use crate::tools::context::ToolPayload;
|
||||
use crate::tools::registry::ToolHandler;
|
||||
use crate::tools::registry::ToolKind;
|
||||
|
|
@ -184,7 +184,7 @@ impl ToolHandler for McpResourceHandler {
|
|||
ToolKind::Function
|
||||
}
|
||||
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutput, FunctionCallError> {
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
let ToolInvocation {
|
||||
session,
|
||||
turn,
|
||||
|
|
@ -245,7 +245,7 @@ async fn handle_list_resources(
|
|||
turn: Arc<TurnContext>,
|
||||
call_id: String,
|
||||
arguments: Option<Value>,
|
||||
) -> Result<ToolOutput, FunctionCallError> {
|
||||
) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
let args: ListResourcesArgs = parse_args_with_default(arguments.clone())?;
|
||||
let ListResourcesArgs { server, cursor } = args;
|
||||
let server = normalize_optional_string(server);
|
||||
|
|
@ -298,10 +298,12 @@ async fn handle_list_resources(
|
|||
match payload_result {
|
||||
Ok(payload) => match serialize_function_output(payload) {
|
||||
Ok(output) => {
|
||||
let ToolOutput::Function { body, success } = &output else {
|
||||
unreachable!("MCP resource handler should return function output");
|
||||
let Some(output_text) =
|
||||
(&*output as &dyn std::any::Any).downcast_ref::<TextToolOutput>()
|
||||
else {
|
||||
unreachable!("MCP resource handler should return text output");
|
||||
};
|
||||
let content = body.to_text().unwrap_or_default();
|
||||
let content = output_text.text.clone();
|
||||
let duration = start.elapsed();
|
||||
emit_tool_call_end(
|
||||
&session,
|
||||
|
|
@ -309,7 +311,7 @@ async fn handle_list_resources(
|
|||
&call_id,
|
||||
invocation,
|
||||
duration,
|
||||
Ok(call_tool_result_from_content(&content, *success)),
|
||||
Ok(call_tool_result_from_content(&content, output_text.success)),
|
||||
)
|
||||
.await;
|
||||
Ok(output)
|
||||
|
|
@ -351,7 +353,7 @@ async fn handle_list_resource_templates(
|
|||
turn: Arc<TurnContext>,
|
||||
call_id: String,
|
||||
arguments: Option<Value>,
|
||||
) -> Result<ToolOutput, FunctionCallError> {
|
||||
) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
let args: ListResourceTemplatesArgs = parse_args_with_default(arguments.clone())?;
|
||||
let ListResourceTemplatesArgs { server, cursor } = args;
|
||||
let server = normalize_optional_string(server);
|
||||
|
|
@ -406,10 +408,12 @@ async fn handle_list_resource_templates(
|
|||
match payload_result {
|
||||
Ok(payload) => match serialize_function_output(payload) {
|
||||
Ok(output) => {
|
||||
let ToolOutput::Function { body, success } = &output else {
|
||||
unreachable!("MCP resource handler should return function output");
|
||||
let Some(output_text) =
|
||||
(&*output as &dyn std::any::Any).downcast_ref::<TextToolOutput>()
|
||||
else {
|
||||
unreachable!("MCP resource handler should return text output");
|
||||
};
|
||||
let content = body.to_text().unwrap_or_default();
|
||||
let content = output_text.text.clone();
|
||||
let duration = start.elapsed();
|
||||
emit_tool_call_end(
|
||||
&session,
|
||||
|
|
@ -417,7 +421,7 @@ async fn handle_list_resource_templates(
|
|||
&call_id,
|
||||
invocation,
|
||||
duration,
|
||||
Ok(call_tool_result_from_content(&content, *success)),
|
||||
Ok(call_tool_result_from_content(&content, output_text.success)),
|
||||
)
|
||||
.await;
|
||||
Ok(output)
|
||||
|
|
@ -459,7 +463,7 @@ async fn handle_read_resource(
|
|||
turn: Arc<TurnContext>,
|
||||
call_id: String,
|
||||
arguments: Option<Value>,
|
||||
) -> Result<ToolOutput, FunctionCallError> {
|
||||
) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
let args: ReadResourceArgs = parse_args(arguments.clone())?;
|
||||
let ReadResourceArgs { server, uri } = args;
|
||||
let server = normalize_required_string("server", server)?;
|
||||
|
|
@ -499,10 +503,12 @@ async fn handle_read_resource(
|
|||
match payload_result {
|
||||
Ok(payload) => match serialize_function_output(payload) {
|
||||
Ok(output) => {
|
||||
let ToolOutput::Function { body, success } = &output else {
|
||||
unreachable!("MCP resource handler should return function output");
|
||||
let Some(output_text) =
|
||||
(&*output as &dyn std::any::Any).downcast_ref::<TextToolOutput>()
|
||||
else {
|
||||
unreachable!("MCP resource handler should return text output");
|
||||
};
|
||||
let content = body.to_text().unwrap_or_default();
|
||||
let content = output_text.text.clone();
|
||||
let duration = start.elapsed();
|
||||
emit_tool_call_end(
|
||||
&session,
|
||||
|
|
@ -510,7 +516,7 @@ async fn handle_read_resource(
|
|||
&call_id,
|
||||
invocation,
|
||||
duration,
|
||||
Ok(call_tool_result_from_content(&content, *success)),
|
||||
Ok(call_tool_result_from_content(&content, output_text.success)),
|
||||
)
|
||||
.await;
|
||||
Ok(output)
|
||||
|
|
@ -614,7 +620,7 @@ fn normalize_required_string(field: &str, value: String) -> Result<String, Funct
|
|||
}
|
||||
}
|
||||
|
||||
fn serialize_function_output<T>(payload: T) -> Result<ToolOutput, FunctionCallError>
|
||||
fn serialize_function_output<T>(payload: T) -> Result<ToolOutputBox, FunctionCallError>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
|
|
@ -624,10 +630,10 @@ where
|
|||
))
|
||||
})?;
|
||||
|
||||
Ok(ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(content),
|
||||
Ok(Box::new(TextToolOutput {
|
||||
text: content,
|
||||
success: Some(true),
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
fn parse_arguments(raw_args: &str) -> Result<Option<Value>, FunctionCallError> {
|
||||
|
|
|
|||
|
|
@ -13,8 +13,9 @@ use crate::config::Config;
|
|||
use crate::error::CodexErr;
|
||||
use crate::features::Feature;
|
||||
use crate::function_tool::FunctionCallError;
|
||||
use crate::tools::context::TextToolOutput;
|
||||
use crate::tools::context::ToolInvocation;
|
||||
use crate::tools::context::ToolOutput;
|
||||
use crate::tools::context::ToolOutputBox;
|
||||
use crate::tools::context::ToolPayload;
|
||||
use crate::tools::handlers::parse_arguments;
|
||||
use crate::tools::registry::ToolHandler;
|
||||
|
|
@ -22,7 +23,6 @@ use crate::tools::registry::ToolKind;
|
|||
use async_trait::async_trait;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::models::BaseInstructions;
|
||||
use codex_protocol::models::FunctionCallOutputBody;
|
||||
use codex_protocol::protocol::CollabAgentInteractionBeginEvent;
|
||||
use codex_protocol::protocol::CollabAgentInteractionEndEvent;
|
||||
use codex_protocol::protocol::CollabAgentRef;
|
||||
|
|
@ -65,7 +65,7 @@ impl ToolHandler for MultiAgentHandler {
|
|||
matches!(payload, ToolPayload::Function { .. })
|
||||
}
|
||||
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutput, FunctionCallError> {
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
let ToolInvocation {
|
||||
session,
|
||||
turn,
|
||||
|
|
@ -127,7 +127,7 @@ mod spawn {
|
|||
turn: Arc<TurnContext>,
|
||||
call_id: String,
|
||||
arguments: String,
|
||||
) -> Result<ToolOutput, FunctionCallError> {
|
||||
) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
let args: SpawnAgentArgs = parse_arguments(&arguments)?;
|
||||
let role_name = args
|
||||
.agent_type
|
||||
|
|
@ -225,10 +225,10 @@ mod spawn {
|
|||
FunctionCallError::Fatal(format!("failed to serialize spawn_agent result: {err}"))
|
||||
})?;
|
||||
|
||||
Ok(ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(content),
|
||||
Ok(Box::new(TextToolOutput {
|
||||
text: content,
|
||||
success: Some(true),
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -255,7 +255,7 @@ mod send_input {
|
|||
turn: Arc<TurnContext>,
|
||||
call_id: String,
|
||||
arguments: String,
|
||||
) -> Result<ToolOutput, FunctionCallError> {
|
||||
) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
let args: SendInputArgs = parse_arguments(&arguments)?;
|
||||
let receiver_thread_id = agent_id(&args.id)?;
|
||||
let input_items = parse_collab_input(args.message, args.items)?;
|
||||
|
|
@ -318,10 +318,10 @@ mod send_input {
|
|||
FunctionCallError::Fatal(format!("failed to serialize send_input result: {err}"))
|
||||
})?;
|
||||
|
||||
Ok(ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(content),
|
||||
Ok(Box::new(TextToolOutput {
|
||||
text: content,
|
||||
success: Some(true),
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -345,7 +345,7 @@ mod resume_agent {
|
|||
turn: Arc<TurnContext>,
|
||||
call_id: String,
|
||||
arguments: String,
|
||||
) -> Result<ToolOutput, FunctionCallError> {
|
||||
) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
let args: ResumeAgentArgs = parse_arguments(&arguments)?;
|
||||
let receiver_thread_id = agent_id(&args.id)?;
|
||||
let (receiver_agent_nickname, receiver_agent_role) = session
|
||||
|
|
@ -432,10 +432,10 @@ mod resume_agent {
|
|||
FunctionCallError::Fatal(format!("failed to serialize resume_agent result: {err}"))
|
||||
})?;
|
||||
|
||||
Ok(ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(content),
|
||||
Ok(Box::new(TextToolOutput {
|
||||
text: content,
|
||||
success: Some(true),
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
async fn try_resume_closed_agent(
|
||||
|
|
@ -495,7 +495,7 @@ pub(crate) mod wait {
|
|||
turn: Arc<TurnContext>,
|
||||
call_id: String,
|
||||
arguments: String,
|
||||
) -> Result<ToolOutput, FunctionCallError> {
|
||||
) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
let args: WaitArgs = parse_arguments(&arguments)?;
|
||||
if args.ids.is_empty() {
|
||||
return Err(FunctionCallError::RespondToModel(
|
||||
|
|
@ -645,10 +645,10 @@ pub(crate) mod wait {
|
|||
FunctionCallError::Fatal(format!("failed to serialize wait result: {err}"))
|
||||
})?;
|
||||
|
||||
Ok(ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(content),
|
||||
Ok(Box::new(TextToolOutput {
|
||||
text: content,
|
||||
success: None,
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
async fn wait_for_final_status(
|
||||
|
|
@ -688,7 +688,7 @@ pub mod close_agent {
|
|||
turn: Arc<TurnContext>,
|
||||
call_id: String,
|
||||
arguments: String,
|
||||
) -> Result<ToolOutput, FunctionCallError> {
|
||||
) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
let args: CloseAgentArgs = parse_arguments(&arguments)?;
|
||||
let agent_id = agent_id(&args.id)?;
|
||||
let (receiver_agent_nickname, receiver_agent_role) = session
|
||||
|
|
@ -765,10 +765,10 @@ pub mod close_agent {
|
|||
FunctionCallError::Fatal(format!("failed to serialize close_agent result: {err}"))
|
||||
})?;
|
||||
|
||||
Ok(ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(content),
|
||||
Ok(Box::new(TextToolOutput {
|
||||
text: content,
|
||||
success: Some(true),
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -993,6 +993,8 @@ mod tests {
|
|||
use crate::protocol::SandboxPolicy;
|
||||
use crate::protocol::SessionSource;
|
||||
use crate::protocol::SubAgentSource;
|
||||
use crate::tools::context::TextToolOutput;
|
||||
use crate::tools::context::ToolOutputBox;
|
||||
use crate::turn_diff_tracker::TurnDiffTracker;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::models::ContentItem;
|
||||
|
|
@ -1038,6 +1040,13 @@ mod tests {
|
|||
)
|
||||
}
|
||||
|
||||
fn expect_text_output(output: ToolOutputBox) -> (String, Option<bool>) {
|
||||
let output = (&*output as &dyn std::any::Any)
|
||||
.downcast_ref::<TextToolOutput>()
|
||||
.expect("expected text output");
|
||||
(output.text.clone(), output.success)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn handler_rejects_non_function_payloads() {
|
||||
let (session, turn) = make_session_and_context().await;
|
||||
|
|
@ -1160,13 +1169,7 @@ mod tests {
|
|||
.handle(invocation)
|
||||
.await
|
||||
.expect("spawn_agent should succeed");
|
||||
let ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(content),
|
||||
..
|
||||
} = output
|
||||
else {
|
||||
panic!("expected function output");
|
||||
};
|
||||
let (content, _) = expect_text_output(output);
|
||||
let result: SpawnAgentResult =
|
||||
serde_json::from_str(&content).expect("spawn_agent result should be json");
|
||||
let agent_id = agent_id(&result.agent_id).expect("agent_id should be valid");
|
||||
|
|
@ -1259,13 +1262,7 @@ mod tests {
|
|||
.handle(invocation)
|
||||
.await
|
||||
.expect("spawn_agent should succeed");
|
||||
let ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(content),
|
||||
..
|
||||
} = output
|
||||
else {
|
||||
panic!("expected function output");
|
||||
};
|
||||
let (content, _) = expect_text_output(output);
|
||||
let result: SpawnAgentResult =
|
||||
serde_json::from_str(&content).expect("spawn_agent result should be json");
|
||||
let agent_id = agent_id(&result.agent_id).expect("agent_id should be valid");
|
||||
|
|
@ -1349,14 +1346,7 @@ mod tests {
|
|||
.handle(invocation)
|
||||
.await
|
||||
.expect("spawn should succeed within configured depth");
|
||||
let ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(content),
|
||||
success,
|
||||
..
|
||||
} = output
|
||||
else {
|
||||
panic!("expected function output");
|
||||
};
|
||||
let (content, success) = expect_text_output(output);
|
||||
let result: SpawnAgentResult =
|
||||
serde_json::from_str(&content).expect("spawn_agent result should be json");
|
||||
assert!(!result.agent_id.is_empty());
|
||||
|
|
@ -1601,14 +1591,7 @@ mod tests {
|
|||
.handle(invocation)
|
||||
.await
|
||||
.expect("resume_agent should succeed");
|
||||
let ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(content),
|
||||
success,
|
||||
..
|
||||
} = output
|
||||
else {
|
||||
panic!("expected function output");
|
||||
};
|
||||
let (content, success) = expect_text_output(output);
|
||||
let result: resume_agent::ResumeAgentResult =
|
||||
serde_json::from_str(&content).expect("resume_agent result should be json");
|
||||
assert_eq!(result.status, status_before);
|
||||
|
|
@ -1670,14 +1653,7 @@ mod tests {
|
|||
.handle(resume_invocation)
|
||||
.await
|
||||
.expect("resume_agent should succeed");
|
||||
let ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(content),
|
||||
success,
|
||||
..
|
||||
} = output
|
||||
else {
|
||||
panic!("expected function output");
|
||||
};
|
||||
let (content, success) = expect_text_output(output);
|
||||
let result: resume_agent::ResumeAgentResult =
|
||||
serde_json::from_str(&content).expect("resume_agent result should be json");
|
||||
assert_ne!(result.status, AgentStatus::NotFound);
|
||||
|
|
@ -1693,14 +1669,7 @@ mod tests {
|
|||
.handle(send_invocation)
|
||||
.await
|
||||
.expect("send_input should succeed after resume");
|
||||
let ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(content),
|
||||
success,
|
||||
..
|
||||
} = output
|
||||
else {
|
||||
panic!("expected function output");
|
||||
};
|
||||
let (content, success) = expect_text_output(output);
|
||||
let result: serde_json::Value =
|
||||
serde_json::from_str(&content).expect("send_input result should be json");
|
||||
let submission_id = result
|
||||
|
|
@ -1825,14 +1794,7 @@ mod tests {
|
|||
.handle(invocation)
|
||||
.await
|
||||
.expect("wait should succeed");
|
||||
let ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(content),
|
||||
success,
|
||||
..
|
||||
} = output
|
||||
else {
|
||||
panic!("expected function output");
|
||||
};
|
||||
let (content, success) = expect_text_output(output);
|
||||
let result: wait::WaitResult =
|
||||
serde_json::from_str(&content).expect("wait result should be json");
|
||||
assert_eq!(
|
||||
|
|
@ -1869,14 +1831,7 @@ mod tests {
|
|||
.handle(invocation)
|
||||
.await
|
||||
.expect("wait should succeed");
|
||||
let ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(content),
|
||||
success,
|
||||
..
|
||||
} = output
|
||||
else {
|
||||
panic!("expected function output");
|
||||
};
|
||||
let (content, success) = expect_text_output(output);
|
||||
let result: wait::WaitResult =
|
||||
serde_json::from_str(&content).expect("wait result should be json");
|
||||
assert_eq!(
|
||||
|
|
@ -1966,14 +1921,7 @@ mod tests {
|
|||
.handle(invocation)
|
||||
.await
|
||||
.expect("wait should succeed");
|
||||
let ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(content),
|
||||
success,
|
||||
..
|
||||
} = output
|
||||
else {
|
||||
panic!("expected function output");
|
||||
};
|
||||
let (content, success) = expect_text_output(output);
|
||||
let result: wait::WaitResult =
|
||||
serde_json::from_str(&content).expect("wait result should be json");
|
||||
assert_eq!(
|
||||
|
|
@ -2006,14 +1954,7 @@ mod tests {
|
|||
.handle(invocation)
|
||||
.await
|
||||
.expect("close_agent should succeed");
|
||||
let ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(content),
|
||||
success,
|
||||
..
|
||||
} = output
|
||||
else {
|
||||
panic!("expected function output");
|
||||
};
|
||||
let (content, success) = expect_text_output(output);
|
||||
let result: close_agent::CloseAgentResult =
|
||||
serde_json::from_str(&content).expect("close_agent result should be json");
|
||||
assert_eq!(result.status, status_before);
|
||||
|
|
|
|||
|
|
@ -3,15 +3,15 @@ use crate::client_common::tools::ToolSpec;
|
|||
use crate::codex::Session;
|
||||
use crate::codex::TurnContext;
|
||||
use crate::function_tool::FunctionCallError;
|
||||
use crate::tools::context::TextToolOutput;
|
||||
use crate::tools::context::ToolInvocation;
|
||||
use crate::tools::context::ToolOutput;
|
||||
use crate::tools::context::ToolOutputBox;
|
||||
use crate::tools::context::ToolPayload;
|
||||
use crate::tools::registry::ToolHandler;
|
||||
use crate::tools::registry::ToolKind;
|
||||
use crate::tools::spec::JsonSchema;
|
||||
use async_trait::async_trait;
|
||||
use codex_protocol::config_types::ModeKind;
|
||||
use codex_protocol::models::FunctionCallOutputBody;
|
||||
use codex_protocol::plan_tool::UpdatePlanArgs;
|
||||
use codex_protocol::protocol::EventMsg;
|
||||
use std::collections::BTreeMap;
|
||||
|
|
@ -67,7 +67,7 @@ impl ToolHandler for PlanHandler {
|
|||
ToolKind::Function
|
||||
}
|
||||
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutput, FunctionCallError> {
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
let ToolInvocation {
|
||||
session,
|
||||
turn,
|
||||
|
|
@ -88,10 +88,10 @@ impl ToolHandler for PlanHandler {
|
|||
let content =
|
||||
handle_update_plan(session.as_ref(), turn.as_ref(), arguments, call_id).await?;
|
||||
|
||||
Ok(ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(content),
|
||||
Ok(Box::new(TextToolOutput {
|
||||
text: content,
|
||||
success: Some(true),
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
use codex_protocol::models::FunctionCallOutputBody;
|
||||
use std::collections::VecDeque;
|
||||
use std::path::PathBuf;
|
||||
|
||||
|
|
@ -7,8 +6,9 @@ use codex_utils_string::take_bytes_at_char_boundary;
|
|||
use serde::Deserialize;
|
||||
|
||||
use crate::function_tool::FunctionCallError;
|
||||
use crate::tools::context::TextToolOutput;
|
||||
use crate::tools::context::ToolInvocation;
|
||||
use crate::tools::context::ToolOutput;
|
||||
use crate::tools::context::ToolOutputBox;
|
||||
use crate::tools::context::ToolPayload;
|
||||
use crate::tools::handlers::parse_arguments;
|
||||
use crate::tools::registry::ToolHandler;
|
||||
|
|
@ -98,7 +98,7 @@ impl ToolHandler for ReadFileHandler {
|
|||
ToolKind::Function
|
||||
}
|
||||
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutput, FunctionCallError> {
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
let ToolInvocation { payload, .. } = invocation;
|
||||
|
||||
let arguments = match payload {
|
||||
|
|
@ -146,10 +146,10 @@ impl ToolHandler for ReadFileHandler {
|
|||
indentation::read_block(&path, offset, limit, indentation).await?
|
||||
}
|
||||
};
|
||||
Ok(ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(collected.join("\n")),
|
||||
Ok(Box::new(TextToolOutput {
|
||||
text: collected.join("\n"),
|
||||
success: Some(true),
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use async_trait::async_trait;
|
||||
use codex_protocol::models::FunctionCallOutputBody;
|
||||
use codex_protocol::request_permissions::RequestPermissionsArgs;
|
||||
|
||||
use crate::function_tool::FunctionCallError;
|
||||
use crate::sandboxing::normalize_additional_permissions;
|
||||
use crate::tools::context::TextToolOutput;
|
||||
use crate::tools::context::ToolInvocation;
|
||||
use crate::tools::context::ToolOutput;
|
||||
use crate::tools::context::ToolOutputBox;
|
||||
use crate::tools::context::ToolPayload;
|
||||
use crate::tools::handlers::parse_arguments_with_base_path;
|
||||
use crate::tools::registry::ToolHandler;
|
||||
|
|
@ -24,7 +24,7 @@ impl ToolHandler for RequestPermissionsHandler {
|
|||
ToolKind::Function
|
||||
}
|
||||
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutput, FunctionCallError> {
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
let ToolInvocation {
|
||||
session,
|
||||
turn,
|
||||
|
|
@ -67,9 +67,9 @@ impl ToolHandler for RequestPermissionsHandler {
|
|||
))
|
||||
})?;
|
||||
|
||||
Ok(ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(content),
|
||||
Ok(Box::new(TextToolOutput {
|
||||
text: content,
|
||||
success: Some(true),
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
use async_trait::async_trait;
|
||||
use codex_protocol::models::FunctionCallOutputBody;
|
||||
|
||||
use crate::function_tool::FunctionCallError;
|
||||
use crate::tools::context::TextToolOutput;
|
||||
use crate::tools::context::ToolInvocation;
|
||||
use crate::tools::context::ToolOutput;
|
||||
use crate::tools::context::ToolOutputBox;
|
||||
use crate::tools::context::ToolPayload;
|
||||
use crate::tools::handlers::parse_arguments;
|
||||
use crate::tools::registry::ToolHandler;
|
||||
use crate::tools::registry::ToolKind;
|
||||
use async_trait::async_trait;
|
||||
use codex_protocol::config_types::ModeKind;
|
||||
use codex_protocol::config_types::TUI_VISIBLE_COLLABORATION_MODES;
|
||||
use codex_protocol::request_user_input::RequestUserInputArgs;
|
||||
|
|
@ -63,7 +62,7 @@ impl ToolHandler for RequestUserInputHandler {
|
|||
ToolKind::Function
|
||||
}
|
||||
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutput, FunctionCallError> {
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
let ToolInvocation {
|
||||
session,
|
||||
turn,
|
||||
|
|
@ -116,10 +115,10 @@ impl ToolHandler for RequestUserInputHandler {
|
|||
))
|
||||
})?;
|
||||
|
||||
Ok(ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(content),
|
||||
Ok(Box::new(TextToolOutput {
|
||||
text: content,
|
||||
success: Some(true),
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ use bm25::Document;
|
|||
use bm25::Language;
|
||||
use bm25::SearchEngineBuilder;
|
||||
use codex_app_server_protocol::AppInfo;
|
||||
use codex_protocol::models::FunctionCallOutputBody;
|
||||
use serde::Deserialize;
|
||||
use serde_json::json;
|
||||
use std::collections::HashMap;
|
||||
|
|
@ -13,8 +12,9 @@ use crate::connectors;
|
|||
use crate::function_tool::FunctionCallError;
|
||||
use crate::mcp::CODEX_APPS_MCP_SERVER_NAME;
|
||||
use crate::mcp_connection_manager::ToolInfo;
|
||||
use crate::tools::context::TextToolOutput;
|
||||
use crate::tools::context::ToolInvocation;
|
||||
use crate::tools::context::ToolOutput;
|
||||
use crate::tools::context::ToolOutputBox;
|
||||
use crate::tools::context::ToolPayload;
|
||||
use crate::tools::handlers::parse_arguments;
|
||||
use crate::tools::registry::ToolHandler;
|
||||
|
|
@ -78,7 +78,7 @@ impl ToolHandler for SearchToolBm25Handler {
|
|||
ToolKind::Function
|
||||
}
|
||||
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutput, FunctionCallError> {
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
let ToolInvocation {
|
||||
payload,
|
||||
session,
|
||||
|
|
@ -141,10 +141,10 @@ impl ToolHandler for SearchToolBm25Handler {
|
|||
"tools": [],
|
||||
})
|
||||
.to_string();
|
||||
return Ok(ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(content),
|
||||
return Ok(Box::new(TextToolOutput {
|
||||
text: content,
|
||||
success: Some(true),
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
let documents: Vec<Document<usize>> = entries
|
||||
|
|
@ -184,10 +184,10 @@ impl ToolHandler for SearchToolBm25Handler {
|
|||
})
|
||||
.to_string();
|
||||
|
||||
Ok(ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(content),
|
||||
Ok(Box::new(TextToolOutput {
|
||||
text: content,
|
||||
success: Some(true),
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
use async_trait::async_trait;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::models::FunctionCallOutputBody;
|
||||
use codex_protocol::models::ShellCommandToolCallParams;
|
||||
use codex_protocol::models::ShellToolCallParams;
|
||||
use std::sync::Arc;
|
||||
|
|
@ -15,8 +14,9 @@ use crate::is_safe_command::is_known_safe_command;
|
|||
use crate::protocol::ExecCommandSource;
|
||||
use crate::shell::Shell;
|
||||
use crate::skills::maybe_emit_implicit_skill_invocation;
|
||||
use crate::tools::context::TextToolOutput;
|
||||
use crate::tools::context::ToolInvocation;
|
||||
use crate::tools::context::ToolOutput;
|
||||
use crate::tools::context::ToolOutputBox;
|
||||
use crate::tools::context::ToolPayload;
|
||||
use crate::tools::events::ToolEmitter;
|
||||
use crate::tools::events::ToolEventCtx;
|
||||
|
|
@ -165,7 +165,7 @@ impl ToolHandler for ShellHandler {
|
|||
}
|
||||
}
|
||||
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutput, FunctionCallError> {
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
let ToolInvocation {
|
||||
session,
|
||||
turn,
|
||||
|
|
@ -253,7 +253,7 @@ impl ToolHandler for ShellCommandHandler {
|
|||
.unwrap_or(true)
|
||||
}
|
||||
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutput, FunctionCallError> {
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
let ToolInvocation {
|
||||
session,
|
||||
turn,
|
||||
|
|
@ -305,7 +305,7 @@ impl ToolHandler for ShellCommandHandler {
|
|||
}
|
||||
|
||||
impl ShellHandler {
|
||||
async fn run_exec_like(args: RunExecLikeArgs) -> Result<ToolOutput, FunctionCallError> {
|
||||
async fn run_exec_like(args: RunExecLikeArgs) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
let RunExecLikeArgs {
|
||||
tool_name,
|
||||
exec_params,
|
||||
|
|
@ -449,10 +449,10 @@ impl ShellHandler {
|
|||
.map(|result| result.output);
|
||||
let event_ctx = ToolEventCtx::new(session.as_ref(), turn.as_ref(), &call_id, None);
|
||||
let content = emitter.finish(event_ctx, out).await?;
|
||||
Ok(ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(content),
|
||||
Ok(Box::new(TextToolOutput {
|
||||
text: content,
|
||||
success: Some(true),
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
use codex_protocol::models::FunctionCallOutputBody;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::sync::Arc;
|
||||
|
|
@ -11,8 +10,9 @@ use tokio::sync::Barrier;
|
|||
use tokio::time::sleep;
|
||||
|
||||
use crate::function_tool::FunctionCallError;
|
||||
use crate::tools::context::TextToolOutput;
|
||||
use crate::tools::context::ToolInvocation;
|
||||
use crate::tools::context::ToolOutput;
|
||||
use crate::tools::context::ToolOutputBox;
|
||||
use crate::tools::context::ToolPayload;
|
||||
use crate::tools::handlers::parse_arguments;
|
||||
use crate::tools::registry::ToolHandler;
|
||||
|
|
@ -61,7 +61,7 @@ impl ToolHandler for TestSyncHandler {
|
|||
ToolKind::Function
|
||||
}
|
||||
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutput, FunctionCallError> {
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
let ToolInvocation { payload, .. } = invocation;
|
||||
|
||||
let arguments = match payload {
|
||||
|
|
@ -91,10 +91,10 @@ impl ToolHandler for TestSyncHandler {
|
|||
sleep(Duration::from_millis(delay)).await;
|
||||
}
|
||||
|
||||
Ok(ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text("ok".to_string()),
|
||||
Ok(Box::new(TextToolOutput {
|
||||
text: "ok".to_string(),
|
||||
success: Some(true),
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,8 +7,9 @@ use crate::sandboxing::SandboxPermissions;
|
|||
use crate::shell::Shell;
|
||||
use crate::shell::get_shell_by_model_provided_path;
|
||||
use crate::skills::maybe_emit_implicit_skill_invocation;
|
||||
use crate::tools::context::TextToolOutput;
|
||||
use crate::tools::context::ToolInvocation;
|
||||
use crate::tools::context::ToolOutput;
|
||||
use crate::tools::context::ToolOutputBox;
|
||||
use crate::tools::context::ToolPayload;
|
||||
use crate::tools::handlers::apply_granted_turn_permissions;
|
||||
use crate::tools::handlers::apply_patch::intercept_apply_patch;
|
||||
|
|
@ -24,7 +25,6 @@ use crate::unified_exec::UnifiedExecProcessManager;
|
|||
use crate::unified_exec::UnifiedExecResponse;
|
||||
use crate::unified_exec::WriteStdinRequest;
|
||||
use async_trait::async_trait;
|
||||
use codex_protocol::models::FunctionCallOutputBody;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use serde::Deserialize;
|
||||
use std::path::PathBuf;
|
||||
|
|
@ -114,7 +114,7 @@ impl ToolHandler for UnifiedExecHandler {
|
|||
!is_known_safe_command(&command)
|
||||
}
|
||||
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutput, FunctionCallError> {
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
let ToolInvocation {
|
||||
session,
|
||||
turn,
|
||||
|
|
@ -291,10 +291,10 @@ impl ToolHandler for UnifiedExecHandler {
|
|||
|
||||
let content = format_response(&response);
|
||||
|
||||
Ok(ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::Text(content),
|
||||
Ok(Box::new(TextToolOutput {
|
||||
text: content,
|
||||
success: Some(true),
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
use async_trait::async_trait;
|
||||
use codex_protocol::models::ContentItem;
|
||||
use codex_protocol::models::FunctionCallOutputBody;
|
||||
use codex_protocol::models::FunctionCallOutputContentItem;
|
||||
use codex_protocol::models::ImageDetail;
|
||||
use codex_protocol::models::local_image_content_items_with_label_number;
|
||||
|
|
@ -13,8 +12,9 @@ use crate::features::Feature;
|
|||
use crate::function_tool::FunctionCallError;
|
||||
use crate::protocol::EventMsg;
|
||||
use crate::protocol::ViewImageToolCallEvent;
|
||||
use crate::tools::context::ContentToolOutput;
|
||||
use crate::tools::context::ToolInvocation;
|
||||
use crate::tools::context::ToolOutput;
|
||||
use crate::tools::context::ToolOutputBox;
|
||||
use crate::tools::context::ToolPayload;
|
||||
use crate::tools::handlers::parse_arguments;
|
||||
use crate::tools::registry::ToolHandler;
|
||||
|
|
@ -36,7 +36,7 @@ impl ToolHandler for ViewImageHandler {
|
|||
ToolKind::Function
|
||||
}
|
||||
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutput, FunctionCallError> {
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutputBox, FunctionCallError> {
|
||||
if !invocation
|
||||
.turn
|
||||
.model_info
|
||||
|
|
@ -121,9 +121,9 @@ impl ToolHandler for ViewImageHandler {
|
|||
)
|
||||
.await;
|
||||
|
||||
Ok(ToolOutput::Function {
|
||||
body: FunctionCallOutputBody::ContentItems(content),
|
||||
Ok(Box::new(ContentToolOutput {
|
||||
content,
|
||||
success: Some(true),
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use crate::memories::usage::emit_metric_for_tool_read;
|
|||
use crate::protocol::SandboxPolicy;
|
||||
use crate::sandbox_tags::sandbox_tag;
|
||||
use crate::tools::context::ToolInvocation;
|
||||
use crate::tools::context::ToolOutput;
|
||||
use crate::tools::context::ToolOutputBox;
|
||||
use crate::tools::context::ToolPayload;
|
||||
use async_trait::async_trait;
|
||||
use codex_hooks::HookEvent;
|
||||
|
|
@ -52,7 +52,7 @@ pub trait ToolHandler: Send + Sync {
|
|||
|
||||
/// Perform the actual [ToolInvocation] and returns a [ToolOutput] containing
|
||||
/// the final output to return to the model.
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutput, FunctionCallError>;
|
||||
async fn handle(&self, invocation: ToolInvocation) -> Result<ToolOutputBox, FunctionCallError>;
|
||||
}
|
||||
|
||||
pub struct ToolRegistry {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue