mirror of
https://wget.la/https://github.com/leookun/cursor-byok
synced 2026-10-06 05:12:03 +08:00
176 lines
5.1 KiB
Rust
176 lines
5.1 KiB
Rust
mod await_shell;
|
|
mod exec;
|
|
mod gate;
|
|
mod interaction;
|
|
mod local;
|
|
mod mcp;
|
|
mod mcp_state;
|
|
mod semble;
|
|
|
|
use serde_json::Value;
|
|
use tokio::sync::mpsc;
|
|
|
|
use crate::{
|
|
cursor::proto::agent::v1 as pb,
|
|
model::{ToolCall, ToolImageReference, ToolResult},
|
|
store::BlobId,
|
|
Error, Result,
|
|
};
|
|
|
|
use super::runtime::now_ms;
|
|
|
|
pub(crate) use await_shell::{await_error, await_result, await_sleep};
|
|
pub(crate) use exec::{edit_failure, from_exec};
|
|
pub(crate) use interaction::{complete_web_fetch, complete_web_search, from_interaction};
|
|
pub(crate) use local::{local, subagents_disabled, todo_items};
|
|
pub(crate) use mcp::failure as mcp_failure;
|
|
pub(crate) use semble::complete as semble;
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct ToolCompletion {
|
|
result: ToolResult,
|
|
tool_call: pb::ToolCall,
|
|
read_image: Option<ReadImage>,
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub(crate) struct ReadImage {
|
|
pub(crate) data: Vec<u8>,
|
|
pub(crate) mime_type: String,
|
|
pub(crate) path: String,
|
|
}
|
|
|
|
impl ToolCompletion {
|
|
pub fn result(&self) -> &ToolResult {
|
|
&self.result
|
|
}
|
|
|
|
pub fn tool_call(&self) -> &pb::ToolCall {
|
|
&self.tool_call
|
|
}
|
|
|
|
pub(super) fn with_read_image(mut self, image: Option<ReadImage>) -> Self {
|
|
self.read_image = image;
|
|
self
|
|
}
|
|
|
|
pub(crate) fn take_read_image(&mut self) -> Option<ReadImage> {
|
|
self.read_image.take()
|
|
}
|
|
|
|
pub(crate) fn persist_read_image(&mut self, blob_id: &BlobId, image: &ReadImage) -> Result<()> {
|
|
self.result.content = format!("Read image file: {}", image.path);
|
|
self.result.image = Some(ToolImageReference {
|
|
blob_id: blob_id.to_base64(),
|
|
mime_type: image.mime_type.clone(),
|
|
path: image.path.clone(),
|
|
});
|
|
let Some(pb::tool_call::Tool::ReadToolCall(call)) = self.tool_call.tool.as_mut() else {
|
|
return Err(Error::Protocol(
|
|
"Read image completion has no Read tool state".into(),
|
|
));
|
|
};
|
|
let Some(pb::read_tool_result::Result::Success(success)) = call
|
|
.result
|
|
.as_mut()
|
|
.and_then(|result| result.result.as_mut())
|
|
else {
|
|
return Err(Error::Protocol(
|
|
"Read image completion has no success state".into(),
|
|
));
|
|
};
|
|
success.output = Some(pb::read_tool_success::Output::DataBlobId(
|
|
blob_id.as_bytes().to_vec(),
|
|
));
|
|
Ok(())
|
|
}
|
|
|
|
pub(crate) fn new(
|
|
call: &ToolCall,
|
|
started_at_ms: u64,
|
|
mut result: ToolResult,
|
|
tool: pb::tool_call::Tool,
|
|
) -> Self {
|
|
gate::model_content(&tool, &mut result.content);
|
|
Self {
|
|
result,
|
|
tool_call: pb::ToolCall {
|
|
tool_call_id: Some(call.call_id.clone()),
|
|
started_at_ms: Some(started_at_ms),
|
|
completed_at_ms: Some(now_ms()),
|
|
tool: Some(tool),
|
|
hook_additional_contexts: Vec::new(),
|
|
},
|
|
read_image: None,
|
|
}
|
|
}
|
|
|
|
pub(super) fn from_rendered(
|
|
call: &ToolCall,
|
|
started_at_ms: u64,
|
|
output: String,
|
|
is_error: bool,
|
|
rendered: pb::ToolCall,
|
|
) -> Result<Self> {
|
|
let tool = rendered.tool.ok_or_else(|| {
|
|
Error::Protocol(format!("tool {} has no Cursor representation", call.name))
|
|
})?;
|
|
Ok(Self::new(
|
|
call,
|
|
started_at_ms,
|
|
ToolResult {
|
|
call_id: call.call_id.clone(),
|
|
content: output,
|
|
is_error,
|
|
image: None,
|
|
},
|
|
tool,
|
|
))
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct ToolResultSender(mpsc::UnboundedSender<Result<ToolCompletion>>);
|
|
pub struct ToolResultReceiver(mpsc::UnboundedReceiver<Result<ToolCompletion>>);
|
|
|
|
pub fn tool_result_channel() -> (ToolResultSender, ToolResultReceiver) {
|
|
let (sender, receiver) = mpsc::unbounded_channel();
|
|
(ToolResultSender(sender), ToolResultReceiver(receiver))
|
|
}
|
|
|
|
impl ToolResultSender {
|
|
pub fn send(&self, result: ToolCompletion) {
|
|
let _ = self.0.send(Ok(result));
|
|
}
|
|
|
|
pub fn send_error(&self, error: Error) {
|
|
let _ = self.0.send(Err(error));
|
|
}
|
|
}
|
|
|
|
impl ToolResultReceiver {
|
|
pub async fn recv(&mut self) -> Option<Result<ToolCompletion>> {
|
|
self.0.recv().await
|
|
}
|
|
}
|
|
|
|
pub(super) fn prost_json(value: &prost_types::Value) -> Value {
|
|
use prost_types::value::Kind;
|
|
match value.kind.as_ref() {
|
|
None | Some(Kind::NullValue(_)) => Value::Null,
|
|
Some(Kind::NumberValue(value)) => serde_json::Number::from_f64(*value)
|
|
.map(Value::Number)
|
|
.unwrap_or(Value::Null),
|
|
Some(Kind::StringValue(value)) => Value::String(value.clone()),
|
|
Some(Kind::BoolValue(value)) => Value::Bool(*value),
|
|
Some(Kind::StructValue(value)) => Value::Object(
|
|
value
|
|
.fields
|
|
.iter()
|
|
.map(|(key, value)| (key.clone(), prost_json(value)))
|
|
.collect(),
|
|
),
|
|
Some(Kind::ListValue(value)) => Value::Array(value.values.iter().map(prost_json).collect()),
|
|
}
|
|
}
|