UNPKG

eve

Version:

Filesystem-first framework for durable backend AI agents that run anywhere.

107 lines 5.47 kB
/** * Host-side serialization for the QuickJS engine. * * Implements the workflow wire codec (format-prefixed devalue, identical to * `codec-devalue-vm.ts` / the node:vm engine's workflow-mode codec) as * host code operating on `JSValueHandle`s, using devalue 5.9's pluggable * operations (quickjs-wasi's host-side introspection primitives underneath). * The serde bundle previously evaluated inside the VM is gone: guest values * are read and built through handles, so no serializer code lives in (or * can be tampered with from) the guest realm. * * Side-effect discipline mirrors the node:vm engine's hardened codec * (serialization/hardened.ts): * * - classification is by engine brand (`classId` against boot-captured * samples, `isError`, `isProxy`), never `instanceof` or * `Symbol.toStringTag`; * - extraction goes through intrinsics captured at boot (before any user * code runs) invoked with explicit receivers, or through own-property * descriptor reads, so patched prototypes and inherited accessors never * run; * - the only guest code serialization can execute is the same code the * previous in-VM codec executed by contract: a class's static * `WORKFLOW_SERIALIZE` method, a step proxy's `__closureVarsFn`, and * `WORKFLOW_USE_STEP` / `WORKFLOW_DESERIALIZE` on revival. * * Wire-format parity with the previous in-VM codec is REQUIRED and covered * by tests: event logs written by either codec must be readable by the * other (steps serialized by the node runtime feed VM revival and vice * versa). * * Hybrid value space: reducers return host shapes (plain objects, strings, * numbers) whose leaves may be guest handles, exactly how the node:vm * codec mixes host shapes with sandbox-realm leaves. Every stringify * operation therefore dispatches on `JSValueHandle` and falls back to * devalue's default host operations for host values. Parse operations * always build guest values, so the parse side is handle-only. */ import { JSValueHandle, type QuickJS } from '../_quickjs-wasi.js'; /** * Evaluate the serde capture root in a VM. Must run after the runtime * bootstrap and BEFORE any user code. The returned handle owns the * container; on the baseline-snapshot path its raw box pointer * (`handle.ptr`) is recorded next to the snapshot and re-adopted per * restored VM via {@link adoptSerdeRoot}. Do NOT dispose the handle * before the snapshot is taken (the box must stay live in the memory * image). */ export declare function captureSerdeRoot(vm: QuickJS): JSValueHandle; /** * Re-create the capture-root handle in a VM restored from a snapshot * taken while the exported root was alive, via quickjs-wasi's * snapshot-portable handle tokens (`importHandle` duplicates the * underlying value: the returned handle is independently owned and the * serde disposes it per restored VM). No guest code executes. */ export declare function adoptSerdeRoot(vm: QuickJS, token: number): JSValueHandle; /** * Export the capture root as a snapshot-portable token to record next to * the baseline snapshot. The root handle must stay undisposed until the * snapshot is taken (its box, and the reference it holds, must be part * of the memory image). */ export declare function exportSerdeRoot(vm: QuickJS, root: JSValueHandle): number; export interface QuickJSSerde { /** Serialize a guest value handle to format-prefixed wire bytes. */ serialize(value: JSValueHandle): Uint8Array; /** Build a guest value in the VM from format-prefixed wire bytes. */ deserialize(data: Uint8Array): JSValueHandle; /** * The reducer names this codec applies, in registration order. Exposed * so tests can assert exhaustiveness against the shared value-space * codec (codec-devalue-vm): a reducer added there but not here would * otherwise silently round-trip values as plain objects. */ reducerKeys: readonly string[]; /** Reviver names, for the same exhaustiveness check. */ reviverKeys: readonly string[]; /** * Install `globalThis.process = { env: Object.freeze({...}) }` in the * VM through handles and boot-captured intrinsics only: no guest * source is evaluated, so a module-scope wrapper around JSON.parse / * Object.freeze cannot observe the injection. This matters on the * baseline-snapshot path, where env injection happens after user code * ran; evaluating guest source there would let patched globals see * (and perturb) it, diverging from the fresh path. */ installProcessEnv(env: Record<string, string | undefined>): void; dispose(): void; } /** * Create the host-side serde for a VM. Must be called after the runtime * bootstrap has been evaluated (so bootstrap-installed globals are * capturable) and before the workflow bundle runs. */ export declare function createQuickJSSerde(vm: QuickJS, /** * Pre-captured serde root (baseline-snapshot path): the container * `captureSerdeRoot` created BEFORE user code, re-adopted from the * restored memory image via `adoptSerdeRoot`. When omitted, the root * is captured now; callers must guarantee no user code has run yet. * Either way, initialization below performs only plain-data property * reads and C-level classId reads on the container: NO guest code * executes here, so nothing user-patchable can observe or perturb it. */ capturedRoot?: JSValueHandle): QuickJSSerde; //# sourceMappingURL=quickjs-serde.d.ts.map