normalize tool display; remove backticks (#1482)
This commit is contained in:
committed by
GitHub
parent
d0392e6d5e
commit
84d80659b3
1
.gitignore
vendored
1
.gitignore
vendored
@@ -69,6 +69,7 @@ crates/executors/bindings
|
|||||||
crates/utils/bindings
|
crates/utils/bindings
|
||||||
crates/services/bindings
|
crates/services/bindings
|
||||||
crates/server/bindings
|
crates/server/bindings
|
||||||
|
crates/remote/bindings
|
||||||
|
|
||||||
build-npm-package-codesign.sh
|
build-npm-package-codesign.sh
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ use agent_client_protocol::{self as acp, SessionNotification};
|
|||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use tracing::{debug, trace};
|
|
||||||
use workspace_utils::msg_store::MsgStore;
|
use workspace_utils::msg_store::MsgStore;
|
||||||
|
|
||||||
pub use super::AcpAgentHarness;
|
pub use super::AcpAgentHarness;
|
||||||
@@ -38,7 +37,7 @@ pub fn normalize_logs(msg_store: Arc<MsgStore>, worktree_path: &Path) {
|
|||||||
let mut stdout_lines = msg_store.stdout_lines_stream();
|
let mut stdout_lines = msg_store.stdout_lines_stream();
|
||||||
while let Some(Ok(line)) = stdout_lines.next().await {
|
while let Some(Ok(line)) = stdout_lines.next().await {
|
||||||
if let Some(parsed) = AcpEventParser::parse_line(&line) {
|
if let Some(parsed) = AcpEventParser::parse_line(&line) {
|
||||||
trace!("Parsed ACP line: {:?}", parsed);
|
tracing::trace!("Parsed ACP line: {:?}", parsed);
|
||||||
match parsed {
|
match parsed {
|
||||||
AcpEvent::SessionStart(id) => {
|
AcpEvent::SessionStart(id) => {
|
||||||
if !stored_session_id {
|
if !stored_session_id {
|
||||||
@@ -207,7 +206,7 @@ pub fn normalize_logs(msg_store: Arc<MsgStore>, worktree_path: &Path) {
|
|||||||
.map(|s| s.title.clone())
|
.map(|s| s.title.clone())
|
||||||
.or_else(|| Some("".to_string()));
|
.or_else(|| Some("".to_string()));
|
||||||
}
|
}
|
||||||
trace!("Got tool call update: {:?}", update);
|
tracing::trace!("Got tool call update: {:?}", update);
|
||||||
if let Ok(tc) = agent_client_protocol::ToolCall::try_from(update.clone()) {
|
if let Ok(tc) = agent_client_protocol::ToolCall::try_from(update.clone()) {
|
||||||
handle_tool_call(
|
handle_tool_call(
|
||||||
&tc,
|
&tc,
|
||||||
@@ -218,7 +217,7 @@ pub fn normalize_logs(msg_store: Arc<MsgStore>, worktree_path: &Path) {
|
|||||||
&msg_store,
|
&msg_store,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
debug!("Failed to convert tool call update to ToolCall");
|
tracing::debug!("Failed to convert tool call update to ToolCall");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AcpEvent::User(_) | AcpEvent::Other(_) => (),
|
AcpEvent::User(_) | AcpEvent::Other(_) => (),
|
||||||
@@ -303,9 +302,10 @@ pub fn normalize_logs(msg_store: Arc<MsgStore>, worktree_path: &Path) {
|
|||||||
// Prefer structured raw_output, else fallback to aggregated text content
|
// Prefer structured raw_output, else fallback to aggregated text content
|
||||||
let completed =
|
let completed =
|
||||||
matches!(tc.status, agent_client_protocol::ToolCallStatus::Completed);
|
matches!(tc.status, agent_client_protocol::ToolCallStatus::Completed);
|
||||||
trace!(
|
tracing::trace!(
|
||||||
"Mapping execute tool call, completed: {}, command: {}",
|
"Mapping execute tool call, completed: {}, command: {}",
|
||||||
completed, command
|
completed,
|
||||||
|
command
|
||||||
);
|
);
|
||||||
let tc_exit_status = match tc.status {
|
let tc_exit_status = match tc.status {
|
||||||
agent_client_protocol::ToolCallStatus::Completed => {
|
agent_client_protocol::ToolCallStatus::Completed => {
|
||||||
@@ -617,7 +617,7 @@ impl AcpEventParser {
|
|||||||
return Some(acp_event);
|
return Some(acp_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("Failed to parse ACP raw log {trimmed}");
|
tracing::debug!("Failed to parse ACP raw log {trimmed}");
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1196,11 +1196,11 @@ impl ClaudeLogProcessor {
|
|||||||
worktree_path: &str,
|
worktree_path: &str,
|
||||||
) -> String {
|
) -> String {
|
||||||
match action_type {
|
match action_type {
|
||||||
ActionType::FileRead { path } => format!("`{path}`"),
|
ActionType::FileRead { path } => path.to_string(),
|
||||||
ActionType::FileEdit { path, .. } => format!("`{path}`"),
|
ActionType::FileEdit { path, .. } => path.to_string(),
|
||||||
ActionType::CommandRun { command, .. } => format!("`{command}`"),
|
ActionType::CommandRun { command, .. } => command.to_string(),
|
||||||
ActionType::Search { query } => format!("`{query}`"),
|
ActionType::Search { query } => query.to_string(),
|
||||||
ActionType::WebFetch { url } => format!("`{url}`"),
|
ActionType::WebFetch { url } => url.to_string(),
|
||||||
ActionType::TaskCreate { description } => {
|
ActionType::TaskCreate { description } => {
|
||||||
if description.is_empty() {
|
if description.is_empty() {
|
||||||
"Task".to_string()
|
"Task".to_string()
|
||||||
@@ -1232,13 +1232,13 @@ impl ClaudeLogProcessor {
|
|||||||
if relative_path.is_empty() {
|
if relative_path.is_empty() {
|
||||||
"List directory".to_string()
|
"List directory".to_string()
|
||||||
} else {
|
} else {
|
||||||
format!("List directory: `{relative_path}`")
|
format!("List directory: {relative_path}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ClaudeToolData::Glob { pattern, path, .. } => {
|
ClaudeToolData::Glob { pattern, path, .. } => {
|
||||||
if let Some(search_path) = path {
|
if let Some(search_path) = path {
|
||||||
format!(
|
format!(
|
||||||
"Find files: `{}` in `{}`",
|
"Find files: `{}` in {}",
|
||||||
pattern,
|
pattern,
|
||||||
make_path_relative(search_path, worktree_path)
|
make_path_relative(search_path, worktree_path)
|
||||||
)
|
)
|
||||||
@@ -1257,7 +1257,7 @@ impl ClaudeLogProcessor {
|
|||||||
ClaudeToolData::CodebaseSearchAgent { query, path, .. } => {
|
ClaudeToolData::CodebaseSearchAgent { query, path, .. } => {
|
||||||
match (query.as_ref(), path.as_ref()) {
|
match (query.as_ref(), path.as_ref()) {
|
||||||
(Some(q), Some(p)) if !q.is_empty() && !p.is_empty() => format!(
|
(Some(q), Some(p)) if !q.is_empty() && !p.is_empty() => format!(
|
||||||
"Codebase search: `{}` in `{}`",
|
"Codebase search: `{}` in {}",
|
||||||
q,
|
q,
|
||||||
make_path_relative(p, worktree_path)
|
make_path_relative(p, worktree_path)
|
||||||
),
|
),
|
||||||
@@ -1948,7 +1948,7 @@ mod tests {
|
|||||||
"/tmp/test-worktree",
|
"/tmp/test-worktree",
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(result, "`**/*.ts`");
|
assert_eq!(result, "**/*.ts");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1967,7 +1967,7 @@ mod tests {
|
|||||||
"/tmp/test-worktree",
|
"/tmp/test-worktree",
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(result, "`*.js`");
|
assert_eq!(result, "*.js");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1984,7 +1984,7 @@ mod tests {
|
|||||||
"/tmp/test-worktree",
|
"/tmp/test-worktree",
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(result, "List directory: `components`");
|
assert_eq!(result, "List directory: components");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -2190,8 +2190,8 @@ mod tests {
|
|||||||
let parsed: ClaudeJson = serde_json::from_str(bash_json).unwrap();
|
let parsed: ClaudeJson = serde_json::from_str(bash_json).unwrap();
|
||||||
let entries = normalize(&parsed, "/tmp/work");
|
let entries = normalize(&parsed, "/tmp/work");
|
||||||
assert_eq!(entries.len(), 1);
|
assert_eq!(entries.len(), 1);
|
||||||
// Content should display the command in backticks
|
// Content should display the command
|
||||||
assert_eq!(entries[0].content, "`echo hello`");
|
assert_eq!(entries[0].content, "echo hello");
|
||||||
|
|
||||||
// Task content should include description/prompt wrapped in backticks
|
// Task content should include description/prompt wrapped in backticks
|
||||||
let task_json = r#"{
|
let task_json = r#"{
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ struct CommandState {
|
|||||||
|
|
||||||
impl ToNormalizedEntry for CommandState {
|
impl ToNormalizedEntry for CommandState {
|
||||||
fn to_normalized_entry(&self) -> NormalizedEntry {
|
fn to_normalized_entry(&self) -> NormalizedEntry {
|
||||||
let content = format!("`{}`", self.command);
|
let content = self.command.to_string();
|
||||||
|
|
||||||
NormalizedEntry {
|
NormalizedEntry {
|
||||||
timestamp: None,
|
timestamp: None,
|
||||||
@@ -894,7 +894,7 @@ pub fn normalize_logs(msg_store: Arc<MsgStore>, worktree_path: &Path) {
|
|||||||
},
|
},
|
||||||
status: ToolStatus::Success,
|
status: ToolStatus::Success,
|
||||||
},
|
},
|
||||||
content: format!("`{relative_path}`"),
|
content: relative_path.to_string(),
|
||||||
metadata: None,
|
metadata: None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -717,10 +717,7 @@ impl CursorToolCall {
|
|||||||
match self {
|
match self {
|
||||||
CursorToolCall::Read { args, .. } => {
|
CursorToolCall::Read { args, .. } => {
|
||||||
let path = make_path_relative(&args.path, worktree_path);
|
let path = make_path_relative(&args.path, worktree_path);
|
||||||
(
|
(ActionType::FileRead { path: path.clone() }, path)
|
||||||
ActionType::FileRead { path: path.clone() },
|
|
||||||
format!("`{path}`"),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
CursorToolCall::Write { args, .. } => {
|
CursorToolCall::Write { args, .. } => {
|
||||||
let path = make_path_relative(&args.path, worktree_path);
|
let path = make_path_relative(&args.path, worktree_path);
|
||||||
@@ -729,7 +726,7 @@ impl CursorToolCall {
|
|||||||
path: path.clone(),
|
path: path.clone(),
|
||||||
changes: vec![],
|
changes: vec![],
|
||||||
},
|
},
|
||||||
format!("`{path}`"),
|
path,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
CursorToolCall::Edit { args, result, .. } => {
|
CursorToolCall::Edit { args, result, .. } => {
|
||||||
@@ -789,7 +786,7 @@ impl CursorToolCall {
|
|||||||
path: path.clone(),
|
path: path.clone(),
|
||||||
changes,
|
changes,
|
||||||
},
|
},
|
||||||
format!("`{path}`"),
|
path,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
CursorToolCall::Delete { args, .. } => {
|
CursorToolCall::Delete { args, .. } => {
|
||||||
@@ -799,7 +796,7 @@ impl CursorToolCall {
|
|||||||
path: path.clone(),
|
path: path.clone(),
|
||||||
changes: vec![FileChange::Delete],
|
changes: vec![FileChange::Delete],
|
||||||
},
|
},
|
||||||
format!("`{path}`"),
|
path.to_string(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
CursorToolCall::Shell { args, .. } => {
|
CursorToolCall::Shell { args, .. } => {
|
||||||
@@ -809,7 +806,7 @@ impl CursorToolCall {
|
|||||||
command: cmd.clone(),
|
command: cmd.clone(),
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
format!("`{cmd}`"),
|
cmd.to_string(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
CursorToolCall::Grep { args, .. } => {
|
CursorToolCall::Grep { args, .. } => {
|
||||||
@@ -818,7 +815,7 @@ impl CursorToolCall {
|
|||||||
ActionType::Search {
|
ActionType::Search {
|
||||||
query: pattern.clone(),
|
query: pattern.clone(),
|
||||||
},
|
},
|
||||||
format!("`{pattern}`"),
|
pattern.to_string(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
CursorToolCall::SemSearch { args, .. } => {
|
CursorToolCall::SemSearch { args, .. } => {
|
||||||
@@ -827,7 +824,7 @@ impl CursorToolCall {
|
|||||||
ActionType::Search {
|
ActionType::Search {
|
||||||
query: query.clone(),
|
query: query.clone(),
|
||||||
},
|
},
|
||||||
format!("`{query}`"),
|
query.to_string(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
CursorToolCall::Glob { args, .. } => {
|
CursorToolCall::Glob { args, .. } => {
|
||||||
@@ -838,7 +835,7 @@ impl CursorToolCall {
|
|||||||
ActionType::Search {
|
ActionType::Search {
|
||||||
query: pattern.clone(),
|
query: pattern.clone(),
|
||||||
},
|
},
|
||||||
format!("Find files: `{pattern}` in `{path}`"),
|
format!("Find files: `{pattern}` in {path}"),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
@@ -854,7 +851,7 @@ impl CursorToolCall {
|
|||||||
let content = if path.is_empty() {
|
let content = if path.is_empty() {
|
||||||
"List directory".to_string()
|
"List directory".to_string()
|
||||||
} else {
|
} else {
|
||||||
format!("List directory: `{path}`")
|
format!("List directory: {path}")
|
||||||
};
|
};
|
||||||
(
|
(
|
||||||
ActionType::Other {
|
ActionType::Other {
|
||||||
|
|||||||
@@ -1026,7 +1026,7 @@ impl ToNormalizedEntry for FileReadState {
|
|||||||
},
|
},
|
||||||
status: self.status.clone(),
|
status: self.status.clone(),
|
||||||
},
|
},
|
||||||
content: format!("`{}`", self.path),
|
content: self.path.clone(),
|
||||||
metadata: None,
|
metadata: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1052,7 +1052,7 @@ impl ToNormalizedEntry for FileEditState {
|
|||||||
},
|
},
|
||||||
status: self.status.clone(),
|
status: self.status.clone(),
|
||||||
},
|
},
|
||||||
content: format!("`{}`", self.path),
|
content: self.path.clone(),
|
||||||
metadata: None,
|
metadata: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1094,7 +1094,7 @@ impl ToNormalizedEntry for CommandRunState {
|
|||||||
},
|
},
|
||||||
status: self.status.clone(),
|
status: self.status.clone(),
|
||||||
},
|
},
|
||||||
content: format!("`{}`", self.command),
|
content: self.command.clone(),
|
||||||
metadata: None,
|
metadata: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1149,7 +1149,7 @@ impl ToNormalizedEntry for SearchState {
|
|||||||
},
|
},
|
||||||
status: self.status.clone(),
|
status: self.status.clone(),
|
||||||
},
|
},
|
||||||
content: format!("`{}`", self.query),
|
content: self.query.clone(),
|
||||||
metadata: None,
|
metadata: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1173,7 +1173,7 @@ impl ToNormalizedEntry for WebFetchState {
|
|||||||
},
|
},
|
||||||
status: self.status.clone(),
|
status: self.status.clone(),
|
||||||
},
|
},
|
||||||
content: format!("`{}`", self.url),
|
content: self.url.clone(),
|
||||||
metadata: None,
|
metadata: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -215,7 +215,7 @@ export default function DiffCard({
|
|||||||
// Title row
|
// Title row
|
||||||
const title = (
|
const title = (
|
||||||
<p
|
<p
|
||||||
className="text-xs font-mono overflow-x-auto flex-1"
|
className="text-sm font-mono overflow-x-auto flex-1"
|
||||||
style={{ color: 'hsl(var(--muted-foreground) / 0.7)' }}
|
style={{ color: 'hsl(var(--muted-foreground) / 0.7)' }}
|
||||||
>
|
>
|
||||||
<Icon className="h-3 w-3 inline mr-2" aria-hidden />
|
<Icon className="h-3 w-3 inline mr-2" aria-hidden />
|
||||||
|
|||||||
@@ -516,9 +516,9 @@ const ToolCallCard: React.FC<{
|
|||||||
{entryType && getEntryIcon(entryType)}
|
{entryType && getEntryIcon(entryType)}
|
||||||
</span>
|
</span>
|
||||||
{showInlineSummary ? (
|
{showInlineSummary ? (
|
||||||
<span className="font-light">{inlineText}</span>
|
<span className="text-sm font-mono">{inlineText}</span>
|
||||||
) : (
|
) : (
|
||||||
<span className="font-normal">{label}</span>
|
<span className="text-sm font-mono">{label}</span>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
</HeaderWrapper>
|
</HeaderWrapper>
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ const FileChangeRenderer = ({
|
|||||||
{icon}
|
{icon}
|
||||||
<p
|
<p
|
||||||
onClick={() => expandable && setExpanded()}
|
onClick={() => expandable && setExpanded()}
|
||||||
className="text-sm font-light overflow-x-auto flex-1 cursor-pointer"
|
className="text-sm font-mono overflow-x-auto flex-1 cursor-pointer"
|
||||||
>
|
>
|
||||||
{titleNode}
|
{titleNode}
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
Reference in New Issue
Block a user