From dec28e5384f8e3b6a697dd2f8bd8d20f57e7493e Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 15 Feb 2026 07:06:54 +0000 Subject: [PATCH] refactor(subagents): share token usage formatting --- src/agents/tools/subagents-tool.ts | 56 ++----------------- src/auto-reply/reply/commands-subagents.ts | 63 ++-------------------- src/shared/subagents-format.ts | 55 +++++++++++++++++++ 3 files changed, 62 insertions(+), 112 deletions(-) diff --git a/src/agents/tools/subagents-tool.ts b/src/agents/tools/subagents-tool.ts index be65760d8..1eafeeb79 100644 --- a/src/agents/tools/subagents-tool.ts +++ b/src/agents/tools/subagents-tool.ts @@ -14,7 +14,8 @@ import { } from "../../routing/session-key.js"; import { formatDurationCompact, - formatTokenShort, + formatTokenUsageDisplay, + resolveTotalTokens, truncateLine, } from "../../shared/subagents-format.js"; import { INTERNAL_MESSAGE_CHANNEL } from "../../utils/message-channel.js"; @@ -138,55 +139,6 @@ function resolveModelDisplay(entry?: SessionEntry, fallbackModel?: string) { return modelRef; } -function resolveTotalTokens(entry?: SessionEntry) { - if (!entry) { - return undefined; - } - if (typeof entry.totalTokens === "number" && Number.isFinite(entry.totalTokens)) { - return entry.totalTokens; - } - const input = typeof entry.inputTokens === "number" ? entry.inputTokens : 0; - const output = typeof entry.outputTokens === "number" ? entry.outputTokens : 0; - const total = input + output; - return total > 0 ? total : undefined; -} - -function resolveIoTokens(entry?: SessionEntry) { - if (!entry) { - return undefined; - } - const input = - typeof entry.inputTokens === "number" && Number.isFinite(entry.inputTokens) - ? entry.inputTokens - : 0; - const output = - typeof entry.outputTokens === "number" && Number.isFinite(entry.outputTokens) - ? entry.outputTokens - : 0; - const total = input + output; - if (total <= 0) { - return undefined; - } - return { input, output, total }; -} - -function resolveUsageDisplay(entry?: SessionEntry) { - const io = resolveIoTokens(entry); - const promptCache = resolveTotalTokens(entry); - const parts: string[] = []; - if (io) { - const input = formatTokenShort(io.input) ?? "0"; - const output = formatTokenShort(io.output) ?? "0"; - parts.push(`tokens ${formatTokenShort(io.total)} (in ${input} / out ${output})`); - } else if (typeof promptCache === "number" && promptCache > 0) { - parts.push(`tokens ${formatTokenShort(promptCache)} prompt/cache`); - } - if (typeof promptCache === "number" && io && promptCache > io.total) { - parts.push(`prompt/cache ${formatTokenShort(promptCache)}`); - } - return parts.join(", "); -} - function resolveSubagentTarget( runs: SubagentRunRecord[], token: string | undefined, @@ -470,7 +422,7 @@ export function createSubagentsTool(opts?: { agentSessionKey?: string }): AnyAge cache, }).entry; const totalTokens = resolveTotalTokens(sessionEntry); - const usageText = resolveUsageDisplay(sessionEntry); + const usageText = formatTokenUsageDisplay(sessionEntry); const status = resolveRunStatus(entry); const runtime = formatDurationCompact(now - (entry.startedAt ?? entry.createdAt)); const label = truncateLine(resolveRunLabel(entry), 48); @@ -501,7 +453,7 @@ export function createSubagentsTool(opts?: { agentSessionKey?: string }): AnyAge cache, }).entry; const totalTokens = resolveTotalTokens(sessionEntry); - const usageText = resolveUsageDisplay(sessionEntry); + const usageText = formatTokenUsageDisplay(sessionEntry); const status = resolveRunStatus(entry); const runtime = formatDurationCompact( (entry.endedAt ?? now) - (entry.startedAt ?? entry.createdAt), diff --git a/src/auto-reply/reply/commands-subagents.ts b/src/auto-reply/reply/commands-subagents.ts index eb076e523..35e3556d3 100644 --- a/src/auto-reply/reply/commands-subagents.ts +++ b/src/auto-reply/reply/commands-subagents.ts @@ -29,7 +29,7 @@ import { formatTimeAgo } from "../../infra/format-time/format-relative.ts"; import { parseAgentSessionKey } from "../../routing/session-key.js"; import { formatDurationCompact, - formatTokenShort, + formatTokenUsageDisplay, truncateLine, } from "../../shared/subagents-format.js"; import { INTERNAL_MESSAGE_CHANNEL } from "../../utils/message-channel.js"; @@ -97,63 +97,6 @@ function resolveModelDisplay( return combined; } -function resolveTotalTokens(entry?: { - totalTokens?: unknown; - inputTokens?: unknown; - outputTokens?: unknown; -}) { - if (!entry || typeof entry !== "object") { - return undefined; - } - if (typeof entry.totalTokens === "number" && Number.isFinite(entry.totalTokens)) { - return entry.totalTokens; - } - const input = typeof entry.inputTokens === "number" ? entry.inputTokens : 0; - const output = typeof entry.outputTokens === "number" ? entry.outputTokens : 0; - const total = input + output; - return total > 0 ? total : undefined; -} - -function resolveIoTokens(entry?: { inputTokens?: unknown; outputTokens?: unknown }) { - if (!entry || typeof entry !== "object") { - return undefined; - } - const input = - typeof entry.inputTokens === "number" && Number.isFinite(entry.inputTokens) - ? entry.inputTokens - : 0; - const output = - typeof entry.outputTokens === "number" && Number.isFinite(entry.outputTokens) - ? entry.outputTokens - : 0; - const total = input + output; - if (total <= 0) { - return undefined; - } - return { input, output, total }; -} - -function resolveUsageDisplay(entry?: { - totalTokens?: unknown; - inputTokens?: unknown; - outputTokens?: unknown; -}) { - const io = resolveIoTokens(entry); - const promptCache = resolveTotalTokens(entry); - const parts: string[] = []; - if (io) { - const input = formatTokenShort(io.input) ?? "0"; - const output = formatTokenShort(io.output) ?? "0"; - parts.push(`tokens ${formatTokenShort(io.total)} (in ${input} / out ${output})`); - } else if (typeof promptCache === "number" && promptCache > 0) { - parts.push(`tokens ${formatTokenShort(promptCache)} prompt/cache`); - } - if (typeof promptCache === "number" && io && promptCache > io.total) { - parts.push(`prompt/cache ${formatTokenShort(promptCache)}`); - } - return parts.join(", "); -} - function resolveDisplayStatus(entry: SubagentRunRecord) { const status = formatRunStatus(entry); return status === "error" ? "failed" : status; @@ -394,7 +337,7 @@ export const handleSubagentsCommand: CommandHandler = async (params, allowTextCo entry.childSessionKey, storeCache, ); - const usageText = resolveUsageDisplay(sessionEntry); + const usageText = formatTokenUsageDisplay(sessionEntry); const label = truncateLine(formatRunLabel(entry, { maxLength: 48 }), 48); const task = formatTaskPreview(entry.task); const runtime = formatDurationCompact(now - (entry.startedAt ?? entry.createdAt)); @@ -411,7 +354,7 @@ export const handleSubagentsCommand: CommandHandler = async (params, allowTextCo entry.childSessionKey, storeCache, ); - const usageText = resolveUsageDisplay(sessionEntry); + const usageText = formatTokenUsageDisplay(sessionEntry); const label = truncateLine(formatRunLabel(entry, { maxLength: 48 }), 48); const task = formatTaskPreview(entry.task); const runtime = formatDurationCompact( diff --git a/src/shared/subagents-format.ts b/src/shared/subagents-format.ts index 94f632a57..f31ec9e9d 100644 --- a/src/shared/subagents-format.ts +++ b/src/shared/subagents-format.ts @@ -39,3 +39,58 @@ export function truncateLine(value: string, maxLength: number) { } return `${value.slice(0, maxLength).trimEnd()}...`; } + +export type TokenUsageLike = { + totalTokens?: unknown; + inputTokens?: unknown; + outputTokens?: unknown; +}; + +export function resolveTotalTokens(entry?: TokenUsageLike) { + if (!entry || typeof entry !== "object") { + return undefined; + } + if (typeof entry.totalTokens === "number" && Number.isFinite(entry.totalTokens)) { + return entry.totalTokens; + } + const input = typeof entry.inputTokens === "number" ? entry.inputTokens : 0; + const output = typeof entry.outputTokens === "number" ? entry.outputTokens : 0; + const total = input + output; + return total > 0 ? total : undefined; +} + +export function resolveIoTokens(entry?: TokenUsageLike) { + if (!entry || typeof entry !== "object") { + return undefined; + } + const input = + typeof entry.inputTokens === "number" && Number.isFinite(entry.inputTokens) + ? entry.inputTokens + : 0; + const output = + typeof entry.outputTokens === "number" && Number.isFinite(entry.outputTokens) + ? entry.outputTokens + : 0; + const total = input + output; + if (total <= 0) { + return undefined; + } + return { input, output, total }; +} + +export function formatTokenUsageDisplay(entry?: TokenUsageLike) { + const io = resolveIoTokens(entry); + const promptCache = resolveTotalTokens(entry); + const parts: string[] = []; + if (io) { + const input = formatTokenShort(io.input) ?? "0"; + const output = formatTokenShort(io.output) ?? "0"; + parts.push(`tokens ${formatTokenShort(io.total)} (in ${input} / out ${output})`); + } else if (typeof promptCache === "number" && promptCache > 0) { + parts.push(`tokens ${formatTokenShort(promptCache)} prompt/cache`); + } + if (typeof promptCache === "number" && io && promptCache > io.total) { + parts.push(`prompt/cache ${formatTokenShort(promptCache)}`); + } + return parts.join(", "); +}