610c869ac6
The core-tool rename shipped `todo_list` on the wire (legacy alias `todo` kept for old transcripts), but the Desktop renderer still matched the tool by the literal `todo` in seven places: the live tool.start/tool.complete mirror into the composer status stack, the todo-stream router, args carry-over, the transcript hoist, the silent-tool class, the count noun, and stored-history hydration. Every live task update therefore went into the transcript as an ordinary tool row while the task panel stayed empty, and reopening a chat never restored a finished list. One predicate (`isTodoToolName`) now owns the wire/legacy name pair and every site reads it.
47 lines
2.2 KiB
TypeScript
47 lines
2.2 KiB
TypeScript
/**
|
|
* Which surface a tool call renders as.
|
|
*
|
|
* Two consumers have to agree on this and they sit on opposite sides of the
|
|
* app: the transcript decides what to draw, and the DOM render budget decides
|
|
* how much of the transcript to mount. Pricing a turn correctly means pricing
|
|
* what the grouping actually renders, so the classification lives on its own
|
|
* rather than inside either one.
|
|
*/
|
|
|
|
const FILE_EDIT_TOOL_NAMES = new Set(['edit_file', 'patch', 'write_file'])
|
|
|
|
/** Renders a diff — the deliverable of the turn, and the one card whose cost scales. */
|
|
export function isFileEditTool(toolName: string): boolean {
|
|
return FILE_EDIT_TOOL_NAMES.has(toolName)
|
|
}
|
|
|
|
// Tools that draw their own surface and must never be folded into a run's
|
|
// summary. Two kinds, for the same reason — the thing on screen IS the point:
|
|
//
|
|
// - File edits are the deliverable, not scaffolding. The diff is what the
|
|
// user reviews, so it stays visible at its place in the turn, live and
|
|
// settled, the way a PR shows its changes.
|
|
// - `clarify`, `image_generate` and `delegate_task` bypass ToolEntry to
|
|
// render their own markup: a question the user has to answer, an image
|
|
// they asked for, the several agents a fan-out is running.
|
|
// - `setup_mcp` is the same kind: an inline consent card the user has to
|
|
// act on. Folding it into a "Using 2 tools" summary hides the buttons.
|
|
//
|
|
// Everything else is ephemeral activity — reads, searches, commands — which is
|
|
// what a run summarizes and what the live ticker cycles through.
|
|
const CARD_TOOL_NAMES = new Set(['clarify', 'delegate_task', 'image_generate', 'setup_mcp'])
|
|
|
|
export function isCardTool(toolName: string): boolean {
|
|
return CARD_TOOL_NAMES.has(toolName) || isFileEditTool(toolName)
|
|
}
|
|
|
|
// Activity tools that render nothing at all: `todo` parts are hoisted to a
|
|
// dedicated panel above the message content, and a reaction's UI is the emoji
|
|
// landing on the bubble. Both still render when they FAIL, which is a bounded
|
|
// error row either way.
|
|
const SILENT_TOOL_NAMES = new Set(['react_to_message', 'todo', 'todo_list'])
|
|
|
|
export function isSilentTool(toolName: string): boolean {
|
|
return SILENT_TOOL_NAMES.has(toolName)
|
|
}
|