import { MessageJSON } from "smoltalk"; import type { HostSettings } from "../../host/host.js"; import type { ModuleFingerprint } from "../moduleFingerprintRegistry.js"; import type { RuntimeContext } from "./context.js"; import type { GlobalStoreJSON } from "./globalStore.js"; import type { LoopStopKind, SourceLocation } from "./sourceLocation.js"; import type { StateJSON, StateStackJSON } from "./stateStack.js"; import type { MessageThreadJSON, QueuedMessage } from "./messageThread.js"; import type { StateStack } from "./stateStack.js"; /** Label used for pinned checkpoints created at function entry for Result error handling. */ export declare const RESULT_ENTRY_LABEL = "result-entry"; export type SourceLocationOpts = Omit; /** Whether two locations name the same step on the same stack. A step * inside a tool call is told from the same step in another call of that * tool by its branch path. */ export declare function sameLocation(a: SourceLocationOpts, b: SourceLocationOpts): boolean; /** Reset the global checkpoint ID counter. For use in tests only. */ export declare function resetGlobalCheckpointCounter(): void; export type ThreadMessages = { threadId: string; label: string | null; threadIndex: number; threadCount: number; messages: ThreadMessage[]; /** Messages waiting on the thread's queue, delivered at the next * round boundary; a debugger's edit lands here behind unanswered * tool calls. */ queued: ThreadMessage[]; }; /** One message as a pane shows it; `pending` when a queued edit added * it and the program has not resumed yet. */ export type ThreadMessage = { role: string; content: string; pending?: boolean; }; /** One branch of a frame, as the branches pane lists it: a tool call, a * fork branch, or a parallel item, with its own stack. */ export type BranchSummary = { /** The branch keys from the root down to this branch. */ path: string[]; /** Where the branch's record is in the checkpoint's JSON. */ jsonPath: string[]; key: string; /** The frame that owns the branch; the tool loop's is `llm()`. */ owner: string; /** The scope the branch runs: the tool's name, or the block's. */ label: string; status: "running" | "interrupted" | "done"; }; /** One guard on the stacks the stopped code runs under. */ export type GuardSummary = { label: string | null; dimension: "cost" | "time"; /** Dollars, or milliseconds. */ limit: number; spent: number; disarmed: boolean; root: boolean; /** True when the figures are a branch's own copy of a guard above it: * a time guard's clone carries the parent's remaining time as its * limit, and the branch's own working time as spent. */ inherited: boolean; /** The owning copy's limit: the guard's whole budget, which a * `setGuard` edit sets. The same as `limit` unless inherited. */ ownerLimit: number; /** Where every copy of the guard is in the checkpoint's JSON, the * owning copy first, then each clone down the branch path. */ jsonPaths: string[][]; }; /** The tool loop the stopped code is in: its round, counted from 1, the * tools the loop has removed, the tools it was offered (null for a * checkpoint from before the loop recorded them), and where its frame * is in the checkpoint's JSON. */ export type LoopState = { round: number; removedTools: string[]; tools: string[] | null; jsonPath: string[]; }; /** A frame along a branch path, with where it is in the checkpoint's * JSON, so a reader can say what it found and where. */ export type FrameAt = { frame: StateJSON; jsonPath: string[]; }; /** The globals a stack reads and where they are in the JSON. */ export type GlobalsLocation = { store: Record>; jsonPath: string[]; }; /** Which list a message appended to `thread` goes in. A thread whose * last assistant message made tool calls that the messages after it do * not all answer cannot take a user message yet, since providers want * every call answered first; the message is queued and the round * boundary delivers it after the results. Any other thread takes it at * the end. */ export type MessageLike = { role?: string; content?: unknown; toolCalls?: { id?: string; name?: string; }[]; toolCallId?: string; }; export declare function messageLanding(thread: { messages: MessageLike[]; }): "messages" | "queued"; /** How many calls of the thread's last assistant message have no result * after it yet. */ export declare function pendingToolCalls(thread: { messages: MessageLike[]; }): number; /** One thread of the store, as the threads pane and its picker list it. */ export type ThreadSummary = { id: string; label: string | null; parentId: string | null; messageCount: number; /** The thread the program is in, the top of the active stack. */ active: boolean; hidden: boolean; }; export type CheckpointArgs = { id?: number; stack: StateStackJSON; globals: GlobalStoreJSON; nodeId: string; moduleId?: string; scopeName?: string; stepPath?: string; branchPath?: string[]; loopStop?: LoopStopKind; label?: string | null; pinned?: boolean; moduleFingerprints?: Record; usage?: unknown; traceId?: string; signature?: string; }; export type CheckpointJSON = { id: number; stack: StateStackJSON; globals: GlobalStoreJSON; nodeId: string; moduleId: string; scopeName: string; stepPath: string; /** Present only for a checkpoint taken on a branch stack, so a * checkpoint from before the field keeps its signature. */ branchPath?: string[]; /** Present only for a checkpoint taken at one of the tool loop's own * stops. */ loopStop?: LoopStopKind; label: string | null; pinned: boolean; moduleFingerprints?: Record; usage?: unknown; traceId?: string; signature?: string; }; /** A thread as the panes see it: the store's key, its JSON, whether the * program is in it, and a newer copy of its messages when a tool loop * holds one. */ export type ThreadView = { key: string; thread: MessageThreadJSON; active: boolean; newer?: MessageJSON[]; /** The loop copy's queue, when a loop frame holds the thread. */ queued?: QueuedMessage[]; /** Where the messages a resume reads are in the checkpoint's JSON: * the loop's copy when a loop frame holds one, else the store's. */ jsonPath: string[]; }; /** The name a pane shows for a frame: the tool loop's own frame reads * `llm()`, the compiler-named frame of a fork or parallel arm or a block * argument reads `block`, and a frame with no scope name `(unknown)`. */ export declare function frameName(scopeName: string | null | undefined): string; export declare class Checkpoint implements SourceLocation { id: number; stack: StateStackJSON; globals: GlobalStoreJSON; nodeId: string; moduleId: string; scopeName: string; stepPath: string; /** The branch keys from the root stack down to the stack the step ran * on, empty for the root. `getCurrentFrame` follows it. */ branchPath: string[]; /** Set for a checkpoint taken at one of the tool loop's own stops, * before a model request or a tool call. See `SourceLocation`. */ loopStop?: LoopStopKind; label: string | null; pinned: boolean; /** The fingerprint (a hash of the generated code) of each module with a * live frame, captured at creation so a resume can refuse when the code * changed. */ moduleFingerprints?: Record; /** What the run had spent when this checkpoint was taken, so a resumed run * keeps counting from there. `unknown` because a checkpoint comes back from * the host; `InvocationUsageMeter.resumeFrom` recovers it. */ usage?: unknown; /** The statelog trace the run was logging to when this checkpoint was * taken, so a resume from a file can log into the same trace. Absent on * checkpoints from before the field and on contexts with no run id. */ traceId?: string; /** HMAC checksum embedded at creation when a signing key is configured. * Absent on unsigned checkpoints. See lib/runtime/checkpointChecksum.ts. */ signature?: string; constructor(args: CheckpointArgs); getScopeKey(): string; /** The location, with `branchPath` only when the step ran on a branch * stack, as `toJSON` writes it. */ get location(): SourceLocation; /** The frame the checkpoint was taken in: the last frame of the stack * its branch path leads to. For a step inside a tool call that is the * tool's frame, not the root's last frame. Undefined when the path * names a branch the checkpoint does not hold. */ getCurrentFrame(): StateJSON | undefined; /** The last frame of the stack `branchPath` leads to, from the root. */ frameAt(branchPath: string[]): StateJSON | undefined; /** The frames of the stack `branchPath` leads to, from the root; * undefined when the path names a branch the checkpoint does not * hold. */ stackAt(branchPath: string[]): StateJSON[] | undefined; /** The frames from the root down the branch path, the deepest last: * every frame of every stack the path passes through. */ framesAlong(branchPath: string[]): StateJSON[]; /** Every frame along `branchPath`, root first, each with its path in * the checkpoint's JSON. The one walk the readers and the patch * library share, so the layout of the JSON is known here and nowhere * else. A path that names a branch the checkpoint does not hold * stops at the last stack it reaches. */ walkFrames(branchPath: string[]): FrameAt[]; /** The stacks along `branchPath`, the root first, each with the path * of its `StateStackJSON`. A branch hangs off the last frame of the * stack above it. */ private stacksAlong; /** The path of the stack `branchPath` leads to, or undefined when the * checkpoint does not hold it. */ private stackPathAt; /** The path to the branch paused on the interrupt `interruptId`, or * undefined when no branch is. A user interrupt raised inside a tool, * a fork, or a parallel arm has no stop record to say which branch it * came from, but the branch's own state records the interrupt's id. * An interrupt bubbles up unchanged, so every enclosing branch records * the same id (`setInterruptOnBranch` in `prompt.ts` and `runBatch.ts`); * the deepest match is the branch that raised it. Only the last frame * of a stack holds branches, as `stackAt` reads. */ branchPathOf(interruptId: string): string[] | undefined; /** The frame a person inspects at a location: the frame the step ran * in, or, at one of the tool loop's own stops, the frame that called * `llm()`, the one under the loop's frame. */ shownFrame(location: { branchPath?: string[]; loopStop?: LoopStopKind; }): StateJSON | undefined; /** The last frame of the root stack. */ private rootFrame; /** The active stack of the deepest `fork` or `parallel` branch on the * path, when there is one. Such a branch runs on a view of the node's * store with its own active stack, serialized with the branch; a tool * call shares its caller's and has none. */ private branchActiveStack; /** The threads the stopped code can see, from two places in the * checkpoint. The node frame carries the thread store, with the thread * the program is in at the top of the active stack: the store's own, * or the branch's inside a `fork` or `parallel` arm. A tool loop's * frame carries `messagesJSON`, the thread it is working on, refreshed * on every checkpoint. When its `id` matches a store thread it is the * copy shown, since a resume continues from it, whatever the lengths. * A loop inside a tool runs on a fresh store * that is not serialized, so its copy is the only record of the * thread: it is listed under its own id, named after the function that * called `llm()`, with no parent, since its `parentId` names a thread * of the store that was not serialized. */ threadsAlong(branchPath: string[]): ThreadView[]; /** Every thread the stopped code can see, in the checkpoint's order, * with the one the program is in marked. Empty when the checkpoint * holds no threads. */ listThreads(branchPath?: string[]): ThreadSummary[]; /** The messages of one thread, `threadId`, or of the thread the program * is in when none is named. */ getThreadMessages(threadId?: string, branchPath?: string[]): ThreadMessages | null; private getContentFromMessage; /** The stack `branchPath` leads to, as serialized. */ private stackJSONAt; /** Every branch in the checkpoint, from the root stack down through * the stacks of the branches themselves, in the order the checkpoint * holds them. Stopped inside one tool, a person sees its siblings. * Only the last frame of a stack holds branches, as `stackAt` reads. * A finished branch has no frames, since a function pops its frame * when it returns, so its name comes from the tool loop's record of * the call, when the loop is the owner. */ listBranches(): BranchSummary[]; /** The guards the stopped code runs under, outermost first: those on * each stack along `branchPath`, a stack's inherited ones before its * own. A branch stack serializes the time guards it inherited, since * their elapsed time is its own: each is a clone with the parent's * guard id, the parent's remaining time as its limit, and the branch's * working time as spent. A guard is listed once, in the place of its * outermost copy, with the figures of the deepest, which are the ones * the stopped code is metered by. A cost guard is shared by reference, * so a branch never serializes an inherited one. */ listGuards(branchPath?: string[]): GuardSummary[]; /** The tool loop the stopped code is in, from the deepest loop frame * along `branchPath`; null outside any `llm()` call. */ loopState(branchPath?: string[]): LoopState | null; getGlobalsForModule(): Record | null; /** The globals the stack `branchPath` leads to sees. A `fork` or * `parallel` branch runs on its own copy of the globals (`runBatch` * saves it as the branch's `globalsJSON` before every checkpoint), so * a module-level variable it changed has the branch's value here and * the root's in `globals`. A branch that shares the globals, a tool * call, has no copy and sees the nearest copy above it, or the root's. */ globalsAt(branchPath: string[]): GlobalStoreJSON; /** `globalsAt`, with where in the JSON that store is. */ globalsLocation(branchPath: string[]): GlobalsLocation; private globalsAlong; /** The branch's own record (its stack, result, globals copy). */ private branchRecordAt; getFilename(): string; pathEquals(other: Checkpoint): boolean; equals(other: Checkpoint): boolean; toJSON(): CheckpointJSON; /** A copy with `opts` applied, re-signed under `settings` when this * checkpoint was signed, since `opts` may change signed fields * (typically `id`). */ clone(opts: Partial, settings: HostSettings): Checkpoint; getLocation(): string; static fromStateStack(stateStack: StateStack, ctx: RuntimeContext, opts: SourceLocationOpts & { label?: string | null; pinned?: boolean; }): Checkpoint; static fromContext(ctx: RuntimeContext, opts: SourceLocationOpts & { label?: string | null; pinned?: boolean; }): Checkpoint; static fromJSON(json: any): Checkpoint | null; } export type CheckpointStoreJSON = { checkpoints: Record; counter: number; }; export declare class CheckpointStore { private checkpoints; private restoreCounts; private locationRestoreCounts; private maxRestores; private maxSize; constructor(maxRestores?: number, maxSize?: number); create(stateStack: StateStack, ctx: RuntimeContext, opts: SourceLocationOpts & { label?: string | null; pinned?: boolean; removeDuplicate?: boolean; }): number; add(checkpoint: Checkpoint): void; cloneCheckpoint(_checkpoint: Checkpoint, opts: Partial, settings: HostSettings): number; findCheckpoint(location: SourceLocationOpts): Checkpoint | null; /** The newest unpinned checkpoint older than `beforeId` whose step ran * on the stack the program is stopped on, or on a stack above it, and * not at `location`, the step it is stopped at. `step back` rewinds to * it. A sibling branch's steps are passed over: stopped inside one tool * of a round, `step back` must not land in the other tool. The stopped * location is passed on its own because the checkpoint on a stop * inside a tool call is the shared one stamped at the join, whose own * location is the loop's. */ findBefore(beforeId: number, location: SourceLocationOpts): Checkpoint | null; createRolling(ctx: RuntimeContext, opts: SourceLocationOpts): number; /** Strip the `__dbg_` flags from the frame the checkpoint was taken in, * and from that frame only, so a replay stops there again. Ancestor * frames keep theirs: a rewind into a tool call must pass through the * `llm()` statement without stopping, and a sibling branch that was * mid-step keeps its flag so it replays past its own stop. */ removeDebugFlagsFor(id: number): void; private removeDuplicate; createPinned(stateStack: StateStack, ctx: RuntimeContext, opts: SourceLocationOpts & { label: string | null; }): number; /** Mark a checkpoint pinned, re-signing it under `settings` when it was * signed: `pinned` and `label` are signed fields. */ pin(id: number, label: string | undefined, settings: HostSettings): void; get(id: number): Checkpoint | undefined; delete(id: number): void; getCheckpoints(): Checkpoint[]; deleteAfterCheckpoint(id: number, deletePinned?: boolean): void; getLocationRestoreCount(location: string): number; trackLocationRestore(location: string): void; trackRestore(id: number): void; getSorted(): Checkpoint[]; prettyPrint(): string; toJSON(): CheckpointStoreJSON; static fromJSON(json: CheckpointStoreJSON, maxRestores?: number): CheckpointStore; private evictIfNeeded; }