From 78538b18aac0c268a64a874e80eb9381908f2456 Mon Sep 17 00:00:00 2001 From: nightfield Date: Wed, 24 Jun 2026 11:23:11 +0800 Subject: [PATCH 1/2] fix agent name showing 'unknown' issue --- src/index.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index 87fafd7..11e6cd1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -183,9 +183,14 @@ export const OtelPlugin: Plugin = async ({ project, client, directory, worktree "chat.message": safe("chat.message", async (input, output) => { const agent = input.agent ?? "unknown" + let totals = sessionTotals.get(input.sessionID) + if (totals) { + if (input.agent) totals.agent = input.agent + } else { + totals = { startMs: Date.now(), tokens: 0, cost: 0, messages: 0, agent, agentType: "primary" } + sessionTotals.set(input.sessionID, totals) + } const { agentType } = getSessionAgentMeta(input.sessionID, ctx) - const totals = sessionTotals.get(input.sessionID) - if (totals) totals.agent = agent const sessionSpan = sessionSpans.get(input.sessionID) if (sessionSpan) sessionSpan.setAttributes({ [AGENT_NAME]: agent, "agent.type": agentType }) const promptText = output.parts.map((part) => { From bfcc4077c824e8a9fb95dbc1c2b27de9ba7e45c8 Mon Sep 17 00:00:00 2001 From: nightfield Date: Wed, 24 Jun 2026 11:35:43 +0800 Subject: [PATCH 2/2] fix: initialize sessionTotals for existing sessions in chat.message hook --- src/handlers/message.ts | 27 +++++++++++++++++++++++++ src/index.ts | 10 ++-------- tests/handlers/message.test.ts | 36 +++++++++++++++++++++++++++++++++- 3 files changed, 64 insertions(+), 9 deletions(-) diff --git a/src/handlers/message.ts b/src/handlers/message.ts index f6dce25..473146a 100644 --- a/src/handlers/message.ts +++ b/src/handlers/message.ts @@ -453,3 +453,30 @@ export function startMessageSpan( ) setBoundedMap(ctx.messageSpans, msgKey, msgSpan) } + +/** + * Initialises or updates the per-session agent metadata when a new user prompt + * arrives. Creates a fresh `SessionTotals` entry if none exists for this session + * (e.g. the session was started before the current plugin process). Updates the + * agent name on subsequent messages within the same session. + */ +export function handleChatMessage( + sessionID: string, + agent: string | undefined, + ctx: Pick, +) { + const agentName = agent ?? "unknown" + const totals = ctx.sessionTotals.get(sessionID) + if (totals) { + if (agent) totals.agent = agentName + } else { + ctx.sessionTotals.set(sessionID, { + startMs: Date.now(), + tokens: 0, + cost: 0, + messages: 0, + agent: agentName, + agentType: "primary", + }) + } +} diff --git a/src/index.ts b/src/index.ts index 11e6cd1..bed8088 100644 --- a/src/index.ts +++ b/src/index.ts @@ -22,7 +22,7 @@ import { probeEndpoint } from "./probe.ts" import { setupOtel, createInstruments } from "./otel.ts" import { remoteParentContext } from "./trace-context.ts" import { handleSessionCreated, handleSessionIdle, handleSessionError, handleSessionStatus } from "./handlers/session.ts" -import { handleMessageUpdated, handleMessagePartUpdated, startMessageSpan } from "./handlers/message.ts" +import { handleMessageUpdated, handleMessagePartUpdated, startMessageSpan, handleChatMessage } from "./handlers/message.ts" import { handlePermissionUpdated, handlePermissionReplied } from "./handlers/permission.ts" import { handleSessionDiff, handleCommandExecuted } from "./handlers/activity.ts" import { agentAttrs, getSessionAgentMeta } from "./util.ts" @@ -183,13 +183,7 @@ export const OtelPlugin: Plugin = async ({ project, client, directory, worktree "chat.message": safe("chat.message", async (input, output) => { const agent = input.agent ?? "unknown" - let totals = sessionTotals.get(input.sessionID) - if (totals) { - if (input.agent) totals.agent = input.agent - } else { - totals = { startMs: Date.now(), tokens: 0, cost: 0, messages: 0, agent, agentType: "primary" } - sessionTotals.set(input.sessionID, totals) - } + handleChatMessage(input.sessionID, input.agent, ctx) const { agentType } = getSessionAgentMeta(input.sessionID, ctx) const sessionSpan = sessionSpans.get(input.sessionID) if (sessionSpan) sessionSpan.setAttributes({ [AGENT_NAME]: agent, "agent.type": agentType }) diff --git a/tests/handlers/message.test.ts b/tests/handlers/message.test.ts index f5afb39..b5a484c 100644 --- a/tests/handlers/message.test.ts +++ b/tests/handlers/message.test.ts @@ -1,5 +1,5 @@ import { describe, test, expect } from "bun:test" -import { handleMessageUpdated, handleMessagePartUpdated } from "../../src/handlers/message.ts" +import { handleMessageUpdated, handleMessagePartUpdated, handleChatMessage } from "../../src/handlers/message.ts" import { makeCtx } from "../helpers.ts" import type { EventMessageUpdated, EventMessagePartUpdated } from "@opencode-ai/sdk" @@ -239,6 +239,40 @@ describe("handleMessageUpdated", () => { }) }) +describe("handleChatMessage", () => { + test("creates sessionTotals entry with agent when none exists", () => { + const { ctx } = makeCtx() + handleChatMessage("ses_no_totals", "build", ctx) + const totals = ctx.sessionTotals.get("ses_no_totals")! + expect(totals.agent).toBe("build") + expect(totals.agentType).toBe("primary") + expect(totals.tokens).toBe(0) + expect(totals.cost).toBe(0) + expect(totals.messages).toBe(0) + expect(totals.startMs).toBeGreaterThan(0) + }) + + test("defaults agent to 'unknown' when agent is undefined", () => { + const { ctx } = makeCtx() + handleChatMessage("ses_no_agent", undefined, ctx) + expect(ctx.sessionTotals.get("ses_no_agent")!.agent).toBe("unknown") + }) + + test("updates agent on existing entry", () => { + const { ctx } = makeCtx() + ctx.sessionTotals.set("ses_1", { startMs: 1000, tokens: 100, cost: 0.01, messages: 1, agent: "build", agentType: "primary" }) + handleChatMessage("ses_1", "plan", ctx) + expect(ctx.sessionTotals.get("ses_1")!.agent).toBe("plan") + }) + + test("does not override agent with 'unknown' on existing entry when called without agent", () => { + const { ctx } = makeCtx() + ctx.sessionTotals.set("ses_1", { startMs: 1000, tokens: 100, cost: 0.01, messages: 1, agent: "build", agentType: "primary" }) + handleChatMessage("ses_1", undefined, ctx) + expect(ctx.sessionTotals.get("ses_1")!.agent).toBe("build") + }) +}) + describe("handleMessagePartUpdated", () => { test("ignores non-tool parts", async () => { const { ctx, histograms } = makeCtx()