Repository navigation
Description
Problem
Currently, getting full telemetry from the Squad SDK requires ~30 lines of manual wiring per app:
- Import
RuntimeEventBus,CostTracker,recordTokenUsageseparately - Create an
EventBusinstance and pass it toinitSquadTelemetry({ eventBus }) - Create a
CostTrackerand callcostTracker.wireToEventBus(eventBus) - Subscribe to the session's
usageevent and manually forward it to the EventBus as asession:messageevent - Call
recordTokenUsage()yourself to populate the OTel metric counters (squad.tokens.input,squad.tokens.output,squad.tokens.cost) - Print
costTracker.formatSummary()at shutdown
Without all of this, users only see squad.sessions.active and squad.sessions.created in their Aspire/OTel dashboard — no token counts, no cost data, no per-agent breakdowns.
Additionally, the SDK's client.js puts token data as span attributes (tokens.input, tokens.output) on the squad.session.stream span, but never calls recordTokenUsage() to fire the metric counters. So even with a properly configured OTel endpoint, the squad.tokens.* metrics never appear.
Discovery
Found while wiring telemetry across the Squad IRL samples. All 19 samples needed identical boilerplate. The pattern works, but it shouldn't require this much ceremony.
What users have to do today
import { initSquadTelemetry, RuntimeEventBus as EventBus, CostTracker, recordTokenUsage } from '@bradygaster/squad-sdk';
const eventBus = new EventBus();
const telemetry = initSquadTelemetry({ eventBus });
const costTracker = new CostTracker();
costTracker.wireToEventBus(eventBus);
// ... after creating session ...
session.on('usage', (event) => {
const inputTokens = event.inputTokens ?? 0;
const outputTokens = event.outputTokens ?? 0;
const model = event.model ?? 'unknown';
const cost = estimateCost(model, inputTokens, outputTokens); // user must provide pricing!
recordTokenUsage({ type: 'usage', sessionId: session.sessionId, model, inputTokens, outputTokens, estimatedCost: cost, timestamp: new Date() });
eventBus.emit({ type: 'session:message', sessionId: session.sessionId, payload: { inputTokens, outputTokens, model, estimatedCost: cost }, timestamp: new Date() });
});Proposed Solution
initSquadTelemetry() should handle all of this internally. Ideal API:
const telemetry = initSquadTelemetry();
// That's it. Everything lights up.Specifically:
- Auto-create EventBus internally if none is provided
- Auto-wire CostTracker to the internal EventBus
- Have
client.jscallrecordTokenUsage()insendMessage()andsendAndWait()— not just set span attributes - Auto-forward session
usageevents to the EventBus when sessions are created viaSquadClient - Include model pricing in the SDK so users don't have to maintain their own pricing table
- Expose the CostTracker on the returned handle:
telemetry.costTracker.formatSummary()
Bonus: SquadClient integration
If SquadClient accepted an optional eventBus parameter, it could auto-wire session events internally:
const client = new SquadClient({ cwd: process.cwd(), eventBus: telemetry.eventBus });Impact
- Every SDK consumer currently gets incomplete telemetry unless they know the internal architecture
- The
squad.tokens.*OTel metrics are defined but never populated through normal usage paths - Cost reporting requires users to maintain their own model pricing table
Current Workaround
See commit 63bf0b1 in Squad IRL for the full pattern applied across 19 samples.
Activity
github-actions commentedon Mar 9, 2026
📋 Assigned to Telemetry (Aspire & Observability)
Issue: #281 — initSquadTelemetry() should auto-wire EventBus, CostTracker, and token metrics
Telemetry will pick this up in the next Copilot session.
github-actions commentedon Mar 13, 2026
📋 Assigned to Telemetry (Aspire & Observability)
Issue: #281 — initSquadTelemetry() should auto-wire EventBus, CostTracker, and token metrics
Telemetry will pick this up in the next Copilot session.
feat: auto-wire telemetry in initSquadTelemetry() (#281)
feat: auto-wire telemetry in initSquadTelemetry() (github#281) (githu…