From 03747be06d826a07d8124b62819e63952b63d859 Mon Sep 17 00:00:00 2001 From: HiranoMasaaki Date: Thu, 12 Mar 2026 06:36:02 +0900 Subject: [PATCH] fix: handle resolveToolResults event in delegation tree builder Add explicit handling for the resolveToolResults event type in buildRunTreeFromEvents. This event fires after MCP tool execution and after resumeFromStop when all tool results are resolved. Without this handler, these events were silently ignored in the switch statement. Co-Authored-By: Claude Opus 4.6 --- .changeset/resolve-tool-results.md | 5 +++ .../src/log-viewer/build-run-tree.test.ts | 37 +++++++++++++++++++ .../src/log-viewer/build-run-tree.ts | 6 +++ 3 files changed, 48 insertions(+) create mode 100644 .changeset/resolve-tool-results.md diff --git a/.changeset/resolve-tool-results.md b/.changeset/resolve-tool-results.md new file mode 100644 index 00000000..33cde60c --- /dev/null +++ b/.changeset/resolve-tool-results.md @@ -0,0 +1,5 @@ +--- +"@perstack/tui-components": patch +--- + +Handle resolveToolResults event type in delegation tree builder diff --git a/packages/tui-components/src/log-viewer/build-run-tree.test.ts b/packages/tui-components/src/log-viewer/build-run-tree.test.ts index 4bdad4a5..eb5525c7 100644 --- a/packages/tui-components/src/log-viewer/build-run-tree.test.ts +++ b/packages/tui-components/src/log-viewer/build-run-tree.test.ts @@ -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. diff --git a/packages/tui-components/src/log-viewer/build-run-tree.ts b/packages/tui-components/src/log-viewer/build-run-tree.ts index 0fab8f5d..1f582058 100644 --- a/packages/tui-components/src/log-viewer/build-run-tree.ts +++ b/packages/tui-components/src/log-viewer/build-run-tree.ts @@ -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 + } } }