mirror of
https://wget.la/https://github.com/leookun/cursor-byok
synced 2026-10-09 00:16:23 +08:00
Merge pull request #384 from kevin9327/fix/bash-shell-alias
fix(tools): complete the bash shell alias in the tool codec
This commit is contained in:
@@ -167,7 +167,7 @@ pub fn tool_completed(call: &ToolCall, completion: &ToolCompletion) -> pb::Agent
|
|||||||
pub fn tool_placeholder(name: &str, call_id: &str) -> Result<pb::ToolCall> {
|
pub fn tool_placeholder(name: &str, call_id: &str) -> Result<pb::ToolCall> {
|
||||||
use pb::tool_call::Tool;
|
use pb::tool_call::Tool;
|
||||||
let tool = match normalized(name).as_str() {
|
let tool = match normalized(name).as_str() {
|
||||||
"shell" => Tool::ShellToolCall(pb::ShellToolCall::default()),
|
"shell" | "bash" => Tool::ShellToolCall(pb::ShellToolCall::default()),
|
||||||
"delete" => Tool::DeleteToolCall(pb::DeleteToolCall::default()),
|
"delete" => Tool::DeleteToolCall(pb::DeleteToolCall::default()),
|
||||||
"glob" => Tool::GlobToolCall(pb::GlobToolCall::default()),
|
"glob" => Tool::GlobToolCall(pb::GlobToolCall::default()),
|
||||||
"grep" => Tool::GrepToolCall(pb::GrepToolCall::default()),
|
"grep" => Tool::GrepToolCall(pb::GrepToolCall::default()),
|
||||||
@@ -527,3 +527,23 @@ fn now_ms() -> u64 {
|
|||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.as_millis() as u64
|
.as_millis() as u64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::tool_placeholder;
|
||||||
|
use crate::cursor::protocol::proto::agent::v1 as pb;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bash_renders_as_a_shell_placeholder() {
|
||||||
|
// The dispatcher treats `bash`/`Bash` as a Shell alias, so the streaming
|
||||||
|
// placeholder must too; otherwise a `Bash` tool call aborts the turn with
|
||||||
|
// `unsupported tool: bash` before it ever runs.
|
||||||
|
for name in ["shell", "Shell", "bash", "Bash"] {
|
||||||
|
let tool = tool_placeholder(name, "call-1").unwrap().tool;
|
||||||
|
assert!(
|
||||||
|
matches!(tool, Some(pb::tool_call::Tool::ShellToolCall(_))),
|
||||||
|
"{name} should render as a Shell tool"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ pub fn request(id: u32, call: &ToolCall, context: &ExecContext) -> Result<pb::Ag
|
|||||||
.map(|v| v as i32)
|
.map(|v| v as i32)
|
||||||
};
|
};
|
||||||
let message = match normalize(&call.name).as_str() {
|
let message = match normalize(&call.name).as_str() {
|
||||||
"shell" => {
|
"shell" | "bash" => {
|
||||||
let command = string("command")?;
|
let command = string("command")?;
|
||||||
let (simple_commands, parsing_result) = shell_command_metadata(&command);
|
let (simple_commands, parsing_result) = shell_command_metadata(&command);
|
||||||
Message::ShellStreamArgs(pb::ShellArgs {
|
Message::ShellStreamArgs(pb::ShellArgs {
|
||||||
@@ -520,3 +520,37 @@ fn prost_value(value: &Value) -> prost_types::Value {
|
|||||||
};
|
};
|
||||||
ProstValue { kind: Some(kind) }
|
ProstValue { kind: Some(kind) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
|
use super::request;
|
||||||
|
use crate::cursor::protocol::proto::agent::v1 as pb;
|
||||||
|
use crate::cursor::tools::runtime::ExecContext;
|
||||||
|
use crate::model::ToolCall;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bash_is_encoded_as_a_shell_exec_request() {
|
||||||
|
// The dispatcher routes `bash`/`Bash` to the shell executor, so the
|
||||||
|
// request codec must encode it as a Shell stream instead of erroring
|
||||||
|
// with `tool bash is not executed through ExecServerMessage`.
|
||||||
|
let call = ToolCall {
|
||||||
|
index: 0,
|
||||||
|
call_id: "call-1".into(),
|
||||||
|
model_call_id: "model-1".into(),
|
||||||
|
name: "Bash".into(),
|
||||||
|
arguments_text: String::new(),
|
||||||
|
arguments: json!({ "command": "ls -la" }),
|
||||||
|
};
|
||||||
|
let message = request(1, &call, &ExecContext::default()).unwrap();
|
||||||
|
let Some(pb::agent_server_message::Message::ExecServerMessage(exec)) = message.message
|
||||||
|
else {
|
||||||
|
panic!("expected an ExecServerMessage");
|
||||||
|
};
|
||||||
|
let Some(pb::exec_server_message::Message::ShellStreamArgs(args)) = exec.message else {
|
||||||
|
panic!("expected ShellStreamArgs");
|
||||||
|
};
|
||||||
|
assert_eq!(args.command, "ls -la");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -144,7 +144,8 @@ pub async fn stream_closed(id: u32, pending: &CursorToolRuntime) -> Result<Optio
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
let error = "Cursor Exec stream closed before returning a terminal result";
|
let error = "Cursor Exec stream closed before returning a terminal result";
|
||||||
if entry.call.name.eq_ignore_ascii_case("Shell") {
|
if entry.call.name.eq_ignore_ascii_case("Shell") || entry.call.name.eq_ignore_ascii_case("Bash")
|
||||||
|
{
|
||||||
let command = entry
|
let command = entry
|
||||||
.call
|
.call
|
||||||
.arguments
|
.arguments
|
||||||
|
|||||||
Reference in New Issue
Block a user