Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 127 additions & 1 deletion code-rs/core/src/codex/streaming.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ use crate::protocol::McpListToolsResponseEvent;
use crate::protocol::TaskLifecycleEvent;
use crate::protocol::TaskLifecyclePhase;
use crate::protocol::TaskOriginKind;
use crate::review_store::{AutoReviewLedgerOptions, AutoReviewRunStore};
use crate::review_store::{
default_auto_review_detail_max_bytes, hard_auto_review_detail_max_bytes,
AutoReviewLedgerOptions, AutoReviewRunStore,
};
use code_app_server_protocol::AuthMode as AppAuthMode;
use code_protocol::models::ContentItem;
use code_protocol::models::ResponseItem;
Expand Down Expand Up @@ -5073,6 +5076,7 @@ async fn handle_function_call(
"gh_run_wait" => handle_gh_run_wait(sess, &ctx, arguments).await,
"kill" => handle_kill(sess, &ctx, arguments).await,
"code_bridge" | "code_bridge_subscription" => handle_code_bridge(sess, &ctx, arguments).await,
"auto_review_detail" => handle_auto_review_detail(sess, &ctx, arguments).await,
TOOL_SEARCH_TOOL_NAME | LEGACY_SEARCH_TOOL_BM25_TOOL_NAME => {
let arguments = match serde_json::from_str::<serde_json::Value>(&arguments) {
Ok(arguments) => arguments,
Expand Down Expand Up @@ -5360,6 +5364,128 @@ async fn handle_declare_worktree_decision(
}
}

#[derive(Debug, serde::Deserialize)]
struct AutoReviewDetailArgs {
run_id: String,
#[serde(default)]
finding_id: Option<String>,
#[serde(default)]
max_bytes: Option<usize>,
}

async fn handle_auto_review_detail(
sess: &Session,
ctx: &ToolCallCtx,
arguments: String,
) -> ResponseInputItem {
let args = match serde_json::from_str::<AutoReviewDetailArgs>(&arguments) {
Ok(args) => args,
Err(err) => {
return ResponseInputItem::FunctionCallOutput {
call_id: ctx.call_id.clone(),
output: FunctionCallOutputPayload {
body: code_protocol::models::FunctionCallOutputBody::Text(
serde_json::json!({
"success": false,
"error": {
"code": "invalid_arguments",
"message": format!("invalid auto_review_detail arguments: {err}"),
}
})
.to_string(),
),
success: Some(false),
},
};
}
};
let run_id = match Uuid::parse_str(&args.run_id) {
Ok(run_id) => run_id,
Err(err) => {
return ResponseInputItem::FunctionCallOutput {
call_id: ctx.call_id.clone(),
output: FunctionCallOutputPayload {
body: code_protocol::models::FunctionCallOutputBody::Text(
serde_json::json!({
"success": false,
"error": {
"code": "invalid_run_id",
"run_id": args.run_id,
"message": format!("invalid auto review run_id: {err}"),
}
})
.to_string(),
),
success: Some(false),
},
};
}
};
let cwd = sess.cwd.clone();
let finding_id = args.finding_id.clone();
let max_bytes = args.max_bytes;
let lookup = tokio::task::spawn_blocking(move || {
let store = AutoReviewRunStore::open_existing_read_only(&cwd).map_err(|err| {
serde_json::json!({
"code": "store_unavailable",
"message": format!("failed to open auto review run store: {err}"),
})
})?;
let Some(store) = store else {
return Err(serde_json::json!({
"code": "store_missing",
"message": "auto review run store is not available for this workspace",
}));
};
store
.read_detail(run_id, finding_id.as_deref(), max_bytes)
.map_err(|err| serde_json::to_value(err).unwrap_or_else(|ser_err| {
serde_json::json!({
"code": "detail_error",
"message": format!("failed to serialize auto review detail error: {ser_err}"),
})
}))
})
.await;

let body = match lookup {
Ok(Ok(detail)) => serde_json::json!({
"success": true,
"detail": detail,
"defaults": {
"default_max_bytes": default_auto_review_detail_max_bytes(),
"hard_max_bytes": hard_auto_review_detail_max_bytes(),
}
}),
Ok(Err(error)) => serde_json::json!({
"success": false,
"error": error,
"defaults": {
"default_max_bytes": default_auto_review_detail_max_bytes(),
"hard_max_bytes": hard_auto_review_detail_max_bytes(),
}
}),
Err(err) => serde_json::json!({
"success": false,
"error": {
"code": "lookup_join_error",
"message": format!("auto review detail lookup task failed: {err}"),
}
}),
};
let success = body
.get("success")
.and_then(serde_json::Value::as_bool)
.unwrap_or(false);
ResponseInputItem::FunctionCallOutput {
call_id: ctx.call_id.clone(),
output: FunctionCallOutputPayload {
body: code_protocol::models::FunctionCallOutputBody::Text(body.to_string()),
success: Some(success),
},
}
}

async fn handle_dynamic_tool_call(
sess: &Session,
ctx: &ToolCallCtx,
Expand Down
82 changes: 82 additions & 0 deletions code-rs/core/src/openai_tools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1308,6 +1308,7 @@ pub fn get_openai_tools(
tools.push(create_kill_tool());
tools.push(create_gh_run_wait_tool());
tools.push(create_bridge_tool());
tools.push(create_auto_review_detail_tool());

if config.web_search_request {
let search_content_types = match config.web_search_tool_type {
Expand Down Expand Up @@ -1501,6 +1502,49 @@ pub fn create_gh_run_wait_tool() -> OpenAiTool {
})
}

pub fn create_auto_review_detail_tool() -> OpenAiTool {
let mut properties = BTreeMap::new();
properties.insert(
"run_id".to_string(),
JsonSchema::String {
description: Some(
"Stable Auto Review run id from the auto_review_ledger.".to_string(),
),
allowed_values: None,
},
);
properties.insert(
"finding_id".to_string(),
JsonSchema::String {
description: Some(
"Optional stable finding id such as f1 or f2. If omitted, returns bounded whole-run detail."
.to_string(),
),
allowed_values: None,
},
);
properties.insert(
"max_bytes".to_string(),
JsonSchema::Number {
description: Some(
"Optional maximum response size in bytes; clamped to the built-in safe limit."
.to_string(),
),
},
);
OpenAiTool::Function(ResponsesApiTool {
name: "auto_review_detail".to_string(),
description: "Retrieve bounded Auto Review run or finding details from durable sidecars using run ids shown in the Auto Review ledger. Read-only; does not change review disposition."
.to_string(),
strict: false,
parameters: JsonSchema::Object {
properties,
required: Some(vec!["run_id".to_string()]),
additional_properties: Some(false.into()),
},
})
}

pub fn create_bridge_tool() -> OpenAiTool {
let mut properties = BTreeMap::new();

Expand Down Expand Up @@ -1644,11 +1688,40 @@ mod tests {
"kill",
"gh_run_wait",
"code_bridge",
"auto_review_detail",
"web_search",
],
);
}

#[test]
fn auto_review_detail_tool_requires_only_run_id() {
let tool = create_auto_review_detail_tool();
let OpenAiTool::Function(ResponsesApiTool {
name,
parameters,
..
}) = tool
else {
panic!("expected function tool");
};

assert_eq!(name, "auto_review_detail");
let JsonSchema::Object {
properties,
required,
additional_properties,
} = parameters
else {
panic!("expected object parameters");
};
assert_eq!(required, Some(vec!["run_id".to_string()]));
assert_eq!(additional_properties, Some(false.into()));
assert!(properties.contains_key("run_id"));
assert!(properties.contains_key("finding_id"));
assert!(properties.contains_key("max_bytes"));
}

#[test]
fn test_web_search_defaults_to_external_access_enabled() {
let model_family = find_family_for_model("o3").expect("o3 should be a valid model family");
Expand Down Expand Up @@ -1811,6 +1884,7 @@ mod tests {
"kill",
"gh_run_wait",
"code_bridge",
"auto_review_detail",
"web_search",
],
);
Expand Down Expand Up @@ -1845,6 +1919,7 @@ mod tests {
"kill",
"gh_run_wait",
"code_bridge",
"auto_review_detail",
"web_search",
],
);
Expand Down Expand Up @@ -1878,6 +1953,7 @@ mod tests {
"kill",
"gh_run_wait",
"code_bridge",
"auto_review_detail",
"web_search",
],
);
Expand Down Expand Up @@ -1950,6 +2026,7 @@ mod tests {
"kill",
"gh_run_wait",
"code_bridge",
"auto_review_detail",
"web_search",
"test_server/do_something_cool",
],
Expand Down Expand Up @@ -2076,6 +2153,7 @@ mod tests {
"kill",
"gh_run_wait",
"code_bridge",
"auto_review_detail",
"web_search",
"test_server/do_something_cool",
],
Expand Down Expand Up @@ -2204,6 +2282,7 @@ mod tests {
"kill",
"gh_run_wait",
"code_bridge",
"auto_review_detail",
"web_search",
"dash/search",
],
Expand Down Expand Up @@ -2281,6 +2360,7 @@ mod tests {
"kill",
"gh_run_wait",
"code_bridge",
"auto_review_detail",
"web_search",
"dash/paginate",
],
Expand Down Expand Up @@ -2359,6 +2439,7 @@ mod tests {
"kill",
"gh_run_wait",
"code_bridge",
"auto_review_detail",
"web_search",
"dash/tags",
],
Expand Down Expand Up @@ -2435,6 +2516,7 @@ mod tests {
"kill",
"gh_run_wait",
"code_bridge",
"auto_review_detail",
"web_search",
"dash/value",
],
Expand Down
Loading