Cost tracking
agent-mux emits a cost event whenever an adapter reports usage. You can
read it in two ways: live (during client.run) or retroactively
(from a stored session).
Live: per-run budget meter
import { createClient } from '@a5c-ai/agent-mux';
const client = createClient();
const handle = client.run({ agent: 'claude-code', prompt: '…' });
let spent = 0;
for await (const ev of handle.events()) {
if (ev.type === 'cost') {
spent = ev.cost.totalUsd ?? spent;
if (spent > 0.50) { await handle.stop('budget exceeded'); break; }
}
}
await handle.done;
The final total is also available as (await handle.done).cost.
Retroactive: aggregate a stored session
client.sessions(agent).read(id) returns the replayed event list — the
same events the live stream produced. Fold them with the exported helper:
import { createClient, sumCost } from '@a5c-ai/agent-mux';
const sessions = createClient().sessions('claude-code');
const { events } = await sessions.read(sessionId);
const totals = sumCost(events);
console.log(`$${totals.totalUsd.toFixed(4)} · ${totals.totalTokens} tok`);
sumCost accepts any Iterable<AgentEvent>. The async variant
sumCostAsync works directly on handle.events():
import { sumCostAsync } from '@a5c-ai/agent-mux';
const totals = await sumCostAsync(handle.events());
Filter helpers
filterEvents(events, type) returns a narrowed iterator — useful when you
want every tool result, every thinking delta, etc., without writing the
switch yourself:
import { filterEvents } from '@a5c-ai/agent-mux';
for (const tool of filterEvents(events, 'tool_result')) {
// tool.toolName, tool.output, tool.durationMs are all typed
}
CLI
# Sum cost across every stored session for an agent
amux sessions list --agent claude-code --json \
| jq -r '.[].sessionId' \
| while read sid; do
amux sessions read --agent claude-code --id "$sid" --json \
| jq '[.events[]|select(.type=="cost")|.cost.totalUsd]|add // 0'
done \
| paste -sd+ | bc
Caveats
- Not every adapter reports USD. Adapters without pricing data emit
costevents withtotalUsd: 0and token counts only. - Cached-token pricing is adapter-specific;
sumCostexposescachedTokensseparately so you can apply your own multiplier. costevents may fire multiple times in one run (turn-level and run-level).sumCostadds them all — that's by design: the sum is the run total. If you only want the final event, use(await handle.done).cost.