feat(v2): add recursive subagent cost aggregation in agent store and pane

getTotalCost() recursively aggregates costUsd, inputTokens, outputTokens
across parent and all child sessions. AgentPane done-bar displays total
cost in yellow when children are present and total exceeds parent cost.
This commit is contained in:
Hibryda 2026-03-06 17:12:31 +01:00
parent b7f77d8f60
commit 90efeea507
2 changed files with 25 additions and 0 deletions

View file

@ -7,6 +7,7 @@
createAgentSession,
removeAgentSession,
getChildSessions,
getTotalCost,
type AgentSession,
} from '../../stores/agents.svelte';
import { focusPane } from '../../stores/layout.svelte';
@ -41,6 +42,7 @@
let hasToolCalls = $derived(session?.messages.some(m => m.type === 'tool_call') ?? false);
let parentSession = $derived(session?.parentSessionId ? getAgentSession(session.parentSessionId) : undefined);
let childSessions = $derived(session ? getChildSessions(session.id) : []);
let totalCost = $derived(session && childSessions.length > 0 ? getTotalCost(session.id) : null);
const mdRenderer = new Renderer();
mdRenderer.code = function({ text, lang }: { text: string; lang?: string }) {
@ -280,6 +282,9 @@
{:else if session.status === 'done'}
<div class="done-bar">
<span class="cost">${session.costUsd.toFixed(4)}</span>
{#if totalCost && totalCost.costUsd > session.costUsd}
<span class="total-cost">(total: ${totalCost.costUsd.toFixed(4)})</span>
{/if}
<span class="tokens">{session.inputTokens + session.outputTokens} tokens</span>
<span class="duration">{(session.durationMs / 1000).toFixed(1)}s</span>
{#if !autoScroll}
@ -774,6 +779,7 @@
}
.done-bar { color: var(--ctp-green); }
.total-cost { color: var(--ctp-yellow); font-size: 10px; }
.error-bar { color: var(--ctp-red); }
.follow-up {

View file

@ -111,6 +111,25 @@ export function getChildSessions(parentId: string): AgentSession[] {
return sessions.filter(s => s.parentSessionId === parentId);
}
/** Aggregate cost of a session plus all its children (recursive) */
export function getTotalCost(id: string): { costUsd: number; inputTokens: number; outputTokens: number } {
const session = sessions.find(s => s.id === id);
if (!session) return { costUsd: 0, inputTokens: 0, outputTokens: 0 };
let costUsd = session.costUsd;
let inputTokens = session.inputTokens;
let outputTokens = session.outputTokens;
for (const childId of session.childSessionIds) {
const childCost = getTotalCost(childId);
costUsd += childCost.costUsd;
inputTokens += childCost.inputTokens;
outputTokens += childCost.outputTokens;
}
return { costUsd, inputTokens, outputTokens };
}
export function removeAgentSession(id: string): void {
// Also remove from parent's childSessionIds
const session = sessions.find(s => s.id === id);