-
Notifications
You must be signed in to change notification settings - Fork 2.4k
feat: improved UX for tool calls via execute_code #6205
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR enhances the UX for the execute_code tool by adding a tool_graph parameter that describes the execution flow. This allows both the desktop UI and CLI to display user-friendly summaries of what tools will be called and their dependencies, rather than just showing raw code.
Key Changes
- Added
tool_graphfield toexecute_codetool to describe execution flow as a DAG - Desktop UI now renders a visual graph showing tool calls and their dependencies, with code available in an expandable section
- CLI displays formatted output showing numbered tool calls with dependency information
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
ui/desktop/src/components/ToolCallWithResponse.tsx |
Added ToolGraphNode interface and ToolGraphView component to render tool execution graphs in the desktop UI |
crates/goose/src/agents/code_execution_extension.rs |
Added ToolGraphNode struct and tool_graph field to ExecuteCodeParams, updated tool description to instruct models to provide the graph |
crates/goose-cli/src/session/output.rs |
Added render_execute_code_request function to display formatted tool graph output in the CLI |
| return `poking around...`; | ||
|
|
||
| case 'execute_code': { | ||
| const toolGraph = args.tool_graph as unknown as ToolGraphNode[] | undefined; |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The double cast through unknown bypasses TypeScript's type safety. Consider defining a proper type guard function or adding a runtime validation function that checks the structure matches ToolGraphNode[] before casting.
| )} | ||
| {(() => { | ||
| const toolName = toolCall.name.substring(toolCall.name.lastIndexOf('__') + 2); | ||
| const toolGraph = toolCall.arguments?.tool_graph as unknown as ToolGraphNode[] | undefined; |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The double cast through unknown bypasses TypeScript's type safety. Consider defining a proper type guard function or adding a runtime validation function that checks the structure matches ToolGraphNode[] before casting.
| TOOL_GRAPH: Always provide tool_graph to describe the execution flow for the UI. | ||
| Each node has: tool (server/name), description (what it does), depends_on (indices of dependencies). | ||
| Example for chained operations: | ||
| [ | ||
| {"tool": "developer/shell", "description": "list files", "depends_on": []}, | ||
| {"tool": "developer/text_editor", "description": "read README.md", "depends_on": []}, | ||
| {"tool": "developer/text_editor", "description": "write output.txt", "depends_on": [0, 1]} | ||
| ] |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The documentation states "Always provide tool_graph" but the field is marked as optional with #[serde(default)]. This creates inconsistency between the instruction and the actual schema. Either make the field required or update the documentation to say "Provide tool_graph when possible" or similar.
| const deps = | ||
| node.depends_on.length > 0 ? ` (uses ${node.depends_on.map((d) => d + 1).join(', ')})` : ''; |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The dependency indices are displayed without validation. If a dependency index is out of bounds (>= toolGraph.length) or references a later node (>= current index), the displayed number will be misleading. Consider adding validation or at least bounds checking before displaying.
| const deps = | |
| node.depends_on.length > 0 ? ` (uses ${node.depends_on.map((d) => d + 1).join(', ')})` : ''; | |
| const validDeps = node.depends_on.filter( | |
| (d) => | |
| Number.isInteger(d) && | |
| d >= 0 && | |
| d < toolGraph.length && | |
| d < index, | |
| ); | |
| const deps = | |
| validDeps.length > 0 ? ` (uses ${validDeps.map((d) => d + 1).join(', ')})` : ''; |
| .into_iter() | ||
| .flatten() | ||
| .filter_map(Value::as_u64) | ||
| .map(|d| (d + 1).to_string()) |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The dependency indices are displayed without validation. If a dependency index is out of bounds or references a later node, the displayed number will be misleading. Consider adding validation or at least bounds checking before displaying.
| .map(|d| (d + 1).to_string()) | |
| .filter_map(|d| { | |
| let max_index = count as u64; | |
| let current_index = i as u64; | |
| if d < max_index && d <= current_index { | |
| Some((d + 1).to_string()) | |
| } else { | |
| None | |
| } | |
| }) |
c66acc8 to
c99b2e0
Compare
codefromthecrypt
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice I personally like this as when auditing manually found sometimes not sure what it intends vs what it wrote
|
I like it, one suggestion, in GUI, could it say "N tools used" (don't need to add the detail around execute code as that looks a bit extra detailed)? If there is just 1 or 2, could it show it right next to it (not sure if that would be better but just another thought) but just to keep it simple, but yeah, this is great. |
|
Also this seems neat to add to traces at some point.. like I wonder if there would be a "code mode eval" |
c99b2e0 to
b0e8f83
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.
b0e8f83 to
bd917b6
Compare
bd917b6 to
78a1e1f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
| return `${toolGraph[0].description}`; | ||
| } | ||
| if (toolGraph.length === 2) { | ||
| return `${toolGraph[0].tool}, ${toolGraph[1].tool}`; |
Copilot
AI
Dec 22, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When displaying two tools, this shows the tool names but not their descriptions. This is inconsistent with the single-tool case (line 423) which shows the description. For two tools, consider showing descriptions instead of tool names for consistency, or show both items in a format like "description1, description2".
| return `${toolGraph[0].tool}, ${toolGraph[1].tool}`; | |
| const firstLabel = toolGraph[0].description || toolGraph[0].tool; | |
| const secondLabel = toolGraph[1].description || toolGraph[1].tool; | |
| return `${firstLabel}, ${secondLabel}`; |
|
@michaelneale Good suggestions - updated and merging now! |
* main: (155 commits) remove Tool Selection Strategy preview (#6250) fix(cli): correct bash syntax in terminal integration functions (#6181) fix : opening a session to view it modifies session history order in desktop (#6156) test: fix recipe and audio tests to avoid side effects (#6231) chore: Update gemini versions in test_providers.sh (#6246) feat: option to stream json - jsonl really (#6228) feat: add mcp app renderer (#6095) docs: update skills extension to support .agents/skills directories (#6199) Add YouTube short to Chrome DevTools MCP tutorial (#6244) docs: Caveats for privacy information in logs documentation (#6218) move goose issue solver to opus (#6233) feat: improved UX for tool calls via execute_code (#6205) Blog: Code Mode Doesn't Replace MCP (#6227) fix: prevent keychain requests during cargo test (#6219) test: fix test_max_turns_limit slow execution and wrong message type (#6221) Skills vs MCP blog (#6220) Add blog post: Does Your AI Agent Need a Plan? (#6209) fix(ui): enable MCP UI to send a prompt message when an element is clicked (#6207) docs: param option for recipe deeplink/open (#6206) docs: edit in place or fork session (#6203) ...
Change
Improves the UX when in code mode. Makes a new
tool_graphargument the model will provide to outline the flow of execution that will occur with the code it wrote. The client then uses this to render information about what the code it wrote will do (how many tools were called, what tools were called, etc) still with an option to see the code.Demos
Desktop:

CLI:
