Skip to content

feat(sdk): seek SSE replay with durable since_event_id - #2689

Open
Christian Bromann (christian-bromann) wants to merge 5 commits into
mainfrom
cb/sdk-run-boundary-gate
Open

feat(sdk): seek SSE replay with durable since_event_id#2689
Christian Bromann (christian-bromann) wants to merge 5 commits into
mainfrom
cb/sdk-run-boundary-gate

Conversation

@christian-bromann

Copy link
Copy Markdown
Member

SSE reconnects were opening without a durable cursor, so the server dumped the full thread tape every time. Track the last event_id and send since_event_id on reconnect / safe reopen; JS Agent Server skips envelopes at or before that cursor. Session since is unchanged for same-connection seq resume.

Depends on @langchain/protocol with EventStreamRequest.sinceEventId.

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-bot

changeset-bot Bot commented Aug 13, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 421150c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 8 packages
Name Type
@langchain/langgraph-sdk Patch
@langchain/langgraph-api Patch
@langchain/langgraph-cli Patch
@langchain/langgraph-ui Patch
@langchain/angular Patch
@langchain/react Patch
@langchain/svelte Patch
@langchain/vue Patch

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

@open-swe open-swe Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Open SWE Review found 2 potential issues.

Open in WebView Open SWE trace

Comment on lines +849 to +852
if (filter.sinceEventId != null) {
const eventId = event.event_id;
if (typeof eventId !== "string" || !(eventId > filter.sinceEventId)) {
return false;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 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.

Comment thread libs/sdk/package.json Outdated
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.
@pkg-pr-new

pkg-pr-new Bot commented Aug 13, 2026

Copy link
Copy Markdown

Open in StackBlitz

@langchain/langgraph-checkpoint

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-checkpoint@2689

@langchain/langgraph-checkpoint-mongodb

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-checkpoint-mongodb@2689

@langchain/langgraph-checkpoint-postgres

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-checkpoint-postgres@2689

@langchain/langgraph-checkpoint-redis

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-checkpoint-redis@2689

@langchain/langgraph-checkpoint-sqlite

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-checkpoint-sqlite@2689

@langchain/langgraph-checkpoint-validation

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-checkpoint-validation@2689

create-langgraph

npm i https://pkg.pr.new/langchain-ai/langgraphjs/create-langgraph@2689

@langchain/langgraph-api

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-api@2689

@langchain/langgraph-cli

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-cli@2689

@langchain/langgraph

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph@2689

@langchain/langgraph-cua

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-cua@2689

@langchain/langgraph-supervisor

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-supervisor@2689

@langchain/langgraph-swarm

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-swarm@2689

@langchain/langgraph-ui

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-ui@2689

@langchain/langgraph-sdk

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-sdk@2689

@langchain/angular

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/angular@2689

@langchain/react

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/react@2689

@langchain/svelte

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/svelte@2689

@langchain/vue

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/vue@2689

commit: 421150c

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.

@open-swe open-swe Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Open SWE Review found 1 potential issue.

Open in WebView Open SWE trace

Comment on lines +118 to +122
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;
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant