(
fn: (props: P) => Promise
): Promise {
/** Initial props, passed to fn */
initialProps?: P;
/** Inject snapshot for context restoration; if provided, restores root context from snapshot before execution */
snapshot?: AgentSnapshot;
/** Custom trace event emitter; only needs to implement emit(event) */
eventBus?: TraceEventEmitter;
/** Root-injected dependencies; readable via expectResource(key) */
providers?: Record
{ /** Inject snapshot for context restoration; if provided, restores root context from snapshot before execution */ snapshot?: AgentSnapshot; /** * Whether to enable snapshots (default IS_DEV). * When true: records journal, saves child frames, dumpSnapshot is callable. * When false: recordJournal / saveChildFrame returns immediately, dumpSnapshot() throws SnapshotDisabledError. */ enableSnapshot?: boolean; // ... other options in core docs } ``` **How it works with a snapshot:** 1. Restores root context from the snapshot's root frame 2. Restores memory state (from `frame.memory` to `ctx.memory`) 3. Sets the snapshot on the context (for replay interception) 4. Executes the function in the restored context 5. Sub-agents automatically restore their own frames from the parent context's `snapshot.children` on invocation **Replay mechanism:** When an Agent executes in a restored context: - **Prompt replay**: If the snapshot contains a matching prompt call record (matched by `contentHash`), returns the cached result directly — no LLM call - **Tool replay**: If the snapshot contains a matching tool call record (matched by `contentHash`), returns the cached result directly — no tool execution - **Sub-agent replay**: Sub-agents restore their own frames from the parent context's `snapshot.children` and recursively apply the replay mechanism **Use cases:** - **Testing and debugging**: Reproduce specific execution scenarios using saved snapshots - **Cost optimization**: Use snapshots in development environments to avoid repeated LLM and tool calls - **Checkpoint resume**: Resume execution from a saved snapshot, continuing unfinished tasks - **Auditing and compliance**: Reproduce historical execution, verify result consistency **Snapshot restoration notes:** - If a `snapshot` is used, it is restored only once at the start of `runWith`. - The restored context state (memory, replay cache, etc.) persists for the entire `runWith` call. - Prompt and tool calls in the snapshot are matched by hash value — the cache is hit only when inputs are identical. If inputs change, execution proceeds normally and the snapshot is updated. - `runWith` only injects the snapshot; sub-agents restore themselves automatically on invocation. - Sub-agent restoration depends on `snapshot.children[callId]`. The `seq` in `callId` is assigned in the order of actual `SubAgent(...)` calls. When calling the same sub-agent in parallel, ensure sub-agent calls happen during the synchronous construction phase so that both the original run and the replay have consistent ordering: ```typescript // Recommended: SubAgent(...) starts in the synchronous phase of map, seq assigned stably by items order const tasks = items.map(item => SubAgent({ text: item.text })); await Promise.all(tasks); ``` - Avoid inserting `await`, `setTimeout`, or other scheduling-uncertain logic before calling sub-agents. Otherwise, parallel sub-agents with the same `configId` may receive different `seq` values due to varying start times, causing `snapshot.children[callId]` misalignment or misses: ```typescript // Not recommended: SubAgent(...) happens after await, seq depends on async completion order const tasks = items.map(async item => { const prepared = await prepare(item); return SubAgent({ text: prepared.text }); }); await Promise.all(tasks); ``` - If each item needs async preparation, prepare data in parallel first, then synchronously start sub-agents in stable array order: ```typescript const prepared = await Promise.all(items.map(item => prepare(item))); const tasks = prepared.map(item => SubAgent({ text: item.text })); await Promise.all(tasks); ``` - Non-serializable data is marked as errors in the snapshot and skipped during replay. --- ## `restoreSnapshot(trace, options?)` Restores an AgentSnapshot from a linear array of event traces (TraceEvent[]), enabling time travel. Rebuilds the nested frame structure to restore the complete Agent execution state. ```typescript import { EVENTS } from '@rejelly/core'; import type { TraceEvent, AgentEndEvent } from '@rejelly/core'; import { restoreSnapshot } from '@rejelly/core/debugger'; // Obtain captured events from Review, an EventBus subscription, or persistent storage declare const events: TraceEvent[]; // Method 1: Simplest usage — restore to latest state (recommended) const snapshot = restoreSnapshot(events); // Method 2: Restore to a specific span's end event const agentEndEvent = events.find( e => e.type === EVENTS.AGENT_END ) as AgentEndEvent | undefined; if (agentEndEvent) { const snapshot = restoreSnapshot(events, { spanId: agentEndEvent.trace.spanId, anchor: 'after' }); const result = await runWith(async () => { return await MyAgent({ input: 'test' }); }, { snapshot }); } ``` **Function signature:** ```typescript export function restoreSnapshot( trace: TraceEvent[], options?: RestoreOptions ): AgentSnapshot; interface RestoreOptions { /** * Target span ID, determines the point in time to restore to * If not provided, defaults to restoring to the last event of the Trace (latest state) */ spanId?: string; /** * Restore time anchor * - 'before': Restore to before this span started (for retry scenarios) * Snapshot does not include this span's execution records; Agent re-executes after restoration * - 'after': Restore to after this span ended (for resume scenarios, default value) * Snapshot includes this span's results and cache; Agent skips actual execution on restoration * @default 'after' */ anchor?: 'before' | 'after'; } ``` **How it works:** The function uses a stack-based state machine to rebuild nested frame hierarchies from the flat event timeline: 1. **Locate truncation point**: Determines which events to include based on the target `spanId` and `anchor` mode - If `spanId` is not provided: defaults to the last event of the Trace (latest state) - `anchor: 'after'`: Finds the span's end event, includes that event and everything before it - `anchor: 'before'`: Finds the span's start event, only includes events before it (excluding the start event) 2. **Replay state**: Processes events chronologically, rebuilding: - **Frame structure**: Rebuilds Agent hierarchy via `AGENT_START` / `AGENT_END` events - **Memory state**: Restores memory from `GENERATION_END` events - **Prompt cache**: Restores LLM call cache from `TURN_END` events (based on `contentHash`) - **Tool cache**: Restores tool call cache from `TOOLS_EXECUTE_END` events (based on `contentHash`) 3. **State repair**: - Marks frames still running at truncation as `status: 'running'` - **Best Effort auto-correction**: If a parent Agent ends with unclosed child Agents still on the stack (zombie child nodes), they are automatically marked as `failed` with error information recorded. This ensures logical consistency of the snapshot data structure (no completed parent containing running children). **Anchor mode explanation:** - **`'after'` (default)**: Restore to after the target span ends - Snapshot includes the span's execution results and cache entries - On restoration, Agent skips actual execution and uses cached results directly - Suitable for "resume" scenarios, e.g., continuing from a completed step - **`'before'`**: Restore to before the target span starts - Snapshot does not include any trace of this span's execution - On restoration, Agent re-executes from scratch - Suitable for "retry" scenarios, e.g., re-executing after a step failed **Usage examples:** ```typescript import { runWith, EVENTS } from '@rejelly/core'; import type { TraceEvent, AgentStartEvent, AgentEndEvent } from '@rejelly/core'; import { restoreSnapshot } from '@rejelly/core/debugger'; // Scenario 1: Restore to latest state async function restoreToLatest(trace: TraceEvent[]) { const snapshot = restoreSnapshot(trace); return await runWith(async () => await MyAgent({ input: 'test' }), { snapshot }); } // Scenario 2: Resume execution (skip completed steps) async function resumeExecution(trace: TraceEvent[]) { const stepEndEvent = trace.find( e => e.type === EVENTS.AGENT_END && e.trace.spanId === 'step_123' ) as AgentEndEvent; const snapshot = restoreSnapshot(trace, { spanId: stepEndEvent.trace.spanId, anchor: 'after' }); return await runWith(async () => await MyAgent({ input: 'test' }), { snapshot }); } // Scenario 3: Retry execution (re-execute failed steps) async function retryExecution(trace: TraceEvent[]) { const stepStartEvent = trace.find( e => e.type === EVENTS.AGENT_START && e.trace.spanId === 'step_123' ) as AgentStartEvent; const snapshot = restoreSnapshot(trace, { spanId: stepStartEvent.trace.spanId, anchor: 'before' }); return await runWith(async () => await MyAgent({ input: 'test' }), { snapshot }); } ``` **Differences from `dumpSnapshot()`:** - **`dumpSnapshot()`**: Exports a snapshot directly from the currently running Agent context. Requires the Agent to be executing and the current context's `enableSnapshot` to be `true`. - **`restoreSnapshot()`**: Restores a snapshot from historical event traces. Does not require the Agent to be executing and does not depend on `enableSnapshot`. Can restore from any point in time. Even in **production** with snapshots disabled, if event traces were collected during runtime, you can use `restoreSnapshot(trace.events)` afterward to rebuild a Snapshot for replay, auditing, or issue reproduction. **Relationship with `runWith()`:** - `restoreSnapshot()` generates a snapshot; `runWith()` uses the snapshot to restore execution - Together they provide full time-travel functionality: restore a snapshot from event traces, then use the snapshot to resume execution **Error handling:** The function throws errors in these cases: - Trace is empty - Target `spanId` does not exist in the event trace (when `spanId` is provided) - `anchor: 'before'` and the target span is at the beginning of the trace (cannot go further back) - Unable to create a root Agent frame (typically when `anchor: 'before'` and the target is the root Agent) **Notes:** - **Default behavior**: If `spanId` is not provided, automatically restores to the last event in the Trace - Event traces are automatically sorted by timestamp to ensure chronological processing - Prompt and tool cache in the restored snapshot is matched by `contentHash`, ensuring identical inputs produce cache hits - Non-serializable data (functions, class instances) is not saved in event traces and will be lost on restoration - The restored snapshot can be used like one generated by `dumpSnapshot()`, passed to `runWith()` for execution restoration