From c4b02645f5d3843686b261c1604e6eddc25c3066 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 10 Dec 2025 01:37:57 +0000 Subject: [PATCH] fix: persist usage from rpc --- src/agents/agents.test.ts | 11 +++++++++++ src/agents/pi.ts | 20 ++++++++++---------- src/auto-reply/command-reply.ts | 7 +++++-- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/agents/agents.test.ts b/src/agents/agents.test.ts index 5f2d3f584..e513528f6 100644 --- a/src/agents/agents.test.ts +++ b/src/agents/agents.test.ts @@ -57,4 +57,15 @@ describe("pi agent helpers", () => { expect(tool?.toolName).toBe("bash"); expect(tool?.meta).toBe("ls -la"); }); + + it("keeps usage meta even when assistant message has no text", () => { + const stdout = [ + '{"type":"message_start","message":{"role":"assistant"}}', + '{"type":"message_end","message":{"role":"assistant","content":[{"type":"thinking","thinking":"hmm"}],"usage":{"input":10,"output":5},"model":"pi-1","provider":"inflection","stopReason":"end"}}', + ].join("\n"); + const parsed = piSpec.parseOutput(stdout); + expect(parsed.texts?.length ?? 0).toBe(0); + expect((parsed.meta?.usage as { input?: number })?.input).toBe(10); + expect(parsed.meta?.model).toBe("pi-1"); + }); }); diff --git a/src/agents/pi.ts b/src/agents/pi.ts index 5730484c9..027b21ae9 100644 --- a/src/agents/pi.ts +++ b/src/agents/pi.ts @@ -95,10 +95,11 @@ function parsePiJson(raw: string): AgentParseResult { .trim(); if (isAssistantMessage) { + // Always remember the last assistant message so we keep usage/model meta even when no text. + lastAssistant = msg; if (msgText && msgText !== lastPushed) { texts.push(msgText); lastPushed = msgText; - lastAssistant = msg; } } else if (isToolResult && msg.content) { const toolText = msg.content @@ -119,15 +120,14 @@ function parsePiJson(raw: string): AgentParseResult { } } - const meta: AgentMeta | undefined = - lastAssistant && texts.length - ? { - model: lastAssistant.model, - provider: lastAssistant.provider, - stopReason: lastAssistant.stopReason, - usage: lastAssistant.usage, - } - : undefined; + const meta: AgentMeta | undefined = lastAssistant + ? { + model: lastAssistant.model, + provider: lastAssistant.provider, + stopReason: lastAssistant.stopReason, + usage: lastAssistant.usage, + } + : undefined; return { texts, diff --git a/src/auto-reply/command-reply.ts b/src/auto-reply/command-reply.ts index 2fb2e0f87..17d24df71 100644 --- a/src/auto-reply/command-reply.ts +++ b/src/auto-reply/command-reply.ts @@ -59,12 +59,15 @@ function stripRpcNoise(raw: string): string { typeof role === "string" && role.toLowerCase().includes("tool"); if (!isAssistant && !isToolRole) continue; - // Ignore assistant messages that have no text content (pure toolcall scaffolding). + // Ignore assistant messages that have no text content unless they carry usage (final message_end often does). if (msg?.role === "assistant" && Array.isArray(msg?.content)) { const hasText = msg.content.some( (c: unknown) => (c as { type?: string })?.type === "text", ); - if (!hasText) continue; + const hasUsage = + typeof msg?.usage === "object" && + (msg.usage?.input != null || msg.usage?.output != null); + if (!hasText && !hasUsage) continue; } } catch { // not JSON; keep as-is