@sema-agent/core
Version:
Stateless, task-oriented AI agent core
36 lines (35 loc) • 2.44 kB
TypeScript
import { type UsageWindow, type UsageWindowReading, type UsageWindowStore } from "../../core/usage-window-store.js";
import { type CorruptReadInfo } from "./corrupt-read-sink.js";
export interface FileUsageWindowStoreOptions {
/** Disclosure sink for a ledger that is present but cannot be read as one: it fires WITH the refusal and is the
* only carrier of the file's location (the refusal's message, which reaches a run's failure text, names none).
* Never fires on plain absence. Swallow-guarded: a throwing sink cannot change the refusal. */
onCorruptRead?: (info: CorruptReadInfo) => void;
}
/**
* design/164 件五 — file-backed {@link UsageWindowStore}: ONE JSON record per governed key, semantics
* identical to `InMemoryUsageWindowStore` BY CONSTRUCTION (both are shells around the same pure
* `chargeUsageRecord` / `readUsageRecord`; neither re-implements window arithmetic).
*
* Atomicity: the file backend is single-process (the `FileStorageBackend` boot lock guarantees ONE writer
* per data dir) and `charge` reads-modifies-writes SYNCHRONOUSLY (no await between the read and the
* atomic replace), so the update is atomic within the one event loop — the in-memory store's premise.
* Cross-process governance belongs to a database backend with a real transaction.
*
* **Corrupt records FAIL CLOSED** — in the same direction as `FileSessionPolicyStore`'s keyed read. A
* policy file is a DENY list and a usage window is a CEILING; for both, reading a damaged record as "nothing
* there" would drop the very limit the operator wired — here an unlimited, silent allowance, precisely what
* the operator wired this to prevent. A corrupt record therefore refuses with `usage_window.store_corrupt`
* until it is repaired or removed.
*/
export declare class FileUsageWindowStore implements UsageWindowStore {
private readonly dir;
private readonly onCorruptRead;
constructor(root: string, opts?: FileUsageWindowStoreOptions);
/** key → a safe, INJECTIVE filename (`sanitizeScope` appends the full sha256 of the raw key, so two
* distinct principals can never share a ledger file). */
private pathFor;
private loadRecord;
charge(key: string, tokens: number, at: number, windows: readonly UsageWindow[], costMicroUsd?: number | null): Promise<void>;
read(key: string, windows: readonly UsageWindow[], now: number): Promise<readonly UsageWindowReading[]>;
}