diff --git a/package.json b/package.json index 80b868f4..d39e6145 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,9 @@ "overrides": { "@types/react": "catalog:", "@types/react-dom": "catalog:" + }, + "patchedDependencies": { + "@mariozechner/pi-agent-core@0.52.9": "patches/@mariozechner__pi-agent-core@0.52.9.patch" } }, "devDependencies": { diff --git a/packages/core/src/agent/runner.ts b/packages/core/src/agent/runner.ts index ba652939..c9f81f03 100644 --- a/packages/core/src/agent/runner.ts +++ b/packages/core/src/agent/runner.ts @@ -114,7 +114,7 @@ function extractRunLogResultDetails(result: unknown): Record | function formatRunLogToolSummary(tool: string, details: Record | null): string | undefined { if (!details) return undefined; - if (details.error) return `error: ${details.message || details.error}`; + if (details.error) return `error: ${details.code || details.message || details.error}`; switch (tool) { case "web_search": return `${details.count ?? 0} results`; case "web_fetch": { @@ -780,17 +780,19 @@ export class Agent { private handleRunLogEvent(event: AgentEvent) { if (event.type === "tool_execution_start") { + const toolCallId = (event as any).toolCallId ?? "unknown"; const toolName = (event as any).toolName ?? "unknown"; - this.toolStartTimes.set(toolName, Date.now()); + this.toolStartTimes.set(toolCallId, Date.now()); this.runLog.log("tool_start", { tool: toolName, args: JSON.stringify((event as any).args ?? {}).slice(0, 500), }); } else if (event.type === "tool_execution_end") { + const toolCallId = (event as any).toolCallId ?? "unknown"; const toolName = (event as any).toolName ?? "unknown"; - const startTime = this.toolStartTimes.get(toolName); + const startTime = this.toolStartTimes.get(toolCallId); const duration_ms = startTime ? Date.now() - startTime : undefined; - this.toolStartTimes.delete(toolName); + this.toolStartTimes.delete(toolCallId); // Extract result metadata for run-log persistence (survives session compaction) const result = (event as any).result; @@ -806,7 +808,7 @@ export class Agent { result_summary: formatRunLogToolSummary(toolName, details), }; if (details?.error) { - toolEndData.error_type = String(details.error); + toolEndData.error_type = details.code ? String(details.code) : String(details.error); } this.runLog.log("tool_end", toolEndData); } diff --git a/patches/@mariozechner__pi-agent-core@0.52.9.patch b/patches/@mariozechner__pi-agent-core@0.52.9.patch new file mode 100644 index 00000000..e570a653 --- /dev/null +++ b/patches/@mariozechner__pi-agent-core@0.52.9.patch @@ -0,0 +1,80 @@ +diff --git a/dist/agent-loop.js b/dist/agent-loop.js +index b26d45753809da14df6409354e8c537684b9acd7..931399cc12bafea940fe99ab74ae288d71506e52 100644 +--- a/dist/agent-loop.js ++++ b/dist/agent-loop.js +@@ -206,17 +206,18 @@ async function streamAssistantResponse(context, config, signal, stream, streamFn + */ + async function executeToolCalls(tools, assistantMessage, signal, stream, getSteeringMessages) { + const toolCalls = assistantMessage.content.filter((c) => c.type === "toolCall"); +- const results = []; +- let steeringMessages; +- for (let index = 0; index < toolCalls.length; index++) { +- const toolCall = toolCalls[index]; +- const tool = tools?.find((t) => t.name === toolCall.name); ++ // Emit all tool_execution_start events immediately ++ for (const toolCall of toolCalls) { + stream.push({ + type: "tool_execution_start", + toolCallId: toolCall.id, + toolName: toolCall.name, + args: toolCall.arguments, + }); ++ } ++ // Execute all tools in parallel ++ const settled = await Promise.allSettled(toolCalls.map(async (toolCall) => { ++ const tool = tools?.find((t) => t.name === toolCall.name); + let result; + let isError = false; + try { +@@ -240,6 +241,27 @@ async function executeToolCalls(tools, assistantMessage, signal, stream, getStee + }; + isError = true; + } ++ return { result, isError }; ++ })); ++ // Process results IN ORIGINAL ORDER (critical for LLM context) ++ const results = []; ++ let steeringMessages; ++ for (let i = 0; i < settled.length; i++) { ++ const entry = settled[i]; ++ const toolCall = toolCalls[i]; ++ let result; ++ let isError; ++ if (entry.status === "fulfilled") { ++ result = entry.value.result; ++ isError = entry.value.isError; ++ } ++ else { ++ result = { ++ content: [{ type: "text", text: entry.reason instanceof Error ? entry.reason.message : String(entry.reason) }], ++ details: {}, ++ }; ++ isError = true; ++ } + stream.push({ + type: "tool_execution_end", + toolCallId: toolCall.id, +@@ -259,17 +281,12 @@ async function executeToolCalls(tools, assistantMessage, signal, stream, getStee + results.push(toolResultMessage); + stream.push({ type: "message_start", message: toolResultMessage }); + stream.push({ type: "message_end", message: toolResultMessage }); +- // Check for steering messages - skip remaining tools if user interrupted +- if (getSteeringMessages) { +- const steering = await getSteeringMessages(); +- if (steering.length > 0) { +- steeringMessages = steering; +- const remainingCalls = toolCalls.slice(index + 1); +- for (const skipped of remainingCalls) { +- results.push(skipToolCall(skipped, stream)); +- } +- break; +- } ++ } ++ // Check steering messages once after all tools complete ++ if (getSteeringMessages) { ++ const steering = await getSteeringMessages(); ++ if (steering.length > 0) { ++ steeringMessages = steering; + } + } + return { toolResults: results, steeringMessages }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a0221107..18c13e8b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -68,13 +68,18 @@ overrides: '@types/react': ^19.2.0 '@types/react-dom': ^19.2.0 +patchedDependencies: + '@mariozechner/pi-agent-core@0.52.9': + hash: befbe3b9fd1a6d3e13dfa4927d9f0addd2f3b3454bd9d407c16dacb9a71c0f00 + path: patches/@mariozechner__pi-agent-core@0.52.9.patch + importers: .: dependencies: '@mariozechner/pi-agent-core': specifier: 'catalog:' - version: 0.52.9(@modelcontextprotocol/sdk@1.26.0(zod@4.3.6))(ws@8.19.0)(zod@4.3.6) + version: 0.52.9(patch_hash=befbe3b9fd1a6d3e13dfa4927d9f0addd2f3b3454bd9d407c16dacb9a71c0f00)(@modelcontextprotocol/sdk@1.26.0(zod@4.3.6))(ws@8.19.0)(zod@4.3.6) '@mariozechner/pi-ai': specifier: 'catalog:' version: 0.52.9(@modelcontextprotocol/sdk@1.26.0(zod@4.3.6))(ws@8.19.0)(zod@4.3.6) @@ -591,7 +596,7 @@ importers: dependencies: '@mariozechner/pi-agent-core': specifier: 'catalog:' - version: 0.52.9(@modelcontextprotocol/sdk@1.26.0(zod@4.3.6))(ws@8.19.0)(zod@4.3.6) + version: 0.52.9(patch_hash=befbe3b9fd1a6d3e13dfa4927d9f0addd2f3b3454bd9d407c16dacb9a71c0f00)(@modelcontextprotocol/sdk@1.26.0(zod@4.3.6))(ws@8.19.0)(zod@4.3.6) '@mariozechner/pi-ai': specifier: 'catalog:' version: 0.52.9(@modelcontextprotocol/sdk@1.26.0(zod@4.3.6))(ws@8.19.0)(zod@4.3.6) @@ -690,7 +695,7 @@ importers: devDependencies: '@mariozechner/pi-agent-core': specifier: 'catalog:' - version: 0.52.9(@modelcontextprotocol/sdk@1.26.0(zod@4.3.6))(ws@8.19.0)(zod@4.3.6) + version: 0.52.9(patch_hash=befbe3b9fd1a6d3e13dfa4927d9f0addd2f3b3454bd9d407c16dacb9a71c0f00)(@modelcontextprotocol/sdk@1.26.0(zod@4.3.6))(ws@8.19.0)(zod@4.3.6) '@mariozechner/pi-ai': specifier: 'catalog:' version: 0.52.9(@modelcontextprotocol/sdk@1.26.0(zod@4.3.6))(ws@8.19.0)(zod@4.3.6) @@ -12498,7 +12503,7 @@ snapshots: std-env: 3.10.0 yoctocolors: 2.1.2 - '@mariozechner/pi-agent-core@0.52.9(@modelcontextprotocol/sdk@1.26.0(zod@4.3.6))(ws@8.19.0)(zod@4.3.6)': + '@mariozechner/pi-agent-core@0.52.9(patch_hash=befbe3b9fd1a6d3e13dfa4927d9f0addd2f3b3454bd9d407c16dacb9a71c0f00)(@modelcontextprotocol/sdk@1.26.0(zod@4.3.6))(ws@8.19.0)(zod@4.3.6)': dependencies: '@mariozechner/pi-ai': 0.52.9(@modelcontextprotocol/sdk@1.26.0(zod@4.3.6))(ws@8.19.0)(zod@4.3.6) transitivePeerDependencies: @@ -12537,7 +12542,7 @@ snapshots: '@mariozechner/pi-coding-agent@0.52.9(@modelcontextprotocol/sdk@1.26.0(zod@4.3.6))(ws@8.19.0)(zod@4.3.6)': dependencies: '@mariozechner/jiti': 2.6.5 - '@mariozechner/pi-agent-core': 0.52.9(@modelcontextprotocol/sdk@1.26.0(zod@4.3.6))(ws@8.19.0)(zod@4.3.6) + '@mariozechner/pi-agent-core': 0.52.9(patch_hash=befbe3b9fd1a6d3e13dfa4927d9f0addd2f3b3454bd9d407c16dacb9a71c0f00)(@modelcontextprotocol/sdk@1.26.0(zod@4.3.6))(ws@8.19.0)(zod@4.3.6) '@mariozechner/pi-ai': 0.52.9(@modelcontextprotocol/sdk@1.26.0(zod@4.3.6))(ws@8.19.0)(zod@4.3.6) '@mariozechner/pi-tui': 0.52.9 '@silvia-odwyer/photon-node': 0.3.4 @@ -15944,9 +15949,9 @@ snapshots: '@typescript-eslint/eslint-plugin': 8.55.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/parser': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.2(jiti@2.6.1) - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-expo: 1.0.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-react-hooks: 5.2.0(eslint@9.39.2(jiti@2.6.1)) globals: 16.5.0 @@ -15961,8 +15966,8 @@ snapshots: '@next/eslint-plugin-next': 16.1.6 eslint: 9.39.2(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-react-hooks: 7.0.1(eslint@9.39.2(jiti@2.6.1)) @@ -15984,7 +15989,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 @@ -15995,18 +16000,18 @@ snapshots: tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.2(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)) transitivePeerDependencies: - supports-color @@ -16019,7 +16024,7 @@ snapshots: - supports-color - typescript - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -16030,7 +16035,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.2(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3