From 7681e8b8e2778723bc4e560c4aba9e64d550eea3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=A9=98?= Date: Thu, 4 Jun 2026 22:29:54 +0000 Subject: [PATCH] feat: agent-hermes reports $usage (turns + duration) - Count assistant turns from session messages - Measure wall-clock duration per prompt call - inputTokens/outputTokens remain 0 (ACP protocol doesn't expose token data yet) - Both runPrompt and continueHermes report usage Fixes #76 Refs #68 --- packages/agent-hermes/src/hermes.ts | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/packages/agent-hermes/src/hermes.ts b/packages/agent-hermes/src/hermes.ts index 23c1a43..ad46a2c 100644 --- a/packages/agent-hermes/src/hermes.ts +++ b/packages/agent-hermes/src/hermes.ts @@ -1,4 +1,5 @@ import type { Store } from "@ocas/core"; +import type { Usage } from "@united-workforce/protocol"; import { createLogger } from "@united-workforce/util"; import { type AgentContext, @@ -111,14 +112,27 @@ export function createHermesAgent(resumeDisabled: boolean): () => Promise async function runPrompt(ctx: AgentContext, useContinuation: boolean): Promise { const effectiveCtx = useContinuation ? ctx : { ...ctx, isFirstVisit: true }; const fullPrompt = buildHermesPrompt(effectiveCtx); + const startMs = Date.now(); const { text, sessionId } = await client.prompt(fullPrompt); + const durationSec = (Date.now() - startMs) / 1000; const { detailHash } = await storePromptResult(ctx.store, sessionId); if (!resumeDisabled) { await setCachedSessionId(ctx.threadId, ctx.role, sessionId, ctx.storageRoot); } - return { output: text, detailHash, sessionId, assembledPrompt: fullPrompt, usage: null }; + const session = await loadHermesSession(sessionId); + const turns = + session !== null ? session.messages.filter((m) => m.role === "assistant").length : 1; + + const usage: Usage = { + turns, + inputTokens: 0, + outputTokens: 0, + duration: Math.round(durationSec), + }; + + return { output: text, detailHash, sessionId, assembledPrompt: fullPrompt, usage }; } async function runHermes(ctx: AgentContext): Promise { @@ -147,9 +161,19 @@ export function createHermesAgent(resumeDisabled: boolean): () => Promise ): Promise { // Client is already connected from runHermes — same ACP session, // so the agent sees the full conversation history (crucial for retries). + const startMs = Date.now(); const { text, sessionId } = await client.prompt(message); + const durationSec = (Date.now() - startMs) / 1000; const { detailHash } = await storePromptResult(store, sessionId); - return { output: text, detailHash, sessionId, assembledPrompt: "", usage: null }; + + const usage: Usage = { + turns: 1, + inputTokens: 0, + outputTokens: 0, + duration: Math.round(durationSec), + }; + + return { output: text, detailHash, sessionId, assembledPrompt: "", usage }; } const agentMain = createAgent({ -- 2.43.0