Problem
Eve hooks are documented as the extension point for persisting session/message events to an application database, but the current public APIs do not expose a finite, cursor-bearing server-side read of a session's durable event log.
In eve@0.22.5:
message.completed / session.waiting hooks receive the durable event and HookContext, but neither carries the event's remote streamIndex.
ClientSession.stream({ startIndex }) is a live async stream. It correctly remains open after session.waiting because the durable parent accepts future turns.
- Therefore an awaited boundary hook cannot notify an application that then opens
session.stream() and waits to project the suffix: the callback never returns, the hook never settles, and the next queued turn cannot start.
- Browser
onEvent / onFinish persistence is useful for UI state, but cannot be the correctness boundary for a server-owned product projection when the browser refreshes or disappears.
Observed local timeline:
message.completed is visible to the browser.
- A downstream model/tool fetch times out and Eve correctly retries the durable step.
- Eve emits
turn.completed and session.waiting on the retry.
- The application callback opens
session.stream() from its stored cursor.
- The stream waits for future parent events, so the callback and
session.waiting hook remain pending; the product turn stays running, and the queued follow-up remains unattached.
This is not a request to make live streams close at session.waiting; that would break continuation semantics.
Requested public capability
Please expose a finite snapshot/replay operation on ClientSession (name flexible), for example:
const { events, state } = await session.snapshot({ startIndex });
// events: durable events from startIndex through a tail fixed at invocation
// state.streamIndex: the exclusive cursor immediately after that captured tail
Required semantics:
- Captures a finite tail at invocation and never waits for future events.
- Returns ordered public
HandleMessageStreamEvent[] plus the resulting SessionState/exclusive cursor.
- Uses the existing Eve session authorization/continuation contract.
- Works while the durable parent is
running, waiting, completed, or failed.
- Empty suffix is a successful idempotent result with an unchanged cursor.
- Concurrent appends after the captured tail are excluded and available to the next snapshot.
- Does not require consumers to call raw HTTP routes, parse NDJSON, count chunks, or mirror Eve internals.
An equivalent hook API that supplies the exact durable cursor after the current event could also solve incremental projection, but a finite snapshot is more generally useful for bounded recovery and idempotent server-side projection.
Suggested coverage
- finite read of a waiting-but-continuable parent;
- empty suffix / duplicate callback;
- concurrent append around tail capture;
- completed and failed sessions;
- invalid/out-of-range cursor;
- authorization parity with
session.stream();
- client types and server-projection documentation.
I can contribute implementation, tests, docs, and a changeset after maintainer direction on the preferred API shape.
Problem
Eve hooks are documented as the extension point for persisting session/message events to an application database, but the current public APIs do not expose a finite, cursor-bearing server-side read of a session's durable event log.
In
eve@0.22.5:message.completed/session.waitinghooks receive the durable event andHookContext, but neither carries the event's remotestreamIndex.ClientSession.stream({ startIndex })is a live async stream. It correctly remains open aftersession.waitingbecause the durable parent accepts future turns.session.stream()and waits to project the suffix: the callback never returns, the hook never settles, and the next queued turn cannot start.onEvent/onFinishpersistence is useful for UI state, but cannot be the correctness boundary for a server-owned product projection when the browser refreshes or disappears.Observed local timeline:
message.completedis visible to the browser.turn.completedandsession.waitingon the retry.session.stream()from its stored cursor.session.waitinghook remain pending; the product turn staysrunning, and the queued follow-up remains unattached.This is not a request to make live streams close at
session.waiting; that would break continuation semantics.Requested public capability
Please expose a finite snapshot/replay operation on
ClientSession(name flexible), for example:Required semantics:
HandleMessageStreamEvent[]plus the resultingSessionState/exclusive cursor.running,waiting, completed, or failed.An equivalent hook API that supplies the exact durable cursor after the current event could also solve incremental projection, but a finite snapshot is more generally useful for bounded recovery and idempotent server-side projection.
Suggested coverage
session.stream();I can contribute implementation, tests, docs, and a changeset after maintainer direction on the preferred API shape.