import type { ToolState } from "@opencode-ai/sdk/v2" import type { ToolRendererContext, ToolRenderer, ToolCallPart } from "./types" import { getDefaultToolAction, getToolName, isToolStateCompleted, isToolStateRunning } from "./utils" import { enMessages } from "../../lib/i18n/messages/en" import { defaultRenderer } from "./renderers/default" import { bashRenderer } from "./renderers/bash" import { readRenderer } from "./renderers/read" import { writeRenderer } from "./renderers/write" import { editRenderer } from "./renderers/edit" import { applyPatchRenderer } from "./renderers/apply-patch" import { patchRenderer } from "./renderers/patch" import { webfetchRenderer } from "./renderers/webfetch" import { todoRenderer } from "./renderers/todo" import { invalidRenderer } from "./renderers/invalid" const TITLE_RENDERERS: Record = { bash: bashRenderer, read: readRenderer, write: writeRenderer, edit: editRenderer, apply_patch: applyPatchRenderer, patch: patchRenderer, webfetch: webfetchRenderer, todowrite: todoRenderer, todoread: todoRenderer, invalid: invalidRenderer, } interface TitleSnapshot { toolName: string state?: ToolState } function lookupRenderer(toolName: string): ToolRenderer { return TITLE_RENDERERS[toolName] ?? defaultRenderer } function createStaticToolPart(snapshot: TitleSnapshot): ToolCallPart { return { id: "", type: "tool", tool: snapshot.toolName, state: snapshot.state, } as ToolCallPart } function interpolate(template: string, params?: Record): string { if (!params) return template return template.replace(/\{(\w+)\}/g, (_match, key: string) => { const value = params[key] return value === undefined || value === null ? "" : String(value) }) } function createStaticT(): ToolRendererContext["t"] { return (key, params) => { const template = (enMessages as Record)[key] ?? key return interpolate(template, params) } } function createStaticContext(snapshot: TitleSnapshot): ToolRendererContext { const toolStateAccessor = () => snapshot.state const toolNameAccessor = () => snapshot.toolName const toolCallAccessor = () => createStaticToolPart(snapshot) const messageVersionAccessor = () => undefined const partVersionAccessor = () => undefined const t = createStaticT() const renderMarkdown: ToolRendererContext["renderMarkdown"] = () => null const renderAnsi: ToolRendererContext["renderAnsi"] = () => null const renderDiff: ToolRendererContext["renderDiff"] = () => null return { toolCall: toolCallAccessor, toolState: toolStateAccessor, toolName: toolNameAccessor, instanceId: "", sessionId: "", t, messageVersion: messageVersionAccessor, partVersion: partVersionAccessor, renderMarkdown, renderAnsi, renderDiff, renderToolCall: () => null, scrollHelpers: undefined, } } export function resolveTitleForTool(snapshot: TitleSnapshot): string { const renderer = lookupRenderer(snapshot.toolName) const context = createStaticContext(snapshot) const state = snapshot.state const defaultAction = renderer.getAction?.(context) ?? getDefaultToolAction(snapshot.toolName) if (!state || state.status === "pending") { return defaultAction } const stateTitle = typeof (state as { title?: string }).title === "string" ? (state as { title?: string }).title : undefined if (stateTitle && stateTitle.length > 0) { return stateTitle } const customTitle = renderer.getTitle?.(context) if (customTitle) { return customTitle } return getToolName(snapshot.toolName) }