Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/resolve-tool-results.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@perstack/tui-components": patch
---

Handle resolveToolResults event type in delegation tree builder
37 changes: 37 additions & 0 deletions packages/tui-components/src/log-viewer/build-run-tree.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,43 @@ describe("buildRunTreeFromEvents", () => {
expect(gd1Flat.depth).toBe(4) // root > build > te1 > bg1 > gd1
})

it("handles resolveToolResults events without affecting tree structure", () => {
// resolveToolResults fires after MCP tool execution and after resumeFromStop.
// It should be handled gracefully without creating spurious nodes or breaking merges.
const events = [
startRun("root", "coordinator"),
makeEvent("callTools", "root", "coordinator", {
newMessage: {},
toolCalls: [],
usage: baseUsage,
}),
// resolveToolResults after MCP tool execution
makeEvent("resolveToolResults", "root", "coordinator", {
toolResults: [{ id: "tr-1", skillName: "read_file", toolName: "read_file", result: [] }],
}),
stopByDelegate("root", "coordinator"),
startRun("child", "@coordinator/worker", "root"),
completeRun("child", "@coordinator/worker"),
// Resume and immediately resolveToolResults (same timestamp pattern from real data)
resumeFromStop("root-resume", "coordinator"),
makeEvent("resolveToolResults", "root-resume", "coordinator", {
toolResults: [{ id: "tr-2", skillName: "delegate/worker", toolName: "worker", result: [] }],
}),
completeRun("root-resume", "coordinator"),
]

const { treeState, runStats } = buildRunTreeFromEvents(events)

// Resume merged correctly
expect(treeState.nodes.size).toBe(2) // root, child
expect(treeState.nodes.get("root")?.status).toBe("completed")
expect(treeState.nodes.get("child")?.parentRunId).toBe("root")

// resolveToolResults events counted in stats
const rootStats = runStats.get("root")!
expect(rootStats.eventCount).toBeGreaterThanOrEqual(6) // start, callTools, resolve, stop, resume, resolve, complete
})

it("shows failed delegates as error nodes (startRun + stopRunByError)", () => {
// With the runtime fix, failed delegates now emit startRun + stopRunByError events.
// design-roles delegates to 4x find-skill, all fail immediately.
Expand Down
6 changes: 6 additions & 0 deletions packages/tui-components/src/log-viewer/build-run-tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,12 @@ export function buildRunTreeFromEvents(events: RunEvent[]): RunTreeResult {
}
break
}

case "resolveToolResults": {
// Tool results resolved — no node state change needed.
// Stats are already recorded at the top of the loop.
break
}
}
}

Expand Down
Loading