import { SmolConfig } from "smoltalk"; import type { DebuggerState } from "../../debugger/debuggerState.js"; import type { LogLevel } from "../../logger.js"; import { SimpleMachine } from "../../simplemachine/index.js"; import { StatelogClient, StatelogConfig } from "../../statelogClient.js"; import { CoverageCollector } from "../coverageCollector.js"; import type { AbortCause } from "../errors.js"; import { Clock } from "../clock.js"; import type { AgencyCallbacks } from "../hooks.js"; import type { InterruptResponse } from "../interrupts.js"; import { LLMClient } from "../llmClient.js"; import { MemoryManager } from "../memory/manager.js"; import type { MemoryConfig } from "../memory/types.js"; import { GlobalStore } from "../state/globalStore.js"; import { StateStack } from "../state/stateStack.js"; import type { TraceConfig } from "../trace/types.js"; import type { HandlerEntry, HandlerFn } from "../types.js"; import type { Checkpoint } from "./checkpointStore.js"; import { CheckpointStore } from "./checkpointStore.js"; import { PendingPromiseStore } from "./pendingPromiseStore.js"; export declare class RuntimeContext { stateStack: StateStack; globals: GlobalStore; checkpoints: CheckpointStore; callbacks: AgencyCallbacks; onStreamLock: boolean; handlers: HandlerEntry[]; /** The time source for guards. Real by default; a FakeClock only when a * test opts in. NOT serialized — reconstructed per run, like handlers. * * DRIFT WARNING: this and the other non-serialized runtime fields * (handlers, callbacks, checkpoints, locks, …) are set by the constructor * AND, separately, by `createExecutionContext`, which builds the execution * context via `Object.create` and copies fields by hand. A field added * here but forgotten there is silently `undefined` at run time with no * compile error — that is the bug the `clock` copy fixed. If you add a * non-serialized field, copy it in `createExecutionContext` too. */ clock: Clock; locks: Record>; lockOwners: Record; lockWaiters: Record; lockReleasers: Record void>; pendingPromises: PendingPromiseStore; graph: SimpleMachine; _skipNextCheckpoint: boolean; _pendingArgOverrides?: Record; _restoreCount: number; _toolCallDepth: number; debuggerState: DebuggerState | null; /** When set, `input()` calls this instead of opening its own readline on * stdin. Installed by hosts that own the terminal for this execution: * the REPL routes questions through its readline, and the * `_installSlowInput` test seam simulates a slow human. Per-execution on * purpose (never a process global): two runs in one process must not see * each other's override. Never serialized — checkpoints capture the * StateStack and GlobalStore, not the context. The TUI debugger still * uses the legacy `globalThis.__agencyInputOverride` channel because it * only sees the compiled module's exports; `inputImpl` falls back to * that global until the debugger migrates. */ inputOverride?: (prompt: string) => Promise; private traceWriter; statelogClient: StatelogClient; smoltalkDefaults: Partial; /** Max characters of a single tool result fed back to the LLM (the * full result is still returned to Agency code). `undefined` falls * back to the runtime default in `runPrompt`. Baked in from * `agency.json` `client.maxToolResultChars` at compile time. */ maxToolResultChars?: number; /** Paths to provider modules to load at startup, baked in from * `agency.json` `client.providerModules` at compile time and merged * with `AGENCY_PROVIDER_MODULES` at runtime by `loadProviderModules`. * Defaults to `[]`. */ providerModules: string[]; private _llmClient; private _interruptResponses; get llmClient(): LLMClient; setLLMClient(client: LLMClient): void; setInterruptResponses(responses: Record): void; getInterruptResponse(interruptId: string): InterruptResponse | undefined; dirname: string; /** Callbacks registered at module top-level (via `_callback` during * `__initializeGlobals`, when no real caller frame exists yet). Persist for * the whole run. */ topLevelCallbacks: Array<{ name: string; fn: any; }>; abortController: AbortController; traceConfig: TraceConfig; runId: string | null; /** Subprocess nesting depth of THIS process: 0 in the root, seeded from * the run/resume instruction in subprocesses. Surfaced to Agency via the * std::run gate interrupt's `depth` payload and to TS helpers via * `agency.ctx().subprocessDepth`. */ subprocessDepth: number; verbose: boolean; /** * Log threshold used by ad-hoc subsystem loggers (memory, etc.). * Plumbed in from `AgencyConfig.logLevel` so users can crank it up to * `"debug"` when investigating issues without recompiling. The logger * itself is stateless — consumers call `createLogger(execCtx.logLevel)` * on demand rather than carrying a long-lived Logger instance, which * lets each subsystem add its own prefix without coordinating an * instance pool. */ logLevel: LogLevel; getStaticVars?: () => Record; coverageCollector: CoverageCollector | null; private statelogConfig; maxRestores: number; /** Ceiling on logical function-call nesting depth before the runaway- * recursion guard trips. See lib/runtime/callDepth.ts. */ maxCallDepth: number; failurePropagation: "off" | "warn" | "on"; jsonMemoryConfig?: MemoryConfig; private memoryManagerCache; constructor(args: { statelogConfig: StatelogConfig; smoltalkDefaults: Partial; maxToolResultChars?: number; providerModules?: string[]; dirname: string; maxRestores?: number; maxCallDepth?: number; failurePropagation?: "off" | "warn" | "on"; traceConfig?: TraceConfig; verbose?: boolean; memory?: MemoryConfig; /** Threshold for ad-hoc subsystem loggers. Optional so existing * test/runtime constructors keep working; defaults to "info" to * match the established no-debug-by-default behavior. */ logLevel?: LogLevel; /** Test-only override. Omitted in production, where defaultClock() * (the env var or the realClock default) applies. */ clock?: Clock; }); getRunId(): string; /** The statelog sink identity a subprocess should inherit — see * `withParentStatelog` in ipc.ts. Narrow accessor so the full (private) * statelog config stays encapsulated. */ getStatelogSink(): { observability: boolean; logFile?: string; }; createExecutionContext(runId: string): Promise>; /** * Resolve the active `MemoryManager` for the current stateStack. * * Single rule: top of `stateStack.other.memoryFrames` (accessed via * `activeMemoryFrame()`) wins. No fallback to a "default" manager — * the JSON config gets seeded as the bottom frame at execCtx * creation, so if JSON was set there will always be a frame and * this never returns `undefined` for JSON-only setups. * * The one back-compat seam: a checkpoint written before * `memoryFrames` existed (or one taken right after a user called * `disableMemory()` on the JSON-seeded bottom frame) has no frames. * If `jsonMemoryConfig` is set we re-seed it as a courtesy so old * traces resume as before. If the user explicitly popped, they * call `enableMemory(...)` again to turn it back on. * * Managers are cached per execCtx keyed by `configKey` so * push/pop/push of the same dir reuses one instance — important * for the "pop back to A returns A's manager" semantics. */ getActiveMemoryManager(): MemoryManager | undefined; /** Iterate every cached memory manager. Used at shutdown so a * branch that opened a side store doesn't lose its writes. */ getAllCachedMemoryManagers(): MemoryManager[]; /** Register a handler. `liveGuardIds` is REQUIRED and each caller * must decide it explicitly (an empty array is NOT neutral — it * means "hide every guard from this handler", which is correct only * for handlers registered before any guard can exist, like the * --policy handler and top-level init wrappers). Agency handle * blocks capture it in Runner.handle; TS callers capture at call * time in withPushedHandler. See HandlerEntry. */ pushHandler(fn: HandlerFn, liveGuardIds: string[]): void; popHandler(): void; enterToolCall(): void; exitToolCall(): void; isInsideToolCall(): boolean; get aborted(): boolean; /** * Branch-aware cancellation check. Returns true if either: * - the global ctx is aborted (e.g., user pressed Ctrl-C), OR * - the given branch stack's per-branch abort signal has fired (e.g., * this branch is a race loser). * * Pass the local stateStack at any call site that lives inside a fork/race * branch so the check sees that branch's signal. Without a stack arg, this * is equivalent to `ctx.aborted`. */ isCancelled(stack?: StateStack): boolean; /** * Branch-aware AbortSignal for HTTP/fetch/streaming calls. Returns a * composite signal that fires on either global ctx abort OR the given * branch stack's abort. Pass to smoltalk's `abortSignal` so per-branch * cancellation actually tears down in-flight network requests. * * If no stack is given (or the stack has no branch signal), returns the * global ctx signal — same behavior as before. */ getAbortSignal(stack?: StateStack): AbortSignal; /** * Cancel the whole execution. `cause` records WHY: a terminal * `userKill` (TS `cancel()` / an external abort — the default) vs a * recoverable `userInterrupt` (the REPL's Esc, which hands control back * but keeps the session). The cause rides on the AgencyCancelledError * so downstream boundaries can read it via `readCause`. Leaving the * error itself as the abort reason keeps `signal.reason` an Error. */ cancel(reason?: string, cause?: AbortCause): void; /** * Swap in a fresh AbortController after a user-initiated cancel so the * NEXT unit of work isn't immediately aborted. The REPL calls this once * it has caught the AgencyCancelledError from a cancelled turn and is * about to accept the next prompt. Safe only when nothing is in flight * (the cancelled turn has fully unwound) — otherwise an in-flight call * would keep a reference to the old, now-orphaned signal. */ resetCancel(): void; forkStack(): StateStack; /** Sever references held by an execution context so GC can reclaim them. */ cleanup(): void; /** Get the most recent result-entry checkpoint for the current function. */ getResultCheckpoint(): Checkpoint | undefined; restoreState(checkpoint: Checkpoint): void; /** @deprecated Use checkpoints.create() instead */ stateToJSON(): { stack: import("../state/stateStack.js").StateStackJSON; globals: import("../state/globalStore.js").GlobalStoreJSON; }; toJSON(): { stateStack: import("../state/stateStack.js").StateStackJSON; callbacks: string[]; onStreamLock: boolean; graph: { nodes: string[]; edges: Record; config: { debug: { log?: boolean; logData?: boolean; } | undefined; }; }; statelogClient: string; smoltalkDefaults: string; dirname: string; }; getSmoltalkConfig(config?: Partial): Partial; pauseTraceWriter(): Promise; closeTraceWriter(): Promise; writeCheckpointToTraceWriter(checkpoint: Checkpoint): Promise; writeStaticStateToTrace(values: Record): Promise; hasDebugger(): boolean; hasTraceWriter(): boolean; }