/** * Tool types, tool input definitions, and ToolUseBlock. * * ToolUseBlock is the single canonical type for tool invocations — * it includes both the invocation data and display enrichment fields * (result, subActivities). When first created, enrichment fields are null/empty. * The message grouper populates them for UI rendering. */ // ============================================================ // Tool Input Types // ============================================================ export interface BashInput { command: string; description?: string; timeout?: number; runInBackground?: boolean; } export interface ReadInput { filePath: string; offset?: number; limit?: number; pages?: string; } export interface EditInput { filePath: string; oldString: string; newString: string; replaceAll?: boolean; } export interface WriteInput { filePath: string; content: string; } export interface GlobInput { pattern: string; path?: string; } export interface GrepInput { pattern: string; path?: string; glob?: string; outputMode?: 'content' | 'files_with_matches' | 'count'; type?: string; headLimit?: number; offset?: number; multiline?: boolean; caseInsensitive?: boolean; beforeContext?: number; afterContext?: number; context?: number; lineNumbers?: boolean; } export interface WebFetchInput { url: string; prompt: string; } export interface WebSearchInput { query: string; allowedDomains?: string[]; blockedDomains?: string[]; } export interface AskUserQuestionInput { questions: AskUserQuestion[]; } export interface AskUserQuestion { question: string; header: string; options: AskUserQuestionOption[]; multiSelect: boolean; } export interface AskUserQuestionOption { label: string; description: string; markdown?: string; } export interface TodoWriteInput { todos: TodoItem[]; } export interface TodoItem { content: string; status: 'pending' | 'in_progress' | 'completed'; activeForm: string; } /** Task tools (Claude Agent SDK 0.3.142+ — replace the now-default-off TodoWrite). */ export type TaskStatus = 'pending' | 'in_progress' | 'completed'; export interface TaskCreateInput { subject: string; description: string; activeForm?: string; metadata?: Record; } export interface TaskGetInput { taskId: string; } export interface TaskUpdateInput { taskId: string; subject?: string; description?: string; activeForm?: string; status?: TaskStatus | 'deleted'; addBlocks?: string[]; addBlockedBy?: string[]; owner?: string; metadata?: Record; } export interface TaskListInput {} export interface AgentInput { prompt: string; description: string; subagentType: string; model?: string; maxTurns?: number; isolation?: 'worktree'; runInBackground?: boolean; resume?: string; /** Addressable name for a spawned teammate (Claude SDK 0.3.148). */ name?: string; /** Team to spawn into; uses current team context if omitted. */ teamName?: string; /** Permission mode for the spawned teammate. */ mode?: 'acceptEdits' | 'auto' | 'bypassPermissions' | 'default' | 'dontAsk' | 'plan' | 'bubble'; } export interface WorkflowInput { script?: string; name?: string; description?: string; title?: string; args?: Record; scriptPath?: string; resumeFromRunId?: string; } export interface ReplInput { code: string; description?: string; timeout?: number; } export interface SendMessageInput { taskId?: string; agentId?: string; recipient?: string; message: string; } export interface ListAgentsInput { includeCompleted?: boolean; } export interface ReadAgentInput { agentId: string; wait?: boolean; timeout?: number; sinceTurn?: number; } export interface SendInboxInput { summary: string; content: string; } export interface ContextBoardInput { command: 'get' | 'add' | 'prune' | 'get_board'; src?: string; name?: string; description?: string; context?: string; } export interface TaskOutputInput { taskId: string; block?: boolean; timeout?: number; } export interface TaskStopInput { taskId: string; } export interface NotebookEditInput { notebookPath: string; newSource: string; cellId?: string; cellType?: 'code' | 'markdown'; editMode?: 'replace' | 'insert' | 'delete'; } export interface ListMcpResourcesInput { server?: string; } export interface ReadMcpResourceInput { server: string; uri: string; } export interface ConfigInput { key: string; value?: string; } export interface EnterWorktreeInput { name?: string; /** Switch into an existing worktree instead of creating one (Claude SDK 0.3.148). */ path?: string; } export interface EnterPlanModeInput {} export interface ExitPlanModeInput { allowedPrompts?: ExitPlanModePrompt[]; } export interface ExitPlanModePrompt { tool: string; prompt: string; } export interface SkillInput { skill: string; args?: string; } export interface ExitWorktreeInput { keep?: boolean; } export interface ToolSearchInput { query: string; maxResults?: number; } export interface ScheduleWakeupInput { delaySeconds: number; reason: string; prompt: string; } export interface MonitorInput { bashId?: string; processId?: string; source?: string; until?: string; timeout?: number; } export interface PushNotificationInput { title: string; message: string; url?: string; } export interface RemoteTriggerInput { name?: string; payload?: Record; } export interface CronCreateInput { name: string; schedule: string; prompt: string; description?: string; } export interface CronDeleteInput { id: string; } export interface CronListInput { filter?: string; } export interface PatchInput { filePath: string; patch: string; } export interface ListInput { path?: string; ignore?: string[]; } export interface LspInput { operation: string; filePath?: string; line?: number; column?: number; symbol?: string; } // ============================================================ // Tool Input Map // ============================================================ /** * Single source of truth for every canonical tool name accepted by the * chat UI. Any tool emitted by an engine adapter MUST normalize to one * of these names — otherwise the backend emits an `Unknown:*` marker * and the UI renders it as an error (no silent fallback). */ export interface ToolInputMap { // File operations Bash: BashInput; Read: ReadInput; Edit: EditInput; Write: WriteInput; Patch: PatchInput; NotebookEdit: NotebookEditInput; // Discovery Glob: GlobInput; Grep: GrepInput; List: ListInput; // Web WebFetch: WebFetchInput; WebSearch: WebSearchInput; // Planning & questions TodoWrite: TodoWriteInput; AskUserQuestion: AskUserQuestionInput; EnterPlanMode: EnterPlanModeInput; ExitPlanMode: ExitPlanModeInput; // Sub-agents & tasks Agent: AgentInput; Workflow: WorkflowInput; TaskOutput: TaskOutputInput; TaskStop: TaskStopInput; TaskCreate: TaskCreateInput; TaskGet: TaskGetInput; TaskUpdate: TaskUpdateInput; TaskList: TaskListInput; // MCP ListMcpResources: ListMcpResourcesInput; ReadMcpResource: ReadMcpResourceInput; // Harness / workspace Config: ConfigInput; EnterWorktree: EnterWorktreeInput; ExitWorktree: ExitWorktreeInput; Skill: SkillInput; ToolSearch: ToolSearchInput; Lsp: LspInput; REPL: ReplInput; SendMessage: SendMessageInput; ListAgents: ListAgentsInput; ReadAgent: ReadAgentInput; SendInbox: SendInboxInput; ContextBoard: ContextBoardInput; // Automation & notifications ScheduleWakeup: ScheduleWakeupInput; Monitor: MonitorInput; PushNotification: PushNotificationInput; RemoteTrigger: RemoteTriggerInput; CronCreate: CronCreateInput; CronDelete: CronDeleteInput; CronList: CronListInput; } export type KnownToolName = keyof ToolInputMap; export type McpToolName = `mcp__${string}`; /** * Placeholder name for a tool emitted by an engine adapter that does not * map to any canonical name. Adapters MUST wrap the original name (e.g. * `Unknown:weird_tool`) so the UI can render a visible error instead of * silently falling back to a generic box. */ export type UnknownToolName = `Unknown:${string}`; /** * Runtime set of every canonical tool name. Kept in sync with `ToolInputMap`. * Use this in engine adapters to decide whether to emit the tool name as-is * or wrap it with the `Unknown:` prefix. */ export const CANONICAL_TOOL_NAMES = new Set([ 'Bash', 'Read', 'Edit', 'Write', 'Patch', 'NotebookEdit', 'Glob', 'Grep', 'List', 'WebFetch', 'WebSearch', 'TodoWrite', 'AskUserQuestion', 'EnterPlanMode', 'ExitPlanMode', 'Agent', 'Workflow', 'TaskOutput', 'TaskStop', 'TaskCreate', 'TaskGet', 'TaskUpdate', 'TaskList', 'ListMcpResources', 'ReadMcpResource', 'Config', 'EnterWorktree', 'ExitWorktree', 'Skill', 'ToolSearch', 'Lsp', 'REPL', 'SendMessage', 'ListAgents', 'ReadAgent', 'SendInbox', 'ContextBoard', 'ScheduleWakeup', 'Monitor', 'PushNotification', 'RemoteTrigger', 'CronCreate', 'CronDelete', 'CronList', ]); /** * MCP tools that the UI renders via a canonical (non-`mcp__*`) component. * * Currently empty — kept as a hook so a future engine that surfaces a * canonical-equivalent tool through MCP (rather than its native SDK) can * collapse it without re-introducing the registry plumbing. */ const MCP_CANONICAL_OVERRIDES: Record = {}; /** * Normalize an arbitrary tool name emitted by an engine into a shape the UI * is guaranteed to handle: * - canonical names pass through unchanged; * - whitelisted `mcp__*` overrides collapse to their canonical equivalent * (see MCP_CANONICAL_OVERRIDES); * - other `mcp__*` names pass through unchanged; * - anything else is wrapped as `Unknown:` so the UI renders a * visible error with a pointer to the fix (add to the registry). */ export function toCanonicalToolName(rawName: string): KnownToolName | McpToolName | UnknownToolName { if (rawName.startsWith('mcp__')) { const override = MCP_CANONICAL_OVERRIDES[rawName]; if (override) return override; return rawName as McpToolName; } if (CANONICAL_TOOL_NAMES.has(rawName as KnownToolName)) return rawName as KnownToolName; return `Unknown:${rawName}` as UnknownToolName; } // ============================================================ // Sub-Agent Activity (populated in Agent tool blocks) // ============================================================ export interface SubAgentToolActivity { type: 'tool_use'; name: string; input: Record; result: ToolResult | null; } export interface SubAgentTextActivity { type: 'text'; text: string; } export type SubAgentActivity = | SubAgentToolActivity | SubAgentTextActivity; // ============================================================ // Tool Result // ============================================================ /** Result of a tool execution, matched by toolUseId */ export interface ToolResult { type: 'tool_result'; toolUseId: string; content: string; isError: boolean; } // ============================================================ // Tool Use Block // ============================================================ /** * ToolUseBlock is the unified type for tool invocations in assistant messages. * * It includes enrichment fields (result, subActivities, etc.) directly. * These are null/empty when the message is first created from the engine, * and get populated by the message grouper when preparing for display. * * Discriminate on `name` to narrow the `input` type: * ```ts * if (block.name === 'Bash') { * block.input.command; // BashInput * } * ``` */ type KnownToolUseBlock = { [N in KnownToolName]: { type: 'tool_use'; id: string; name: N; input: ToolInputMap[N]; result: ToolResult | null; subActivities: SubAgentActivity[]; skillPrompt: string | null; interrupted: boolean; }; }[KnownToolName]; export interface McpToolUseBlock { type: 'tool_use'; id: string; name: McpToolName; input: Record; result: ToolResult | null; subActivities: SubAgentActivity[]; skillPrompt: string | null; interrupted: boolean; } export interface UnknownToolUseBlock { type: 'tool_use'; id: string; name: UnknownToolName; input: Record; result: ToolResult | null; subActivities: SubAgentActivity[]; skillPrompt: string | null; interrupted: boolean; } export type ToolUseBlock = KnownToolUseBlock | McpToolUseBlock | UnknownToolUseBlock;