talon-agent 2.0.1 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "talon-agent",
3
- "version": "2.0.1",
3
+ "version": "2.0.2",
4
4
  "description": "Multi-frontend AI agent with full tool access, streaming, cron jobs, and plugin system",
5
5
  "author": "Dylan Neve",
6
6
  "license": "MIT",
@@ -388,37 +388,27 @@ export async function handleMessage(
388
388
 
389
389
  // Final authoritative usage settlement — shared by the success post-loop
390
390
  // and the terminal-failure path so failed turns account for the tokens
391
- // they burned too. Codex's `turn.completed.usage` is CUMULATIVE, so we
392
- // read the per-call `token_count` event from the rollout JSONL for an
393
- // accurate /status display, falling back silently when unavailable.
391
+ // they burned too. Codex's `turn.completed.usage` is CUMULATIVE across
392
+ // every API call in the turn — never in the per-turn units the shared
393
+ // stream state (and everything downstream: /status, the companion's
394
+ // per-message counts) speaks. The rollout JSONL's totals diffed against
395
+ // the pre-turn baseline are this turn's real usage; that is the ONLY
396
+ // authoritative source. The SDK figure is a last-resort fallback when
397
+ // the rollout can't be read, and it overstates multi-call turns.
394
398
  const settleUsageAccounting = async (): Promise<void> => {
395
- if (usage) {
396
- recordTokens(streamState, {
397
- inputTokens: usage.input_tokens,
398
- outputTokens: usage.output_tokens,
399
- cacheRead: usage.cached_input_tokens,
400
- cacheWrite: 0, // Codex doesn't report cache writes
401
- });
402
- }
403
- if (!resolvedThreadId) return;
404
- const last = await readLastRolloutSnapshot(resolvedThreadId).catch(
405
- () => null,
406
- );
407
- if (!last) return;
408
- if (last.usage) {
399
+ const last = resolvedThreadId
400
+ ? await readLastRolloutSnapshot(resolvedThreadId).catch(() => null)
401
+ : null;
402
+ if (last?.usage) {
409
403
  streamState.contextTokens = last.usage.contextTokens;
410
404
  if (last.usage.contextWindow) {
411
405
  streamState.contextWindow = last.usage.contextWindow;
412
406
  }
413
407
  }
414
- if (typeof last.numApiCalls === "number") {
408
+ if (typeof last?.numApiCalls === "number") {
415
409
  streamState.numApiCalls = last.numApiCalls;
416
410
  }
417
- // Terminator-driven turns abort the stream before `turn.completed`
418
- // fires, so the SDK-side `usage` capture above is null on almost
419
- // every Talon turn. Recover this turn's real usage by diffing the
420
- // rollout's cumulative totals against the pre-turn baseline.
421
- if (!usage && last.totals && baselineTotals) {
411
+ if (last?.totals && baselineTotals) {
422
412
  recordTokens(streamState, {
423
413
  inputTokens: last.totals.inputTokens - baselineTotals.inputTokens,
424
414
  outputTokens: last.totals.outputTokens - baselineTotals.outputTokens,
@@ -426,6 +416,13 @@ export async function handleMessage(
426
416
  last.totals.cachedInputTokens - baselineTotals.cachedInputTokens,
427
417
  cacheWrite: 0, // Codex doesn't report cache writes
428
418
  });
419
+ } else if (usage) {
420
+ recordTokens(streamState, {
421
+ inputTokens: usage.input_tokens,
422
+ outputTokens: usage.output_tokens,
423
+ cacheRead: usage.cached_input_tokens,
424
+ cacheWrite: 0, // Codex doesn't report cache writes
425
+ });
429
426
  }
430
427
  };
431
428