Răsfoiți Sursa

feat(tui): render execute child calls on v2 (#35231)

Aiden Cline 1 lună în urmă
părinte
comite
d097cc8065
1 a modificat fișierele cu 63 adăugiri și 0 ștergeri
  1. 63 0
      packages/tui/src/routes/session/index.tsx

+ 63 - 0
packages/tui/src/routes/session/index.tsx

@@ -1810,6 +1810,9 @@ function ToolPart(props: { part: SessionMessageAssistantTool }) {
         <Match when={display() === "subagent"}>
         <Match when={display() === "subagent"}>
           <Subagent {...toolprops} />
           <Subagent {...toolprops} />
         </Match>
         </Match>
+        <Match when={display() === "execute"}>
+          <Execute {...toolprops} />
+        </Match>
         <Match when={display() === "apply_patch"}>
         <Match when={display() === "apply_patch"}>
           <ApplyPatch {...toolprops} />
           <ApplyPatch {...toolprops} />
         </Match>
         </Match>
@@ -2296,6 +2299,65 @@ export function formatCompletedSubagentDetail(toolcalls: number, duration: strin
   return `${formatSubagentToolcalls(toolcalls)} · ${duration}`
   return `${formatSubagentToolcalls(toolcalls)} · ${duration}`
 }
 }
 
 
+type ExecuteCall = { tool: string; status: "running" | "completed" | "error"; input?: Record<string, unknown> }
+
+function executeCalls(value: unknown): ExecuteCall[] {
+  if (!Array.isArray(value)) return []
+  return value.flatMap((call) => {
+    const item = recordValue(call)
+    const tool = stringValue(item?.tool)
+    const status = stringValue(item?.status)
+    if (!tool || !status || !["running", "completed", "error"].includes(status)) return []
+    return [{ tool, status: status as ExecuteCall["status"], input: recordValue(item?.input) }]
+  })
+}
+
+function Execute(props: ToolProps) {
+  const ctx = use()
+  const { theme } = useTheme()
+  const isLoading = createMemo(() => props.part.state.status === "pending" || props.part.state.status === "running")
+  const calls = createMemo(() => executeCalls(props.metadata.toolCalls))
+  const output = createMemo(() => stripAnsi(props.output?.trim() ?? ""))
+  const hasRuntimeError = createMemo(() => props.metadata.error === true)
+  const outputPreview = createMemo(() => collapseToolOutput(output(), 4, 4 * Math.max(20, ctx.width - 6)).output)
+  const showOutput = createMemo(() => output() && hasRuntimeError())
+  const content = createMemo(() => {
+    const lines = ["execute"]
+    for (const call of calls()) {
+      const args = input(call.input ?? {})
+      lines.push(`↳ ${call.tool}${args ? ` ${args}` : ""}${call.status === "error" ? " (failed)" : ""}`)
+    }
+    return lines.join("\n")
+  })
+
+  return (
+    <>
+      <InlineTool
+        icon={hasRuntimeError() ? "✗" : props.part.state.status === "completed" ? "✓" : "│"}
+        color={hasRuntimeError() ? theme.error : undefined}
+        spinner={isLoading()}
+        pending="execute"
+        complete={true}
+        part={props.part}
+      >
+        {content()}
+      </InlineTool>
+      <Show when={showOutput()}>
+        <box paddingLeft={3}>
+          <For each={outputPreview().split("\n")}>
+            {(line, index) => (
+              <text paddingLeft={3} fg={theme.error}>
+                {index() === 0 ? "↳ " : "  "}
+                {line}
+              </text>
+            )}
+          </For>
+        </box>
+      </Show>
+    </>
+  )
+}
+
 function Edit(props: ToolProps) {
 function Edit(props: ToolProps) {
   const ctx = use()
   const ctx = use()
   const { theme, syntax } = useTheme()
   const { theme, syntax } = useTheme()
@@ -2571,6 +2633,7 @@ const toolDisplays = new Set([
   "write",
   "write",
   "edit",
   "edit",
   "subagent",
   "subagent",
+  "execute",
   "apply_patch",
   "apply_patch",
   "todowrite",
   "todowrite",
   "question",
   "question",