eve
Version:
Filesystem-first framework for durable backend AI agents that run anywhere.
96 lines (95 loc) • 4.63 kB
TypeScript
/**
* Token-usage accumulator for `$eve.*` observability tags and session limits.
* Lives on `session.state` so the totals survive workflow step boundaries the
* way the rest of the harness state does.
*
* The harness runs each turn as a sequence of `"use step"` invocations
* (one per tool-loop iteration). Each step knows its own
* `result.usage`, but the dashboard cares about totals **per turn**.
* The workflow runtime's attribute store is "last write wins" per key,
* so the simplest cumulative pattern is: read the previous total from
* `session.state`, add the new step's usage, write the running total
* back. The most recent emit then carries the final per-turn total.
*
* `turnId` keys the turn totals so a fresh turn starts at zero without relying
* on a separate "reset" code path. Session totals stay in the same state record
* and keep accumulating until the durable session ends.
*
* `TokenUsageTotals` carries `costUsd`/`sawCost` alongside the token counts
* for the `$eve.*` dashboard tags and session limits — fields the
* cross-cutting {@link TokenUsage} contract (subagent results, callback
* bodies, usage spans) does not carry. {@link toUsage} projects a total down
* to that shared shape at the one site (the driver's `done` action) where a
* session total crosses into it.
*/
import type { HarnessSession, SessionStateMap } from "#harness/types.js";
import type { TokenUsage } from "#shared/token-usage.js";
export interface TokenUsageTotals {
readonly cacheReadTokens: number;
readonly cacheWriteTokens: number;
readonly costUsd: number;
readonly inputTokens: number;
readonly outputTokens: number;
readonly sawCost: boolean;
}
export type TokenUsageDelta = Partial<TokenUsageTotals>;
/**
* Rolling token usage for the durable session and the in-flight turn.
*
* `turnId` is the in-flight turn's stable id; when the harness step
* runs in a different turn, the flat turn totals reset. The nested
* `session` totals do not reset.
*/
export interface TurnUsageState extends TokenUsageTotals {
readonly session: TokenUsageTotals;
readonly turnId: string;
}
/** Reads the stored per-turn token state, or `undefined` when absent. */
export declare function getTurnUsageState(state: SessionStateMap | undefined): TurnUsageState | undefined;
export type SessionTokenLimitViolation = {
readonly kind: "input";
readonly limit: number;
readonly usedTokens: number;
} | {
readonly kind: "output";
readonly limit: number;
readonly usedTokens: number;
};
export declare function getSessionTokenUsage(session: Pick<HarnessSession, "state">): TokenUsageTotals;
/** Projects a {@link TokenUsageTotals} down to the cross-cutting {@link TokenUsage} shape. */
export declare function toUsage(totals: TokenUsageTotals): TokenUsage;
/**
* Resets the session token budget windows to the current usage totals.
*
* Called when the user grants a continuation after a session token limit was
* reached. The configured limits keep their values and act as the size of the
* next budget window, measured from the moment of the grant. Both windows
* reset together so a session near two limits gets one prompt, not two
* back-to-back.
*/
export declare function extendSessionTokenBudget(session: HarnessSession): HarnessSession;
export declare function getSessionTokenLimitViolation(session: Pick<HarnessSession, "limits" | "state">): SessionTokenLimitViolation | null;
/** Writes per-turn token state onto a new copy of the session. */
export declare function setTurnUsageState(session: HarnessSession, next: TurnUsageState): HarnessSession;
/**
* Folds one step's `usage` into the running per-turn totals. When
* `turnId` differs from the stored state (e.g. a new turn just
* started), the previous totals are discarded — fresh turns start at
* zero without an explicit reset path.
*/
export declare function accumulateTurnUsage(input: {
readonly previous: TurnUsageState | undefined;
readonly turnId: string;
readonly usage: TokenUsageDelta | undefined;
}): TurnUsageState;
/**
* Folds a delegated child session's reported totals into the parent's
* session totals without touching the in-flight turn totals. Turn tags
* attribute only the parent's own model calls (child spend is attributed by
* the caller-side `invoke_agent` span); session totals feed the session
* token limits and the remaining-quota budget granted to later delegations.
*/
export declare function accumulateSessionUsage(input: {
readonly previous: TurnUsageState | undefined;
readonly usage: TokenUsageDelta;
}): TurnUsageState;