feat(sdk): seek SSE replay with durable since_event_id - #2689
feat(sdk): seek SSE replay with durable since_event_id#2689Christian Bromann (christian-bromann) wants to merge 5 commits into
Conversation
Reconnects were omitting session since and replaying the full tape. Send since_event_id from the last observed event_id; JS Agent Server honors it on sink filters.
🦋 Changeset detectedLatest commit: 421150c The changes in this PR will be included in the next version bump. This PR includes changesets to release 8 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
| if (filter.sinceEventId != null) { | ||
| const eventId = event.event_id; | ||
| if (typeof eventId !== "string" || !(eventId > filter.sinceEventId)) { | ||
| return false; |
There was a problem hiding this comment.
🟠 Lexicographic cursor drops later events
event_id is not lexicographically ordered. In this server, RunProtocolSession.createEvent() emits decimal IDs ("9", "10", ...), so reconnecting with since_event_id: "9" rejects events 10 through 89 because strings such as "10" > "9" are false. Redis stream IDs have the same issue when the sequence suffix crosses a digit boundary (...-10 sorts before ...-9). Consequently a routine reconnect can silently lose a large range of events. The cursor needs to be compared according to the ID format/order (or replay should locate the cursor in tape order), not with JavaScript string ordering.
(Refers to lines 849-852)
Your feedback helps Open SWE learn. React with 👍 or 👎 to tell us if this review comment was useful.
Oxlint flagged let bindings that were only assigned once; collect unsubscribes in a const array instead.
CI frozen-lockfile cannot resolve ^0.0.19 yet; since_event_id still works via the extensible EventStreamRequest body.
@langchain/langgraph-checkpoint
@langchain/langgraph-checkpoint-mongodb
@langchain/langgraph-checkpoint-postgres
@langchain/langgraph-checkpoint-redis
@langchain/langgraph-checkpoint-sqlite
@langchain/langgraph-checkpoint-validation
create-langgraph
@langchain/langgraph-api
@langchain/langgraph-cli
@langchain/langgraph
@langchain/langgraph-cua
@langchain/langgraph-supervisor
@langchain/langgraph-swarm
@langchain/langgraph-ui
@langchain/langgraph-sdk
@langchain/angular
@langchain/react
@langchain/svelte
@langchain/vue
commit: |
Embed/browser tests do not stamp envelope run_id yet. Dropping bare completed events in expecting mode left submit waiters hanging after bind. Buffer run-id-less terminals (and remember bare running) so flush-on-bind can settle the queue.
| const terminals = this.#bufferedTerminals.filter((event) => { | ||
| const eventRunId = extractEventRunId(event); | ||
| // Keep matching run ids, and legacy envelopes with no run id. | ||
| return eventRunId == null || eventRunId === runId; | ||
| }); |
There was a problem hiding this comment.
🟡 Legacy replay terminal settles new run
Treating every no-run_id terminal as matching defeats the run boundary on the compatibility path this branch is meant to support. With a legacy server (which neither stamps run_id nor honors the new durable cursor), an SSE reopen while run.start is awaiting its response replays the previous run's bare running/completed frames. acceptLifecycle() now buffers that old terminal, and this filter includes it in the flush for the newly acknowledged runId; the submit waiter and loading tracker can therefore settle the new run immediately from the previous run's completion. A no-ID terminal cannot be considered current merely because binding happened afterward; it needs an ordering boundary that excludes replayed history.
(Refers to lines 118-122)
Your feedback helps Open SWE learn. React with 👍 or 👎 to tell us if this review comment was useful.
SSE reconnects were opening without a durable cursor, so the server dumped the full thread tape every time. Track the last
event_idand sendsince_event_idon reconnect / safe reopen; JS Agent Server skips envelopes at or before that cursor. Sessionsinceis unchanged for same-connection seq resume.Depends on
@langchain/protocolwithEventStreamRequest.sinceEventId.