Fix turn-usage keying: idempotent per request turn id, not messageId
Live smoke exposed two bugs in the turn usage log: - onFinish messages carry no ids (withMessageIds fills copies inside persistTurn), so fresh turns keyed messageId "" — provenance lost. - The SDK coalesces continuation turns into ONE response message (a whole probe conversation lands in a single assistant message), so per-messageId idempotency swallowed every continuation turn's usage. TurnUsage gains id: string (unique per request, the append idempotency key); messageId stays as transcript provenance, taken from the id-filled list. appendTurnUsage dedupes by id; continuation turns with the same messageId each keep their usage.
This commit is contained in:
@@ -93,8 +93,13 @@ export interface CallUsage {
|
||||
* agent steps, and each step bills separately. Capture lands at the persist
|
||||
* seam (server); cost is INFERRED at read time from core/prices.ts. */
|
||||
export interface TurnUsage {
|
||||
/** The assistant message the turn produced — keys this entry to the
|
||||
* transcript (the block this turn "belongs" to). */
|
||||
/** Unique per REQUEST (turn): one invocation of the handler — the
|
||||
* idempotency key for the append-only log. Continuation turns share a
|
||||
* response message id (the SDK coalesces them); keying on messageId
|
||||
* would swallow their usage. */
|
||||
id: string;
|
||||
/** The assistant message the turn produced — provenance, keys this
|
||||
* entry to the transcript (the block this turn "belongs" to). */
|
||||
messageId: string;
|
||||
at: string;
|
||||
/** The OpenRouter model id that ran the turn. */
|
||||
|
||||
+3
-2
@@ -39,8 +39,9 @@ export interface ChatStore {
|
||||
/** Persist the session plan tree (plan tools call this in execute). */
|
||||
saveTree(id: string, tree: PlanTree): void;
|
||||
/** Append one usage entry to the record's turn log (the money channel,
|
||||
* 2026-09): idempotent per messageId — a turn persisted twice (abort
|
||||
* replays, wake-turn retries) appends exactly once. */
|
||||
* 2026-09): idempotent per turn id — each request carries its own, so
|
||||
* a turn persisted twice appends once while continuation turns (which
|
||||
* share a response message id) each keep their usage. */
|
||||
appendTurnUsage(id: string, entry: TurnUsage): void;
|
||||
/** Append one extraction-run usage entry — idempotent per startedAt. */
|
||||
appendExtractionUsage(id: string, entry: ExtractionRunUsage): void;
|
||||
|
||||
Reference in New Issue
Block a user