Remove Lua scripting tool (#27388)

We decided to take this out for now. It doesn't seem necessary, and it
complicates the code a lot. We can always put it back later if desired.

Release Notes:

- N/A
This commit is contained in:
Richard Feldman 2025-03-24 15:58:07 -04:00 committed by GitHub
parent 5465198d0d
commit 5d05c4aa70
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 52 additions and 2059 deletions

90
Cargo.lock generated
View file

@ -492,7 +492,6 @@ dependencies = [
"proto", "proto",
"rand 0.8.5", "rand 0.8.5",
"rope", "rope",
"scripting_tool",
"serde", "serde",
"serde_json", "serde_json",
"settings", "settings",
@ -4521,12 +4520,6 @@ dependencies = [
"regex", "regex",
] ]
[[package]]
name = "env_home"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe"
[[package]] [[package]]
name = "env_logger" name = "env_logger"
version = "0.10.2" version = "0.10.2"
@ -7931,25 +7924,6 @@ dependencies = [
"url", "url",
] ]
[[package]]
name = "lua-src"
version = "547.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1edaf29e3517b49b8b746701e5648ccb5785cde1c119062cbabbc5d5cd115e42"
dependencies = [
"cc",
]
[[package]]
name = "luajit-src"
version = "210.5.12+a4f56a4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3a8e7962a5368d5f264d045a5a255e90f9aa3fc1941ae15a8d2940d42cac671"
dependencies = [
"cc",
"which 7.0.2",
]
[[package]] [[package]]
name = "lyon" name = "lyon"
version = "1.0.1" version = "1.0.1"
@ -8365,34 +8339,6 @@ dependencies = [
"strum", "strum",
] ]
[[package]]
name = "mlua"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3f763c1041eff92ffb5d7169968a327e1ed2ebfe425dac0ee5a35f29082534b"
dependencies = [
"bstr",
"either",
"futures-util",
"mlua-sys",
"num-traits",
"parking_lot",
"rustc-hash 2.1.1",
]
[[package]]
name = "mlua-sys"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1901c1a635a22fe9250ffcc4fcc937c16b47c2e9e71adba8784af8bca1f69594"
dependencies = [
"cc",
"cfg-if",
"lua-src",
"luajit-src",
"pkg-config",
]
[[package]] [[package]]
name = "msvc_spectre_libs" name = "msvc_spectre_libs"
version = "0.1.2" version = "0.1.2"
@ -12201,30 +12147,6 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152" checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152"
[[package]]
name = "scripting_tool"
version = "0.1.0"
dependencies = [
"anyhow",
"buffer_diff",
"clock",
"collections",
"futures 0.3.31",
"gpui",
"language",
"log",
"mlua",
"parking_lot",
"project",
"rand 0.8.5",
"regex",
"schemars",
"serde",
"serde_json",
"settings",
"util",
]
[[package]] [[package]]
name = "scrypt" name = "scrypt"
version = "0.11.0" version = "0.11.0"
@ -16186,18 +16108,6 @@ dependencies = [
"winsafe", "winsafe",
] ]
[[package]]
name = "which"
version = "7.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2774c861e1f072b3aadc02f8ba886c26ad6321567ecc294c935434cad06f1283"
dependencies = [
"either",
"env_home",
"rustix",
"winsafe",
]
[[package]] [[package]]
name = "whoami" name = "whoami"
version = "1.5.2" version = "1.5.2"

View file

@ -124,7 +124,6 @@ members = [
"crates/rope", "crates/rope",
"crates/rpc", "crates/rpc",
"crates/schema_generator", "crates/schema_generator",
"crates/scripting_tool",
"crates/search", "crates/search",
"crates/semantic_index", "crates/semantic_index",
"crates/semantic_version", "crates/semantic_version",
@ -329,7 +328,6 @@ reqwest_client = { path = "crates/reqwest_client" }
rich_text = { path = "crates/rich_text" } rich_text = { path = "crates/rich_text" }
rope = { path = "crates/rope" } rope = { path = "crates/rope" }
rpc = { path = "crates/rpc" } rpc = { path = "crates/rpc" }
scripting_tool = { path = "crates/scripting_tool" }
search = { path = "crates/search" } search = { path = "crates/search" }
semantic_index = { path = "crates/semantic_index" } semantic_index = { path = "crates/semantic_index" }
semantic_version = { path = "crates/semantic_version" } semantic_version = { path = "crates/semantic_version" }

View file

@ -62,7 +62,6 @@ prompt_library.workspace = true
prompt_store.workspace = true prompt_store.workspace = true
proto.workspace = true proto.workspace = true
rope.workspace = true rope.workspace = true
scripting_tool.workspace = true
serde.workspace = true serde.workspace = true
serde_json.workspace = true serde_json.workspace = true
settings.workspace = true settings.workspace = true

View file

@ -3,8 +3,9 @@ use crate::thread::{
ThreadEvent, ThreadFeedback, ThreadEvent, ThreadFeedback,
}; };
use crate::thread_store::ThreadStore; use crate::thread_store::ThreadStore;
use crate::tool_use::{PendingToolUseStatus, ToolType, ToolUse, ToolUseStatus}; use crate::tool_use::{PendingToolUseStatus, ToolUse, ToolUseStatus};
use crate::ui::ContextPill; use crate::ui::ContextPill;
use collections::HashMap; use collections::HashMap;
use editor::{Editor, MultiBuffer}; use editor::{Editor, MultiBuffer};
use gpui::{ use gpui::{
@ -16,7 +17,6 @@ use gpui::{
use language::{Buffer, LanguageRegistry}; use language::{Buffer, LanguageRegistry};
use language_model::{LanguageModelRegistry, LanguageModelToolUseId, Role}; use language_model::{LanguageModelRegistry, LanguageModelToolUseId, Role};
use markdown::{Markdown, MarkdownStyle}; use markdown::{Markdown, MarkdownStyle};
use scripting_tool::{ScriptingTool, ScriptingToolInput};
use settings::Settings as _; use settings::Settings as _;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
@ -37,7 +37,6 @@ pub struct ActiveThread {
messages: Vec<MessageId>, messages: Vec<MessageId>,
list_state: ListState, list_state: ListState,
rendered_messages_by_id: HashMap<MessageId, RenderedMessage>, rendered_messages_by_id: HashMap<MessageId, RenderedMessage>,
rendered_scripting_tool_uses: HashMap<LanguageModelToolUseId, Entity<Markdown>>,
rendered_tool_use_labels: HashMap<LanguageModelToolUseId, Entity<Markdown>>, rendered_tool_use_labels: HashMap<LanguageModelToolUseId, Entity<Markdown>>,
editing_message: Option<(MessageId, EditMessageState)>, editing_message: Option<(MessageId, EditMessageState)>,
expanded_tool_uses: HashMap<LanguageModelToolUseId, bool>, expanded_tool_uses: HashMap<LanguageModelToolUseId, bool>,
@ -233,7 +232,6 @@ impl ActiveThread {
save_thread_task: None, save_thread_task: None,
messages: Vec::new(), messages: Vec::new(),
rendered_messages_by_id: HashMap::default(), rendered_messages_by_id: HashMap::default(),
rendered_scripting_tool_uses: HashMap::default(),
rendered_tool_use_labels: HashMap::default(), rendered_tool_use_labels: HashMap::default(),
expanded_tool_uses: HashMap::default(), expanded_tool_uses: HashMap::default(),
expanded_thinking_segments: HashMap::default(), expanded_thinking_segments: HashMap::default(),
@ -260,26 +258,6 @@ impl ActiveThread {
cx, cx,
); );
} }
for tool_use in thread
.read(cx)
.scripting_tool_uses_for_message(message.id, cx)
{
this.render_tool_use_label_markdown(
tool_use.id.clone(),
tool_use.ui_text.clone(),
window,
cx,
);
this.render_scripting_tool_use_markdown(
tool_use.id.clone(),
tool_use.ui_text.as_ref(),
tool_use.input.clone(),
window,
cx,
);
}
} }
this this
@ -360,36 +338,6 @@ impl ActiveThread {
self.rendered_messages_by_id.remove(id); self.rendered_messages_by_id.remove(id);
} }
/// Renders the input of a scripting tool use to Markdown.
///
/// Does nothing if the tool use does not correspond to the scripting tool.
fn render_scripting_tool_use_markdown(
&mut self,
tool_use_id: LanguageModelToolUseId,
tool_name: &str,
tool_input: serde_json::Value,
window: &mut Window,
cx: &mut Context<Self>,
) {
if tool_name != ScriptingTool::NAME {
return;
}
let lua_script = serde_json::from_value::<ScriptingToolInput>(tool_input)
.map(|input| input.lua_script)
.unwrap_or_default();
let lua_script = render_markdown(
format!("```lua\n{lua_script}\n```").into(),
self.language_registry.clone(),
window,
cx,
);
self.rendered_scripting_tool_uses
.insert(tool_use_id, lua_script);
}
fn render_tool_use_label_markdown( fn render_tool_use_label_markdown(
&mut self, &mut self,
tool_use_id: LanguageModelToolUseId, tool_use_id: LanguageModelToolUseId,
@ -476,13 +424,6 @@ impl ActiveThread {
window, window,
cx, cx,
); );
self.render_scripting_tool_use_markdown(
tool_use.id,
tool_use.name.as_ref(),
tool_use.input.clone(),
window,
cx,
);
} }
} }
ThreadEvent::ToolFinished { ThreadEvent::ToolFinished {
@ -725,13 +666,9 @@ impl ActiveThread {
let checkpoint = thread.checkpoint_for_message(message_id); let checkpoint = thread.checkpoint_for_message(message_id);
let context = thread.context_for_message(message_id); let context = thread.context_for_message(message_id);
let tool_uses = thread.tool_uses_for_message(message_id, cx); let tool_uses = thread.tool_uses_for_message(message_id, cx);
let scripting_tool_uses = thread.scripting_tool_uses_for_message(message_id, cx);
// Don't render user messages that are just there for returning tool results. // Don't render user messages that are just there for returning tool results.
if message.role == Role::User if message.role == Role::User && thread.message_has_tool_results(message_id) {
&& (thread.message_has_tool_results(message_id)
|| thread.message_has_scripting_tool_results(message_id))
{
return Empty.into_any(); return Empty.into_any();
} }
@ -996,32 +933,23 @@ impl ActiveThread {
) )
.child(div().p_2().child(message_content)), .child(div().p_2().child(message_content)),
), ),
Role::Assistant => { Role::Assistant => v_flex()
v_flex() .id(("message-container", ix))
.id(("message-container", ix)) .ml_2()
.ml_2() .pl_2()
.pl_2() .pr_4()
.pr_4() .border_l_1()
.border_l_1() .border_color(cx.theme().colors().border_variant)
.border_color(cx.theme().colors().border_variant) .child(message_content)
.child(message_content) .when(!tool_uses.is_empty(), |parent| {
.when( parent.child(
!tool_uses.is_empty() || !scripting_tool_uses.is_empty(), v_flex().children(
|parent| { tool_uses
parent.child( .into_iter()
v_flex() .map(|tool_use| self.render_tool_use(tool_use, cx)),
.children( ),
tool_uses
.into_iter()
.map(|tool_use| self.render_tool_use(tool_use, cx)),
)
.children(scripting_tool_uses.into_iter().map(|tool_use| {
self.render_scripting_tool_use(tool_use, cx)
})),
)
},
) )
} }),
Role::System => div().id(("message-container", ix)).py_1().px_2().child( Role::System => div().id(("message-container", ix)).py_1().px_2().child(
v_flex() v_flex()
.bg(colors.editor_background) .bg(colors.editor_background)
@ -1537,145 +1465,6 @@ impl ActiveThread {
) )
} }
fn render_scripting_tool_use(
&self,
tool_use: ToolUse,
cx: &mut Context<Self>,
) -> impl IntoElement {
let is_open = self
.expanded_tool_uses
.get(&tool_use.id)
.copied()
.unwrap_or_default();
div().px_2p5().child(
v_flex()
.gap_1()
.rounded_lg()
.border_1()
.border_color(cx.theme().colors().border)
.child(
h_flex()
.justify_between()
.py_0p5()
.pl_1()
.pr_2()
.bg(cx.theme().colors().editor_foreground.opacity(0.02))
.map(|element| {
if is_open {
element.border_b_1().rounded_t_md()
} else {
element.rounded_md()
}
})
.border_color(cx.theme().colors().border)
.child(
h_flex()
.gap_1()
.child(Disclosure::new("tool-use-disclosure", is_open).on_click(
cx.listener({
let tool_use_id = tool_use.id.clone();
move |this, _event, _window, _cx| {
let is_open = this
.expanded_tool_uses
.entry(tool_use_id.clone())
.or_insert(false);
*is_open = !*is_open;
}
}),
))
.child(
h_flex()
.gap_1p5()
.child(
Icon::new(IconName::Terminal)
.size(IconSize::XSmall)
.color(Color::Muted),
)
.child(
div()
.text_ui_sm(cx)
.children(
self.rendered_tool_use_labels
.get(&tool_use.id)
.cloned(),
)
.truncate(),
),
),
)
.child(
Label::new(match tool_use.status {
ToolUseStatus::Pending => "Pending",
ToolUseStatus::Running => "Running",
ToolUseStatus::Finished(_) => "Finished",
ToolUseStatus::Error(_) => "Error",
ToolUseStatus::NeedsConfirmation => "Asking Permission",
})
.size(LabelSize::XSmall)
.buffer_font(cx),
),
)
.map(|parent| {
if !is_open {
return parent;
}
let lua_script_markdown =
self.rendered_scripting_tool_uses.get(&tool_use.id).cloned();
parent.child(
v_flex()
.child(
v_flex()
.gap_0p5()
.py_1()
.px_2p5()
.border_b_1()
.border_color(cx.theme().colors().border)
.child(Label::new("Input:"))
.map(|parent| {
if let Some(markdown) = lua_script_markdown {
parent.child(markdown)
} else {
parent.child(Label::new(
"Failed to render script input to Markdown",
))
}
}),
)
.map(|parent| match tool_use.status {
ToolUseStatus::Finished(output) => parent.child(
v_flex()
.gap_0p5()
.py_1()
.px_2p5()
.child(Label::new("Result:"))
.child(Label::new(output)),
),
ToolUseStatus::Error(err) => parent.child(
v_flex()
.gap_0p5()
.py_1()
.px_2p5()
.child(Label::new("Error:"))
.child(Label::new(err)),
),
ToolUseStatus::Pending | ToolUseStatus::Running => parent,
ToolUseStatus::NeedsConfirmation => parent.child(
v_flex()
.gap_0p5()
.py_1()
.px_2p5()
.child(Label::new("Asking Permission")),
),
}),
)
}),
)
}
fn render_rules_item(&self, cx: &Context<Self>) -> AnyElement { fn render_rules_item(&self, cx: &Context<Self>) -> AnyElement {
let Some(system_prompt_context) = self.thread.read(cx).system_prompt_context().as_ref() let Some(system_prompt_context) = self.thread.read(cx).system_prompt_context().as_ref()
else { else {
@ -1751,7 +1540,7 @@ impl ActiveThread {
c.ui_text.clone(), c.ui_text.clone(),
c.input.clone(), c.input.clone(),
&c.messages, &c.messages,
c.tool_type.clone(), c.tool.clone(),
cx, cx,
); );
}); });
@ -1761,13 +1550,12 @@ impl ActiveThread {
fn handle_deny_tool( fn handle_deny_tool(
&mut self, &mut self,
tool_use_id: LanguageModelToolUseId, tool_use_id: LanguageModelToolUseId,
tool_type: ToolType,
_: &ClickEvent, _: &ClickEvent,
_window: &mut Window, _window: &mut Window,
cx: &mut Context<Self>, cx: &mut Context<Self>,
) { ) {
self.thread.update(cx, |thread, cx| { self.thread.update(cx, |thread, cx| {
thread.deny_tool_use(tool_use_id, tool_type, cx); thread.deny_tool_use(tool_use_id, cx);
}); });
} }
@ -1802,7 +1590,7 @@ impl ActiveThread {
thread thread
.tools_needing_confirmation() .tools_needing_confirmation()
.map(|(tool_type, tool)| { .map(|tool| {
div() div()
.m_3() .m_3()
.p_2() .p_2()
@ -1844,7 +1632,6 @@ impl ActiveThread {
move |this, event, window, cx| { move |this, event, window, cx| {
this.handle_deny_tool( this.handle_deny_tool(
tool_id.clone(), tool_id.clone(),
tool_type.clone(),
event, event,
window, window,
cx, cx,

View file

@ -23,7 +23,6 @@ use project::{Project, Worktree};
use prompt_store::{ use prompt_store::{
AssistantSystemPromptContext, PromptBuilder, RulesFile, WorktreeInfoForSystemPrompt, AssistantSystemPromptContext, PromptBuilder, RulesFile, WorktreeInfoForSystemPrompt,
}; };
use scripting_tool::{ScriptingSession, ScriptingTool};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use settings::Settings; use settings::Settings;
use util::{maybe, post_inc, ResultExt as _, TryFutureExt as _}; use util::{maybe, post_inc, ResultExt as _, TryFutureExt as _};
@ -34,7 +33,7 @@ use crate::thread_store::{
SerializedMessage, SerializedMessageSegment, SerializedThread, SerializedToolResult, SerializedMessage, SerializedMessageSegment, SerializedThread, SerializedToolResult,
SerializedToolUse, SerializedToolUse,
}; };
use crate::tool_use::{PendingToolUse, PendingToolUseStatus, ToolType, ToolUse, ToolUseState}; use crate::tool_use::{PendingToolUse, ToolUse, ToolUseState};
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum RequestKind { pub enum RequestKind {
@ -188,8 +187,6 @@ pub struct Thread {
action_log: Entity<ActionLog>, action_log: Entity<ActionLog>,
last_restore_checkpoint: Option<LastRestoreCheckpoint>, last_restore_checkpoint: Option<LastRestoreCheckpoint>,
pending_checkpoint: Option<ThreadCheckpoint>, pending_checkpoint: Option<ThreadCheckpoint>,
scripting_session: Entity<ScriptingSession>,
scripting_tool_use: ToolUseState,
initial_project_snapshot: Shared<Task<Option<Arc<ProjectSnapshot>>>>, initial_project_snapshot: Shared<Task<Option<Arc<ProjectSnapshot>>>>,
cumulative_token_usage: TokenUsage, cumulative_token_usage: TokenUsage,
feedback: Option<ThreadFeedback>, feedback: Option<ThreadFeedback>,
@ -221,8 +218,6 @@ impl Thread {
last_restore_checkpoint: None, last_restore_checkpoint: None,
pending_checkpoint: None, pending_checkpoint: None,
tool_use: ToolUseState::new(tools.clone()), tool_use: ToolUseState::new(tools.clone()),
scripting_session: cx.new(|cx| ScriptingSession::new(project.clone(), cx)),
scripting_tool_use: ToolUseState::new(tools),
action_log: cx.new(|_| ActionLog::new()), action_log: cx.new(|_| ActionLog::new()),
initial_project_snapshot: { initial_project_snapshot: {
let project_snapshot = Self::project_snapshot(project, cx); let project_snapshot = Self::project_snapshot(project, cx);
@ -251,14 +246,7 @@ impl Thread {
.unwrap_or(0), .unwrap_or(0),
); );
let tool_use = let tool_use =
ToolUseState::from_serialized_messages(tools.clone(), &serialized.messages, |name| { ToolUseState::from_serialized_messages(tools.clone(), &serialized.messages, |_| true);
name != ScriptingTool::NAME
});
let scripting_tool_use =
ToolUseState::from_serialized_messages(tools.clone(), &serialized.messages, |name| {
name == ScriptingTool::NAME
});
let scripting_session = cx.new(|cx| ScriptingSession::new(project.clone(), cx));
Self { Self {
id, id,
@ -297,8 +285,6 @@ impl Thread {
tools, tools,
tool_use, tool_use,
action_log: cx.new(|_| ActionLog::new()), action_log: cx.new(|_| ActionLog::new()),
scripting_session,
scripting_tool_use,
initial_project_snapshot: Task::ready(serialized.initial_project_snapshot).shared(), initial_project_snapshot: Task::ready(serialized.initial_project_snapshot).shared(),
// TODO: persist token usage? // TODO: persist token usage?
cumulative_token_usage: TokenUsage::default(), cumulative_token_usage: TokenUsage::default(),
@ -357,37 +343,13 @@ impl Thread {
.pending_tool_uses() .pending_tool_uses()
.into_iter() .into_iter()
.find(|tool_use| &tool_use.id == id) .find(|tool_use| &tool_use.id == id)
.or_else(|| {
self.scripting_tool_use
.pending_tool_uses()
.into_iter()
.find(|tool_use| &tool_use.id == id)
})
} }
pub fn tools_needing_confirmation(&self) -> impl Iterator<Item = (ToolType, &PendingToolUse)> { pub fn tools_needing_confirmation(&self) -> impl Iterator<Item = &PendingToolUse> {
self.tool_use self.tool_use
.pending_tool_uses() .pending_tool_uses()
.into_iter() .into_iter()
.filter_map(|tool_use| { .filter(|tool_use| tool_use.status.needs_confirmation())
if let PendingToolUseStatus::NeedsConfirmation(confirmation) = &tool_use.status {
Some((confirmation.tool_type.clone(), tool_use))
} else {
None
}
})
.chain(
self.scripting_tool_use
.pending_tool_uses()
.into_iter()
.filter_map(|tool_use| {
if tool_use.status.needs_confirmation() {
Some((ToolType::ScriptingTool, tool_use))
} else {
None
}
}),
)
} }
pub fn checkpoint_for_message(&self, id: MessageId) -> Option<ThreadCheckpoint> { pub fn checkpoint_for_message(&self, id: MessageId) -> Option<ThreadCheckpoint> {
@ -520,25 +482,18 @@ impl Thread {
/// Returns whether all of the tool uses have finished running. /// Returns whether all of the tool uses have finished running.
pub fn all_tools_finished(&self) -> bool { pub fn all_tools_finished(&self) -> bool {
let mut all_pending_tool_uses = self
.tool_use
.pending_tool_uses()
.into_iter()
.chain(self.scripting_tool_use.pending_tool_uses());
// If the only pending tool uses left are the ones with errors, then // If the only pending tool uses left are the ones with errors, then
// that means that we've finished running all of the pending tools. // that means that we've finished running all of the pending tools.
all_pending_tool_uses.all(|tool_use| tool_use.status.is_error()) self.tool_use
.pending_tool_uses()
.iter()
.all(|tool_use| tool_use.status.is_error())
} }
pub fn tool_uses_for_message(&self, id: MessageId, cx: &App) -> Vec<ToolUse> { pub fn tool_uses_for_message(&self, id: MessageId, cx: &App) -> Vec<ToolUse> {
self.tool_use.tool_uses_for_message(id, cx) self.tool_use.tool_uses_for_message(id, cx)
} }
pub fn scripting_tool_uses_for_message(&self, id: MessageId, cx: &App) -> Vec<ToolUse> {
self.scripting_tool_use.tool_uses_for_message(id, cx)
}
pub fn tool_results_for_message(&self, id: MessageId) -> Vec<&LanguageModelToolResult> { pub fn tool_results_for_message(&self, id: MessageId) -> Vec<&LanguageModelToolResult> {
self.tool_use.tool_results_for_message(id) self.tool_use.tool_results_for_message(id)
} }
@ -547,21 +502,10 @@ impl Thread {
self.tool_use.tool_result(id) self.tool_use.tool_result(id)
} }
pub fn scripting_tool_results_for_message(
&self,
id: MessageId,
) -> Vec<&LanguageModelToolResult> {
self.scripting_tool_use.tool_results_for_message(id)
}
pub fn message_has_tool_results(&self, message_id: MessageId) -> bool { pub fn message_has_tool_results(&self, message_id: MessageId) -> bool {
self.tool_use.message_has_tool_results(message_id) self.tool_use.message_has_tool_results(message_id)
} }
pub fn message_has_scripting_tool_results(&self, message_id: MessageId) -> bool {
self.scripting_tool_use.message_has_tool_results(message_id)
}
pub fn insert_user_message( pub fn insert_user_message(
&mut self, &mut self,
text: impl Into<String>, text: impl Into<String>,
@ -682,7 +626,6 @@ impl Thread {
tool_uses: this tool_uses: this
.tool_uses_for_message(message.id, cx) .tool_uses_for_message(message.id, cx)
.into_iter() .into_iter()
.chain(this.scripting_tool_uses_for_message(message.id, cx))
.map(|tool_use| SerializedToolUse { .map(|tool_use| SerializedToolUse {
id: tool_use.id, id: tool_use.id,
name: tool_use.name, name: tool_use.name,
@ -692,7 +635,6 @@ impl Thread {
tool_results: this tool_results: this
.tool_results_for_message(message.id) .tool_results_for_message(message.id)
.into_iter() .into_iter()
.chain(this.scripting_tool_results_for_message(message.id))
.map(|tool_result| SerializedToolResult { .map(|tool_result| SerializedToolResult {
tool_use_id: tool_result.tool_use_id.clone(), tool_use_id: tool_result.tool_use_id.clone(),
is_error: tool_result.is_error, is_error: tool_result.is_error,
@ -825,15 +767,6 @@ impl Thread {
let mut request = self.to_completion_request(request_kind, cx); let mut request = self.to_completion_request(request_kind, cx);
request.tools = { request.tools = {
let mut tools = Vec::new(); let mut tools = Vec::new();
if self.tools.is_scripting_tool_enabled() {
tools.push(LanguageModelRequestTool {
name: ScriptingTool::NAME.into(),
description: ScriptingTool::DESCRIPTION.into(),
input_schema: ScriptingTool::input_schema(),
});
}
tools.extend(self.tools().enabled_tools(cx).into_iter().map(|tool| { tools.extend(self.tools().enabled_tools(cx).into_iter().map(|tool| {
LanguageModelRequestTool { LanguageModelRequestTool {
name: tool.name(), name: tool.name(),
@ -894,8 +827,6 @@ impl Thread {
RequestKind::Chat => { RequestKind::Chat => {
self.tool_use self.tool_use
.attach_tool_results(message.id, &mut request_message); .attach_tool_results(message.id, &mut request_message);
self.scripting_tool_use
.attach_tool_results(message.id, &mut request_message);
} }
RequestKind::Summarize => { RequestKind::Summarize => {
// We don't care about tool use during summarization. // We don't care about tool use during summarization.
@ -912,8 +843,6 @@ impl Thread {
RequestKind::Chat => { RequestKind::Chat => {
self.tool_use self.tool_use
.attach_tool_uses(message.id, &mut request_message); .attach_tool_uses(message.id, &mut request_message);
self.scripting_tool_use
.attach_tool_uses(message.id, &mut request_message);
} }
RequestKind::Summarize => { RequestKind::Summarize => {
// We don't care about tool use during summarization. // We don't care about tool use during summarization.
@ -1060,19 +989,11 @@ impl Thread {
.iter() .iter()
.rfind(|message| message.role == Role::Assistant) .rfind(|message| message.role == Role::Assistant)
{ {
if tool_use.name.as_ref() == ScriptingTool::NAME { thread.tool_use.request_tool_use(
thread.scripting_tool_use.request_tool_use( last_assistant_message.id,
last_assistant_message.id, tool_use,
tool_use, cx,
cx, );
);
} else {
thread.tool_use.request_tool_use(
last_assistant_message.id,
tool_use,
cx,
);
}
} }
} }
} }
@ -1237,7 +1158,7 @@ impl Thread {
tool_use.ui_text.clone(), tool_use.ui_text.clone(),
tool_use.input.clone(), tool_use.input.clone(),
messages.clone(), messages.clone(),
ToolType::NonScriptingTool(tool), tool,
); );
} else { } else {
self.run_tool( self.run_tool(
@ -1245,7 +1166,7 @@ impl Thread {
tool_use.ui_text.clone(), tool_use.ui_text.clone(),
tool_use.input.clone(), tool_use.input.clone(),
&messages, &messages,
ToolType::NonScriptingTool(tool), tool,
cx, cx,
); );
} }
@ -1255,33 +1176,13 @@ impl Thread {
tool_use.ui_text.clone(), tool_use.ui_text.clone(),
tool_use.input.clone(), tool_use.input.clone(),
&messages, &messages,
ToolType::NonScriptingTool(tool), tool,
cx, cx,
); );
} }
} }
let pending_scripting_tool_uses = self
.scripting_tool_use
.pending_tool_uses()
.into_iter()
.filter(|tool_use| tool_use.status.is_idle())
.cloned()
.collect::<Vec<_>>();
for scripting_tool_use in pending_scripting_tool_uses.iter() {
self.scripting_tool_use.confirm_tool_use(
scripting_tool_use.id.clone(),
scripting_tool_use.ui_text.clone(),
scripting_tool_use.input.clone(),
messages.clone(),
ToolType::ScriptingTool,
);
}
pending_tool_uses pending_tool_uses
.into_iter()
.chain(pending_scripting_tool_uses)
} }
pub fn run_tool( pub fn run_tool(
@ -1290,21 +1191,12 @@ impl Thread {
ui_text: impl Into<SharedString>, ui_text: impl Into<SharedString>,
input: serde_json::Value, input: serde_json::Value,
messages: &[LanguageModelRequestMessage], messages: &[LanguageModelRequestMessage],
tool_type: ToolType, tool: Arc<dyn Tool>,
cx: &mut Context<'_, Thread>, cx: &mut Context<'_, Thread>,
) { ) {
match tool_type { let task = self.spawn_tool_use(tool_use_id.clone(), messages, input, tool, cx);
ToolType::ScriptingTool => { self.tool_use
let task = self.spawn_scripting_tool_use(tool_use_id.clone(), input, cx); .run_pending_tool(tool_use_id, ui_text.into(), task);
self.scripting_tool_use
.run_pending_tool(tool_use_id, ui_text.into(), task);
}
ToolType::NonScriptingTool(tool) => {
let task = self.spawn_tool_use(tool_use_id.clone(), messages, input, tool, cx);
self.tool_use
.run_pending_tool(tool_use_id, ui_text.into(), task);
}
}
} }
fn spawn_tool_use( fn spawn_tool_use(
@ -1344,60 +1236,6 @@ impl Thread {
}) })
} }
fn spawn_scripting_tool_use(
&mut self,
tool_use_id: LanguageModelToolUseId,
input: serde_json::Value,
cx: &mut Context<Thread>,
) -> Task<()> {
let task = match ScriptingTool::deserialize_input(input) {
Err(err) => Task::ready(Err(err.into())),
Ok(input) => {
let (script_id, script_task) =
self.scripting_session.update(cx, move |session, cx| {
session.run_script(input.lua_script, cx)
});
let session = self.scripting_session.clone();
cx.spawn(async move |_, cx| {
script_task.await;
let message = session.read_with(cx, |session, _cx| {
// Using a id to get the script output seems impractical.
// Why not just include it in the Task result?
// This is because we'll later report the script state as it runs,
session
.get(script_id)
.output_message_for_llm()
.expect("Script shouldn't still be running")
})?;
Ok(message)
})
}
};
cx.spawn({
let tool_use_id = tool_use_id.clone();
async move |thread, cx| {
let output = task.await;
thread
.update(cx, |thread, cx| {
let pending_tool_use = thread
.scripting_tool_use
.insert_tool_output(tool_use_id.clone(), output);
cx.emit(ThreadEvent::ToolFinished {
tool_use_id,
pending_tool_use,
canceled: false,
});
})
.ok();
}
})
}
pub fn attach_tool_results( pub fn attach_tool_results(
&mut self, &mut self,
updated_context: Vec<ContextSnapshot>, updated_context: Vec<ContextSnapshot>,
@ -1654,22 +1492,12 @@ impl Thread {
self.cumulative_token_usage.clone() self.cumulative_token_usage.clone()
} }
pub fn deny_tool_use( pub fn deny_tool_use(&mut self, tool_use_id: LanguageModelToolUseId, cx: &mut Context<Self>) {
&mut self,
tool_use_id: LanguageModelToolUseId,
tool_type: ToolType,
cx: &mut Context<Self>,
) {
let err = Err(anyhow::anyhow!( let err = Err(anyhow::anyhow!(
"Permission to run tool action denied by user" "Permission to run tool action denied by user"
)); ));
if let ToolType::ScriptingTool = tool_type { self.tool_use.insert_tool_output(tool_use_id.clone(), err);
self.scripting_tool_use
.insert_tool_output(tool_use_id.clone(), err);
} else {
self.tool_use.insert_tool_output(tool_use_id.clone(), err);
}
cx.emit(ThreadEvent::ToolFinished { cx.emit(ThreadEvent::ToolFinished {
tool_use_id, tool_use_id,

View file

@ -4,7 +4,6 @@ use assistant_settings::{AgentProfile, AssistantSettings};
use assistant_tool::{ToolSource, ToolWorkingSet}; use assistant_tool::{ToolSource, ToolWorkingSet};
use gpui::{Entity, Subscription}; use gpui::{Entity, Subscription};
use indexmap::IndexMap; use indexmap::IndexMap;
use scripting_tool::ScriptingTool;
use settings::{Settings as _, SettingsStore}; use settings::{Settings as _, SettingsStore};
use ui::{prelude::*, ContextMenu, PopoverMenu, Tooltip}; use ui::{prelude::*, ContextMenu, PopoverMenu, Tooltip};
@ -52,7 +51,6 @@ impl ToolSelector {
let tools = tool_set.clone(); let tools = tool_set.clone();
move |_window, cx| { move |_window, cx| {
tools.disable_source(ToolSource::Native, cx); tools.disable_source(ToolSource::Native, cx);
tools.disable_scripting_tool();
tools.enable( tools.enable(
ToolSource::Native, ToolSource::Native,
&profile &profile
@ -61,10 +59,6 @@ impl ToolSelector {
.filter_map(|(tool, enabled)| enabled.then(|| tool.clone())) .filter_map(|(tool, enabled)| enabled.then(|| tool.clone()))
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
); );
if profile.tools.contains_key(ScriptingTool::NAME) {
tools.enable_scripting_tool();
}
} }
}); });
} }
@ -98,11 +92,6 @@ impl ToolSelector {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if ToolSource::Native == source { if ToolSource::Native == source {
tools.push((
ToolSource::Native,
ScriptingTool::NAME.into(),
tool_set.is_scripting_tool_enabled(),
));
tools.sort_by(|(_, name_a, _), (_, name_b, _)| name_a.cmp(name_b)); tools.sort_by(|(_, name_a, _), (_, name_b, _)| name_a.cmp(name_b));
} }
@ -136,18 +125,10 @@ impl ToolSelector {
menu = menu.toggleable_entry(name.clone(), is_enabled, icon_position, None, { menu = menu.toggleable_entry(name.clone(), is_enabled, icon_position, None, {
let tools = tool_set.clone(); let tools = tool_set.clone();
move |_window, _cx| { move |_window, _cx| {
if name.as_ref() == ScriptingTool::NAME { if is_enabled {
if is_enabled { tools.disable(source.clone(), &[name.clone()]);
tools.disable_scripting_tool();
} else {
tools.enable_scripting_tool();
}
} else { } else {
if is_enabled { tools.enable(source.clone(), &[name.clone()]);
tools.disable(source.clone(), &[name.clone()]);
} else {
tools.enable(source.clone(), &[name.clone()]);
}
} }
} }
}); });

View file

@ -10,7 +10,6 @@ use language_model::{
LanguageModelRequestMessage, LanguageModelToolResult, LanguageModelToolUse, LanguageModelRequestMessage, LanguageModelToolResult, LanguageModelToolUse,
LanguageModelToolUseId, MessageContent, Role, LanguageModelToolUseId, MessageContent, Role,
}; };
use scripting_tool::ScriptingTool;
use crate::thread::MessageId; use crate::thread::MessageId;
use crate::thread_store::SerializedMessage; use crate::thread_store::SerializedMessage;
@ -200,8 +199,6 @@ impl ToolUseState {
) -> SharedString { ) -> SharedString {
if let Some(tool) = self.tools.tool(tool_name, cx) { if let Some(tool) = self.tools.tool(tool_name, cx) {
tool.ui_text(input).into() tool.ui_text(input).into()
} else if tool_name == ScriptingTool::NAME {
"Run Lua Script".into()
} else { } else {
"Unknown tool".into() "Unknown tool".into()
} }
@ -285,7 +282,7 @@ impl ToolUseState {
ui_text: impl Into<Arc<str>>, ui_text: impl Into<Arc<str>>,
input: serde_json::Value, input: serde_json::Value,
messages: Arc<Vec<LanguageModelRequestMessage>>, messages: Arc<Vec<LanguageModelRequestMessage>>,
tool_type: ToolType, tool: Arc<dyn Tool>,
) { ) {
if let Some(tool_use) = self.pending_tool_uses_by_id.get_mut(&tool_use_id) { if let Some(tool_use) = self.pending_tool_uses_by_id.get_mut(&tool_use_id) {
let ui_text = ui_text.into(); let ui_text = ui_text.into();
@ -294,7 +291,7 @@ impl ToolUseState {
tool_use_id, tool_use_id,
input, input,
messages, messages,
tool_type, tool,
ui_text, ui_text,
}; };
tool_use.status = PendingToolUseStatus::NeedsConfirmation(Arc::new(confirmation)); tool_use.status = PendingToolUseStatus::NeedsConfirmation(Arc::new(confirmation));
@ -398,19 +395,13 @@ pub struct PendingToolUse {
pub status: PendingToolUseStatus, pub status: PendingToolUseStatus,
} }
#[derive(Debug, Clone)]
pub enum ToolType {
ScriptingTool,
NonScriptingTool(Arc<dyn Tool>),
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Confirmation { pub struct Confirmation {
pub tool_use_id: LanguageModelToolUseId, pub tool_use_id: LanguageModelToolUseId,
pub input: serde_json::Value, pub input: serde_json::Value,
pub ui_text: Arc<str>, pub ui_text: Arc<str>,
pub messages: Arc<Vec<LanguageModelRequestMessage>>, pub messages: Arc<Vec<LanguageModelRequestMessage>>,
pub tool_type: ToolType, pub tool: Arc<dyn Tool>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View file

@ -15,26 +15,14 @@ pub struct ToolWorkingSet {
state: Mutex<WorkingSetState>, state: Mutex<WorkingSetState>,
} }
#[derive(Default)]
struct WorkingSetState { struct WorkingSetState {
context_server_tools_by_id: HashMap<ToolId, Arc<dyn Tool>>, context_server_tools_by_id: HashMap<ToolId, Arc<dyn Tool>>,
context_server_tools_by_name: HashMap<String, Arc<dyn Tool>>, context_server_tools_by_name: HashMap<String, Arc<dyn Tool>>,
disabled_tools_by_source: HashMap<ToolSource, HashSet<Arc<str>>>, disabled_tools_by_source: HashMap<ToolSource, HashSet<Arc<str>>>,
is_scripting_tool_disabled: bool,
next_tool_id: ToolId, next_tool_id: ToolId,
} }
impl Default for WorkingSetState {
fn default() -> Self {
Self {
context_server_tools_by_id: HashMap::default(),
context_server_tools_by_name: HashMap::default(),
disabled_tools_by_source: HashMap::default(),
is_scripting_tool_disabled: true,
next_tool_id: ToolId::default(),
}
}
}
impl ToolWorkingSet { impl ToolWorkingSet {
pub fn tool(&self, name: &str, cx: &App) -> Option<Arc<dyn Tool>> { pub fn tool(&self, name: &str, cx: &App) -> Option<Arc<dyn Tool>> {
self.state self.state
@ -55,7 +43,7 @@ impl ToolWorkingSet {
pub fn are_all_tools_enabled(&self) -> bool { pub fn are_all_tools_enabled(&self) -> bool {
let state = self.state.lock(); let state = self.state.lock();
state.disabled_tools_by_source.is_empty() && !state.is_scripting_tool_disabled state.disabled_tools_by_source.is_empty()
} }
pub fn are_all_tools_from_source_enabled(&self, source: &ToolSource) -> bool { pub fn are_all_tools_from_source_enabled(&self, source: &ToolSource) -> bool {
@ -70,7 +58,6 @@ impl ToolWorkingSet {
pub fn enable_all_tools(&self) { pub fn enable_all_tools(&self) {
let mut state = self.state.lock(); let mut state = self.state.lock();
state.disabled_tools_by_source.clear(); state.disabled_tools_by_source.clear();
state.enable_scripting_tool();
} }
pub fn disable_all_tools(&self, cx: &App) { pub fn disable_all_tools(&self, cx: &App) {
@ -124,21 +111,6 @@ impl ToolWorkingSet {
.retain(|id, _| !tool_ids_to_remove.contains(id)); .retain(|id, _| !tool_ids_to_remove.contains(id));
state.tools_changed(); state.tools_changed();
} }
pub fn is_scripting_tool_enabled(&self) -> bool {
let state = self.state.lock();
!state.is_scripting_tool_disabled
}
pub fn enable_scripting_tool(&self) {
let mut state = self.state.lock();
state.enable_scripting_tool();
}
pub fn disable_scripting_tool(&self) {
let mut state = self.state.lock();
state.disable_scripting_tool();
}
} }
impl WorkingSetState { impl WorkingSetState {
@ -240,15 +212,5 @@ impl WorkingSetState {
self.disable(source, &tool_names); self.disable(source, &tool_names);
} }
self.disable_scripting_tool();
}
fn enable_scripting_tool(&mut self) {
self.is_scripting_tool_disabled = false;
}
fn disable_scripting_tool(&mut self) {
self.is_scripting_tool_disabled = true;
} }
} }

View file

@ -1,42 +0,0 @@
[package]
name = "scripting_tool"
version = "0.1.0"
edition.workspace = true
publish.workspace = true
license = "GPL-3.0-or-later"
[lints]
workspace = true
[lib]
path = "src/scripting_tool.rs"
doctest = false
[dependencies]
anyhow.workspace = true
buffer_diff.workspace = true
clock.workspace = true
collections.workspace = true
futures.workspace = true
gpui.workspace = true
language.workspace = true
log.workspace = true
mlua.workspace = true
parking_lot.workspace = true
project.workspace = true
regex.workspace = true
schemars.workspace = true
serde.workspace = true
serde_json.workspace = true
settings.workspace = true
util.workspace = true
[dev-dependencies]
buffer_diff = { workspace = true, features = ["test-support"] }
clock = { workspace = true, features = ["test-support"] }
collections = { workspace = true, features = ["test-support"] }
gpui = { workspace = true, features = ["test-support"] }
language = { workspace = true, features = ["test-support"] }
project = { workspace = true, features = ["test-support"] }
rand.workspace = true
settings = { workspace = true, features = ["test-support"] }

View file

@ -1 +0,0 @@
../../LICENSE-GPL

View file

@ -1,55 +0,0 @@
---@diagnostic disable: undefined-global
-- Create a sandbox environment
local sandbox = {}
-- For now, add all globals to `sandbox` (so there effectively is no sandbox).
-- We still need the logic below so that we can do things like overriding print() to write
-- to our in-memory log rather than to stdout, we will delete this loop (and re-enable
-- the I/O module being sandboxed below) to have things be sandboxed again.
for k, v in pairs(_G) do
if sandbox[k] == nil then
sandbox[k] = v
end
end
-- Allow access to standard libraries (safe subset)
sandbox.string = string
sandbox.table = table
sandbox.math = math
sandbox.print = sb_print
sandbox.type = type
sandbox.tostring = tostring
sandbox.tonumber = tonumber
sandbox.pairs = pairs
sandbox.ipairs = ipairs
-- Access to custom functions
sandbox.search = search
sandbox.outline = outline
-- Create a sandboxed version of LuaFileIO
-- local io = {};
--
-- For now we are using unsandboxed io
local io = _G.io;
-- File functions
io.open = sb_io_open
-- Add the sandboxed io library to the sandbox environment
sandbox.io = io
-- Load the script with the sandbox environment
local user_script_fn, err = load(user_script, nil, "t", sandbox)
if not user_script_fn then
error("Failed to load user script: " .. tostring(err))
end
-- Execute the user script within the sandbox
local success, result = pcall(user_script_fn)
if not success then
error("Error executing user script: " .. tostring(result))
end

File diff suppressed because it is too large Load diff

View file

@ -1,30 +0,0 @@
mod scripting_session;
pub use scripting_session::*;
use schemars::JsonSchema;
use serde::Deserialize;
#[derive(Debug, Deserialize, JsonSchema)]
pub struct ScriptingToolInput {
pub lua_script: String,
}
pub struct ScriptingTool;
impl ScriptingTool {
pub const NAME: &str = "lua-interpreter";
pub const DESCRIPTION: &str = include_str!("scripting_tool_description.md");
pub fn input_schema() -> serde_json::Value {
let schema = schemars::schema_for!(ScriptingToolInput);
serde_json::to_value(&schema).unwrap()
}
pub fn deserialize_input(
input: serde_json::Value,
) -> Result<ScriptingToolInput, serde_json::Error> {
serde_json::from_value(input)
}
}

View file

@ -1,21 +0,0 @@
Evaluates the given Lua script in an interpreter with access to the Lua standard library. The tool returns the scripts output to stdout and any error that may have occurred.
Use this tool to explore the current project and edit the user's codebase or operating system as requested.
Additional functions provided:
```lua
--- Search for matches of a regular expression in files.
-- @param pattern The regex pattern to search for (uses Rust's regex syntax)
-- @return An array of tables with 'path' (file path) and 'matches' (array of matching strings)
-- @usage local results = search("function\\s+\\w+")
function search(pattern)
-- Implementation provided by the tool
end
--- Generates an outline for the given file path, extracting top-level symbols such as functions, classes, exports, and other significant declarations. This provides a structural overview of the file's contents.
-- @param path
function outline(path)
-- Implementation provided by the tool
end
```