/** * The `sessionStats` projection unit: a pure fold of step boundaries, stream * chunks, tool pairs, and assembled assistant messages into whole-log counts * and wall times. * * `step/end` — not `assistant/message` — is the counted step event because it * is the step lifecycle authority: the loop appends exactly one per entered * step, in a `finally`, so completed, failed, cancelled, and max-tokens steps * all land one. Counting assembled assistant messages instead would overcount * max-tokens usage-host messages (empty content, excluded from the surface) * and undercount cancelled steps (aborted before the message assembles). * * The wall-time folds mirror the client window fold field by field * (`deriveStats` in dsh-client-ui-conversation, that fold's whole-window * fallback role): model time is `step/start` → `assistant/message`, first * token is the first non-empty delta chunk and survives an in-step * `llm/retry`, decode spans first token → assembled message on steps that * also report output tokens, and tool time pairs `tool/call` → `tool/result` * by callId. A cancelled step assembles no message, so its partial stream * time stays uncounted in every time figure — matching the window, which * renders it as an untimed interrupted node. * * @module @deepseek-ai/dsh-session-stats/projection */ import type { ProjectionDefinition } from '@deepseek-ai/dsh-session-projection'; /** Accumulated whole-log figures (the view is exactly these totals). */ interface SessionStatsTotals { /** Distinct turns with at least one closed step so far. */ turns: number; /** Closed steps so far. */ steps: number; /** Summed model wall time over message-assembling steps, ms. */ llmMs: number; /** Summed matched tool call→result wall time, ms. */ toolMs: number; /** Summed first-token latency over `ttftSteps`, ms. */ ttftMs: number; /** Steps carrying a recorded first token. */ ttftSteps: number; /** Summed decode wall time over usage-reporting steps, ms. */ decodeMs: number; /** Summed provider output tokens over the same steps. */ decodeTokens: number; } /** * Fold state: the totals plus the in-flight boundaries they accrue from. * Turn numbers are host-assigned and monotonic per session, so a single * `lastTurn` slot decides "first closed step of a new turn"; the state is * plain JSON per the unit contract (persisted-cache precondition). */ interface SessionStatsState extends SessionStatsTotals { /** Turn of the last counted `step/end`; null before the first. */ lastTurn: number | null; /** The open step's boundary facts; null outside a step or after its message assembled. */ openStep: { turn: number; step: number; startTime: number; firstTokenTime: number | null; } | null; /** Dispatch times of tool calls whose result has not landed, by callId. */ pendingCalls: Record; } /** The `sessionStats` unit registered on `ctx.sessionProjections` (exported for the unit spec). */ export declare const sessionStatsProjectionDefinition: ProjectionDefinition<'sessionStats', SessionStatsState>; export {}; //# sourceMappingURL=projection.d.ts.map