eve
Version:
Filesystem-first framework for durable backend AI agents that run anywhere.
129 lines • 6.3 kB
TypeScript
import { type Event } from '#compiled/@workflow/world/index.js';
/**
* The QuickJS engine's bookkeeping for the run's event log, giving that engine
* the same relationship to a World's write responses that the node:vm replay
* loop has.
*
* The node engine holds the log as an array it replays from, so a page a World
* hands back on a write (a skipped-slot report against `eventCount`, or an
* inline delta against `sinceCursor`; see `CreateEventParams` in
* `@workflow/world`) is merged into that array and the next replay reads it
* there. The QuickJS engine holds a LIVE VM instead: events are delivered to it
* incrementally through `continueWithEvents`, in log order, exactly once. So
* for this engine a returned page is not merged into a log; it is queued to be
* delivered. This class owns that queue and the three numbers around it:
*
* - `knownMaxSlot`, the highest position this invocation has seen anywhere
* (fed, buffered, or its own write). Reported as `eventCount` on every write
* this invocation makes from its view of the log, so the World can hand
* back what landed above it.
* - `fedMaxSlot`, the highest position delivered to the VM. Delivery is
* strictly in position order with no gaps, because the VM consumes events
* as they arrive and a later replay will read the log in position order;
* feeding position 13 before 12 exists would let the two disagree.
* - `cursor`, the read position for `events.list`, advanced by list pages and
* by a complete inline delta.
*
* Only PAGES are queued for delivery, never the created event a write returns
* on its own. A World reads a report or a delta the way it reads a listing,
* with payload refs resolved, so those events are what a listing would have
* delivered. The created event on a create response is not: a World may hand
* it back with its payload still a ref descriptor and telemetry fields
* stripped (the Vercel World does, for every type whose entity the runtime
* does not read off the response), and a VM given that copy of a `step_failed`
* sees a step that failed with no error in it. The created event still counts
* toward the position the next write names; it is delivered by the page that
* covers it or by the next listing. `deliverEvent` opts a write in when its
* event carries nothing a VM reads (`wait_completed`).
*
* Positions come from slot-numbered event ids. A log whose ids are not slots
* (a World on the old id scheme, or a mocked World) turns tracking off for the
* rest of the invocation: no `eventCount` is sent, nothing is buffered, and the
* engine reads the log back the way it did before any of this existed.
*/
export declare class QuickJSLogView {
private slotTracking;
private knownMaxSlot;
private fedMaxSlot;
/** Events handed back by a World that the VM has not been given yet. */
private readonly unfed;
private cursor;
constructor(fedEvents: readonly Event[], cursor: string | null);
/** Read position for the next `events.list`, or `null` for the start. */
get logCursor(): string | null;
/** How many events are queued for delivery to the VM. */
get bufferedCount(): number;
/** Whether positions are being tracked (see class doc). */
get tracking(): boolean;
/**
* The `eventCount` to attach to a write made from this view. Empty while no
* position is known (an empty log) or once tracking has been turned off.
*/
snapshotParams(): {
eventCount?: number;
};
/** A page of `events.list` was read to `cursor`. */
advanceCursor(cursor: string | null): void;
/**
* Events delivered to the VM by the caller (the initial load, or an
* `events.list` page). Removes them from the delivery queue if a write
* response had already handed them back.
*/
markFed(events: readonly Event[]): void;
/**
* Absorb a write's response: note the committed event's position, and
* queue the skipped-slot report a World attached to it.
*
* A truncated page (`hasMore`) is dropped whole, the same policy as
* `absorbSkippedSlotReport` in the node engine: it covers a span of
* positions but carries only some of them, and queuing part of a span
* would have this view claim, on its next write, to have seen positions it
* never received. The committed event is queued only under `deliverEvent`
* (see the class doc for why not by default); its position always counts.
*/
absorb(result: {
event?: Event;
events?: readonly Event[];
hasMore?: boolean;
}, options?: {
deliverEvent?: boolean;
}): {
queued: number;
truncated: boolean;
};
/**
* Absorb the inline delta a step-terminal write returned for
* `sinceCursor`, and advance the read cursor past it when that is safe.
*
* The delta is everything after `sentCursor`, so it may be taken only if
* the view still stands at that cursor (a list in between would have moved
* it, and appending a delta computed from an older position could deliver
* events behind ones already fed). The cursor advances only when the queue
* then holds every position between what the VM has and the delta's end:
* with a hole in between, a later `events.list` from the advanced cursor
* would never return the missing event and the VM would stall on it.
*
* @returns whether the cursor was advanced.
*/
absorbDelta(sentCursor: string, delta: {
events: readonly Event[];
cursor: string | null;
hasMore: boolean;
}): boolean;
/**
* Drain the events that can be delivered to the VM now: the queued run of
* consecutive positions directly above the highest one already fed. A queued
* event above a position nothing has filled yet stays queued until a list
* fills the gap.
*/
takeContiguous(): Event[];
private queueIsDense;
/** Queue one event for delivery unless the VM already has it. */
private queue;
/**
* The event's position, bumping `knownMaxSlot`. `undefined` once an id
* turns out not to be a slot, which turns tracking off for good.
*/
private slotOf;
}
//# sourceMappingURL=quickjs-log-view.d.ts.map