Sync tool-call titles and task summaries
This commit is contained in:
@@ -1,25 +1,84 @@
|
||||
import { For, createMemo } from "solid-js"
|
||||
import { For, Show, createMemo } from "solid-js"
|
||||
import type { ToolState } from "@opencode-ai/sdk"
|
||||
import type { ToolRenderer } from "../types"
|
||||
import { getRelativePath, getToolIcon, getToolName, readToolStatePayload } from "../utils"
|
||||
import { getDefaultToolAction, getToolIcon, getToolName, readToolStatePayload } from "../utils"
|
||||
import { getTodoTitle } from "./todo"
|
||||
import { resolveTitleForTool } from "../tool-title"
|
||||
|
||||
interface TaskSummaryItem {
|
||||
id: string
|
||||
tool: string
|
||||
input: Record<string, any>
|
||||
metadata: Record<string, any>
|
||||
state?: ToolState
|
||||
status?: ToolState["status"]
|
||||
title?: string
|
||||
}
|
||||
|
||||
function describeTaskItem(item: TaskSummaryItem): string {
|
||||
const input = item.input || {}
|
||||
switch (item.tool) {
|
||||
case "bash":
|
||||
return typeof input.description === "string" ? input.description : input.command || "bash"
|
||||
case "edit":
|
||||
case "read":
|
||||
case "write":
|
||||
return `${item.tool} ${getRelativePath(typeof input.filePath === "string" ? input.filePath : "")}`.trim()
|
||||
default:
|
||||
return item.tool
|
||||
function normalizeStatus(status?: string | null): ToolState["status"] | undefined {
|
||||
if (status === "pending" || status === "running" || status === "completed" || status === "error") {
|
||||
return status
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
function summarizeStatusIcon(status?: ToolState["status"]) {
|
||||
switch (status) {
|
||||
case "pending":
|
||||
return "⏸"
|
||||
case "running":
|
||||
return "⏳"
|
||||
case "completed":
|
||||
return "✓"
|
||||
case "error":
|
||||
return "✗"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
function summarizeStatusLabel(status?: ToolState["status"]) {
|
||||
switch (status) {
|
||||
case "pending":
|
||||
return "Pending"
|
||||
case "running":
|
||||
return "Running"
|
||||
case "completed":
|
||||
return "Completed"
|
||||
case "error":
|
||||
return "Error"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
function describeTaskTitle(input: Record<string, any>) {
|
||||
const description = typeof input.description === "string" ? input.description : undefined
|
||||
const subagent = typeof input.subagent_type === "string" ? input.subagent_type : undefined
|
||||
const base = getToolName("task")
|
||||
if (description && subagent) {
|
||||
return `${base}[${subagent}] ${description}`
|
||||
}
|
||||
if (description) {
|
||||
return `${base} ${description}`
|
||||
}
|
||||
return base
|
||||
}
|
||||
|
||||
function describeToolTitle(item: TaskSummaryItem): string {
|
||||
if (item.title && item.title.length > 0) {
|
||||
return item.title
|
||||
}
|
||||
|
||||
if (item.tool === "task") {
|
||||
return describeTaskTitle({ ...item.metadata, ...item.input })
|
||||
}
|
||||
|
||||
if (item.state) {
|
||||
return resolveTitleForTool({ toolName: item.tool, state: item.state })
|
||||
}
|
||||
|
||||
return getDefaultToolAction(item.tool)
|
||||
}
|
||||
|
||||
export const taskRenderer: ToolRenderer = {
|
||||
@@ -29,18 +88,9 @@ export const taskRenderer: ToolRenderer = {
|
||||
const state = toolState()
|
||||
if (!state) return undefined
|
||||
const { input } = readToolStatePayload(state)
|
||||
const description = input.description
|
||||
const subagent = input.subagent_type
|
||||
const base = getToolName("task")
|
||||
if (description && subagent) {
|
||||
return `${base}[${subagent}] ${description}`
|
||||
}
|
||||
if (description) {
|
||||
return `${base} ${description}`
|
||||
}
|
||||
return base
|
||||
return describeTaskTitle(input)
|
||||
},
|
||||
renderBody({ toolState, toolCall, messageVersion, partVersion, scrollHelpers }) {
|
||||
renderBody({ toolState, messageVersion, partVersion, scrollHelpers }) {
|
||||
const items = createMemo(() => {
|
||||
// Track the reactive change points so we only recompute when the part/message changes
|
||||
messageVersion?.()
|
||||
@@ -54,9 +104,13 @@ export const taskRenderer: ToolRenderer = {
|
||||
|
||||
return summary.map((entry, index) => {
|
||||
const tool = typeof entry?.tool === "string" ? (entry.tool as string) : "unknown"
|
||||
const input = typeof (entry as any)?.state?.input === "object" && entry.state?.input ? entry.state.input : {}
|
||||
const stateValue = typeof entry?.state === "object" ? (entry.state as ToolState) : undefined
|
||||
const metadataFromEntry = typeof entry?.metadata === "object" && entry.metadata ? entry.metadata : {}
|
||||
const fallbackInput = typeof entry?.input === "object" && entry.input ? entry.input : {}
|
||||
const id = typeof entry?.id === "string" && entry.id.length > 0 ? entry.id : `${tool}-${index}`
|
||||
return { id, tool, input }
|
||||
const statusValue = normalizeStatus((entry?.status as string | undefined) ?? stateValue?.status)
|
||||
const title = typeof entry?.title === "string" ? entry.title : undefined
|
||||
return { id, tool, input: fallbackInput, metadata: metadataFromEntry, state: stateValue, status: statusValue, title }
|
||||
})
|
||||
})
|
||||
|
||||
@@ -72,11 +126,23 @@ export const taskRenderer: ToolRenderer = {
|
||||
<For each={items()}>
|
||||
{(item) => {
|
||||
const icon = getToolIcon(item.tool)
|
||||
const description = describeTaskItem(item)
|
||||
const description = describeToolTitle(item)
|
||||
const toolLabel = getToolName(item.tool)
|
||||
const status = normalizeStatus(item.status ?? item.state?.status)
|
||||
const statusIcon = summarizeStatusIcon(status)
|
||||
const statusLabel = summarizeStatusLabel(status)
|
||||
const statusAttr = status ?? "pending"
|
||||
return (
|
||||
<div class="tool-call-task-item" data-task-id={item.id}>
|
||||
<div class="tool-call-task-item" data-task-id={item.id} data-task-status={statusAttr}>
|
||||
<span class="tool-call-task-icon">{icon}</span>
|
||||
<span class="tool-call-task-label">{toolLabel}</span>
|
||||
<span class="tool-call-task-separator" aria-hidden="true">—</span>
|
||||
<span class="tool-call-task-text">{description}</span>
|
||||
<Show when={statusIcon}>
|
||||
<span class="tool-call-task-status" aria-label={statusLabel} title={statusLabel}>
|
||||
{statusIcon}
|
||||
</span>
|
||||
</Show>
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
@@ -87,4 +153,3 @@ export const taskRenderer: ToolRenderer = {
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user