/** * @module koi/oo-harness/cell-session * * The koi's workbench: variables that stay alive between code cells. * * Before this, every `run_code` call ran in a fresh sandbox. A cell could * chain twenty tools, but the moment it ended, everything it had computed * was gone unless it was printed into the transcript — so the model paid for * its own results twice: once to produce them, once to carry them forward as * text. Long jobs then died the way they always die: the transcript filled * with re-serialized intermediate data until compaction ate the plan. * * This is the fix, taken from NVIDIA Object-Oriented Agents (NOOA, * Apache-2.0): **pass by reference**. A cell's bindings persist in a live * session; what enters the model's context is a bounded preview of each * binding — type, true size, a sample of the ends — while the value itself * stays whole and addressable by name in the next cell. The amount of data * the koi can work through stops being bounded by the context window and * starts being bounded by memory. * * Three rules make it safe to run for hours: * - **Only declared keeps survive.** A cell's scratch stays scratch; the * model chooses what to keep with `keep(name, value)` (or by returning a * named record), so the session cannot silently grow without intent. * - **Every keep is previewed, never dumped.** The preview is the contract; * the value is the referent. * - **The session is bounded.** Oldest bindings are evicted past a cap, and * an evicted name says so rather than resolving to something stale. */ import { type PprintOptions } from "./pprint.js"; export type Binding = { name: string; value: unknown; /** The one-line preview the model reads. */ preview: string; /** Rough byte-ish size, for eviction and for telling the model the scale. */ size: number; /** Cell number that last wrote it. */ writtenAtCell: number; /** Cell number that last read it, if any — drives eviction. */ readAtCell: number | null; /** Model-supplied one-liner about what it is. */ note?: string; }; export type CellRecord = { cell: number; code: string; ok: boolean; /** What the model printed, already truncated. */ output: string; error?: string; /** Names written by this cell. */ wrote: string[]; startedAt: number; durationMs: number; }; export type CellSessionOptions = { /** How many bindings to keep before evicting the least recently used. */ maxBindings?: number; /** Total rough size the session may hold. */ maxTotalSize?: number; /** Preview shaping, passed to pformat. */ preview?: PprintOptions; /** Cells remembered for the event tape (the values are not kept). */ maxCellHistory?: number; }; /** * One session per agent run. Holds the live bindings, the cell history, and * the rendering of both for the model. */ export declare class CellSession { private readonly bindings; private readonly cells; private readonly options; /** Names dropped by eviction — remembered so a stale reference is explained. */ private readonly evicted; private cellCounter; constructor(options?: CellSessionOptions); get cellCount(): number; /** Bind a name to a live value; returns the preview the model will see. */ keep(name: string, value: unknown, note?: string): Binding; /** Read a live value by name, marking it used so it survives eviction. */ get(name: string): unknown; has(name: string): boolean; /** Why a name is missing, in words the model can act on. */ explainMissing(name: string): string; drop(name: string): boolean; list(): Binding[]; /** A live handle map for injection into a cell's globals. */ values(): Record; /** Open a new cell; returns its number. */ beginCell(): number; recordCell(record: Omit & { cell?: number; }): CellRecord; history(limit?: number): CellRecord[]; totalSize(): number; /** * The live-variable block: what the model sees at the top of every turn. * Deliberately one line per binding — this block is re-rendered on every * turn, so it has to stay small enough that changing state never blows the * cached prefix out of proportion. */ render(limit?: number): string; /** A compact, serializable snapshot for the live views (web + terminal). */ snapshot(): { cells: number; totalSize: number; bindings: Array<{ name: string; preview: string; size: number; note?: string; writtenAtCell: number; }>; recentCells: Array<{ cell: number; ok: boolean; durationMs: number; wrote: string[]; error?: string; }>; }; /** Least-recently-touched bindings go first, and their names are remembered. */ private evict; } /** Names are JavaScript identifiers; anything else is coerced rather than refused. */ export declare function normalizeName(name: string): string;