fix(tui): show terminal tool failure labels (#31934)
This commit is contained in:
@@ -1831,6 +1831,7 @@ function InlineTool(props: {
|
|||||||
color?: RGBA
|
color?: RGBA
|
||||||
complete: unknown
|
complete: unknown
|
||||||
pending: string
|
pending: string
|
||||||
|
failure?: string
|
||||||
spinner?: boolean
|
spinner?: boolean
|
||||||
subagent?: boolean
|
subagent?: boolean
|
||||||
children: JSX.Element
|
children: JSX.Element
|
||||||
@@ -1884,6 +1885,7 @@ function InlineTool(props: {
|
|||||||
errorExpanded={errorExpanded()}
|
errorExpanded={errorExpanded()}
|
||||||
complete={props.complete}
|
complete={props.complete}
|
||||||
pending={props.pending}
|
pending={props.pending}
|
||||||
|
failure={props.failure}
|
||||||
spinner={props.spinner}
|
spinner={props.spinner}
|
||||||
subagent={props.subagent}
|
subagent={props.subagent}
|
||||||
separateAfter={(id) => id !== undefined && ctx.userMessageIDs().has(id)}
|
separateAfter={(id) => id !== undefined && ctx.userMessageIDs().has(id)}
|
||||||
@@ -1915,6 +1917,7 @@ export function InlineToolRow(props: {
|
|||||||
errorExpanded?: boolean
|
errorExpanded?: boolean
|
||||||
complete: unknown
|
complete: unknown
|
||||||
pending: string
|
pending: string
|
||||||
|
failure?: string
|
||||||
spinner?: boolean
|
spinner?: boolean
|
||||||
subagent?: boolean
|
subagent?: boolean
|
||||||
children: JSX.Element
|
children: JSX.Element
|
||||||
@@ -1958,7 +1961,7 @@ export function InlineToolRow(props: {
|
|||||||
~ {props.pending}
|
~ {props.pending}
|
||||||
</text>
|
</text>
|
||||||
}
|
}
|
||||||
when={props.complete}
|
when={props.complete || props.failed}
|
||||||
>
|
>
|
||||||
<box flexDirection="row">
|
<box flexDirection="row">
|
||||||
<text
|
<text
|
||||||
@@ -1973,7 +1976,7 @@ export function InlineToolRow(props: {
|
|||||||
fg={props.failed ? props.errorColor : props.color}
|
fg={props.failed ? props.errorColor : props.color}
|
||||||
attributes={props.denied ? TextAttributes.STRIKETHROUGH : undefined}
|
attributes={props.denied ? TextAttributes.STRIKETHROUGH : undefined}
|
||||||
>
|
>
|
||||||
{props.children}
|
{props.failed && !props.complete ? (props.failure ?? props.children) : props.children}
|
||||||
</text>
|
</text>
|
||||||
</box>
|
</box>
|
||||||
</Show>
|
</Show>
|
||||||
@@ -2445,7 +2448,7 @@ function ApplyPatch(props: ToolProps) {
|
|||||||
</For>
|
</For>
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={true}>
|
<Match when={true}>
|
||||||
<InlineTool icon="%" pending="Preparing patch..." complete={false} part={props.part}>
|
<InlineTool icon="%" pending="Preparing patch..." failure="Patch failed" complete={false} part={props.part}>
|
||||||
Patch
|
Patch
|
||||||
</InlineTool>
|
</InlineTool>
|
||||||
</Match>
|
</Match>
|
||||||
@@ -2465,7 +2468,13 @@ function TodoWrite(props: ToolProps) {
|
|||||||
</BlockTool>
|
</BlockTool>
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={true}>
|
<Match when={true}>
|
||||||
<InlineTool icon="⚙" pending="Updating todos..." complete={false} part={props.part}>
|
<InlineTool
|
||||||
|
icon="⚙"
|
||||||
|
pending="Updating todos..."
|
||||||
|
failure="Todo update failed"
|
||||||
|
complete={false}
|
||||||
|
part={props.part}
|
||||||
|
>
|
||||||
Updating todos...
|
Updating todos...
|
||||||
</InlineTool>
|
</InlineTool>
|
||||||
</Match>
|
</Match>
|
||||||
|
|||||||
@@ -155,6 +155,34 @@ function StickyScrollFixture(props: { separated: boolean; scroll: (scroll: Scrol
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function FailedPendingToolFixture() {
|
||||||
|
return (
|
||||||
|
<InlineToolRow
|
||||||
|
icon="%"
|
||||||
|
complete={false}
|
||||||
|
pending="Preparing patch..."
|
||||||
|
failed={true}
|
||||||
|
failure="Patch failed"
|
||||||
|
>
|
||||||
|
Patch
|
||||||
|
</InlineToolRow>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function FailedCompleteToolFixture() {
|
||||||
|
return (
|
||||||
|
<InlineToolRow
|
||||||
|
icon="→"
|
||||||
|
complete={true}
|
||||||
|
pending="Reading file..."
|
||||||
|
failed={true}
|
||||||
|
failure="Read failed"
|
||||||
|
>
|
||||||
|
Read src/index.ts
|
||||||
|
</InlineToolRow>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
async function renderFrame(component: () => JSX.Element, options: { width: number; height: number }) {
|
async function renderFrame(component: () => JSX.Element, options: { width: number; height: number }) {
|
||||||
testSetup = await testRender(component, options)
|
testSetup = await testRender(component, options)
|
||||||
await testSetup.renderOnce()
|
await testSetup.renderOnce()
|
||||||
@@ -173,6 +201,18 @@ describe("TUI inline tool wrapping", () => {
|
|||||||
expect(toolDisplay("plugin_tool")).toBe("generic")
|
expect(toolDisplay("plugin_tool")).toBe("generic")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("replaces pending copy when a tool fails before completion", async () => {
|
||||||
|
const frame = await renderFrame(() => <FailedPendingToolFixture />, { width: 72, height: 3 })
|
||||||
|
expect(frame).toContain("Patch failed")
|
||||||
|
expect(frame).not.toContain("Preparing patch")
|
||||||
|
})
|
||||||
|
|
||||||
|
test("preserves useful completed copy when a tool fails", async () => {
|
||||||
|
const frame = await renderFrame(() => <FailedCompleteToolFixture />, { width: 72, height: 3 })
|
||||||
|
expect(frame).toContain("Read src/index.ts")
|
||||||
|
expect(frame).not.toContain("Read failed")
|
||||||
|
})
|
||||||
|
|
||||||
test("filters malformed nested tool wire data", () => {
|
test("filters malformed nested tool wire data", () => {
|
||||||
expect(
|
expect(
|
||||||
parseApplyPatchFiles([
|
parseApplyPatchFiles([
|
||||||
|
|||||||
Reference in New Issue
Block a user