@langchain/langgraph-sdk 1.12.6 → 1.12.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -186,6 +186,21 @@ var StreamController = class {
186
186
  * from the newly-started run and must be accepted.
187
187
  */
188
188
  #interruptReplayThroughSeq;
189
+ /**
190
+ * Command whose barrier is still to be found on the thread stream. The
191
+ * agent server answers HTTP commands from a fresh per-request manager,
192
+ * so `applied_through_seq` is 0 while the stream replays the whole
193
+ * history with positive seqs. The barrier is then the event before the
194
+ * `running` event of the run the command started: `run.start` names that
195
+ * run, and a resume starts the first root run after the answered
196
+ * interrupts were last requested. Unknown interrupts stay buffered until
197
+ * it is found.
198
+ */
199
+ #awaitingRunBoundary;
200
+ /** Seq of each root run's `running` event, keyed by run id. */
201
+ #rootRunStartSeqs = /* @__PURE__ */ new Map();
202
+ /** Highest seq at which each interrupt was requested. */
203
+ #interruptRequestSeqs = /* @__PURE__ */ new Map();
189
204
  /** Unknown inputs waiting to be classified as live or replayed history. */
190
205
  #pendingHydrationInterruptEvents = [];
191
206
  #settleParkedInterruptsPromise;
@@ -351,6 +366,7 @@ var StreamController = class {
351
366
  awaitResumedRunTerminal: (signal) => this.#awaitResumedRunTerminal(signal),
352
367
  onSubmitStart: () => {
353
368
  this.#submitGeneration += 1;
369
+ this.#awaitingRunBoundary = void 0;
354
370
  this.#interruptCommandPending = this.#hydratedActiveInterruptIds != null;
355
371
  this.#pendingInterruptEvents = this.#pendingHydrationInterruptEvents;
356
372
  this.#pendingHydrationInterruptEvents = [];
@@ -362,11 +378,12 @@ var StreamController = class {
362
378
  for (const release of [...this.#heldLifecycleReleases]) release();
363
379
  },
364
380
  onRunCreated: (runId) => {
365
- this.#advanceInterruptReplayBarrier();
381
+ this.#advanceInterruptReplayBarrier({ runId });
366
382
  this.#notifyCreated(runId);
367
383
  },
368
384
  onRunCompleted: (reason, runId) => this.#notifyCompleted(reason, runId),
369
385
  onRunEnd: () => {
386
+ this.#abandonRunBoundary();
370
387
  this.#interruptCommandPending = false;
371
388
  this.#markLocalRunEnd();
372
389
  },
@@ -1100,7 +1117,7 @@ var StreamController = class {
1100
1117
  config: options?.config,
1101
1118
  metadata: options?.metadata
1102
1119
  });
1103
- this.#advanceInterruptReplayBarrier();
1120
+ this.#advanceInterruptReplayBarrier({ answeredInterruptIds: [resolved.interruptId] });
1104
1121
  this.#markInterruptResolvedInRootStore(resolved.interruptId);
1105
1122
  }, prepared?.handle);
1106
1123
  } catch (error) {
@@ -1171,7 +1188,7 @@ var StreamController = class {
1171
1188
  config: options?.config,
1172
1189
  metadata: options?.metadata
1173
1190
  });
1174
- this.#advanceInterruptReplayBarrier();
1191
+ this.#advanceInterruptReplayBarrier({ answeredInterruptIds: responses.map((r) => r.interrupt_id) });
1175
1192
  for (const { interrupt_id: interruptId } of responses) this.#markInterruptResolvedInRootStore(interruptId);
1176
1193
  }, prepared?.handle);
1177
1194
  } catch (error) {
@@ -1391,6 +1408,9 @@ var StreamController = class {
1391
1408
  this.#hydratedActiveInterruptIds = null;
1392
1409
  this.#hydratedCheckpointTime = void 0;
1393
1410
  this.#interruptReplayThroughSeq = void 0;
1411
+ this.#awaitingRunBoundary = void 0;
1412
+ this.#rootRunStartSeqs.clear();
1413
+ this.#interruptRequestSeqs.clear();
1394
1414
  this.#pendingHydrationInterruptEvents = [];
1395
1415
  this.#discardPendingInterruptEvents();
1396
1416
  this.queueStore.setState(() => require_queue_adapter.EMPTY_QUEUE);
@@ -1607,10 +1627,70 @@ var StreamController = class {
1607
1627
  * consumer. `ThreadStream.interrupts` is updated in parallel for
1608
1628
  * resume targeting.
1609
1629
  */
1630
+ this.#observeRunBoundary(event);
1610
1631
  this.#recordInterrupt(event);
1611
1632
  this.#settleParkedInterruptsOnTerminal(event);
1612
1633
  }
1613
1634
  /**
1635
+ * Remember where each root run starts and where each interrupt was last
1636
+ * requested, then try to place a pending command barrier. Both the
1637
+ * content pump and the lifecycle watcher replay the same tape with the
1638
+ * same seqs, so recording either copy is enough.
1639
+ */
1640
+ #observeRunBoundary(event) {
1641
+ const { seq } = event;
1642
+ if (typeof seq !== "number") return;
1643
+ if (event.method === "input.requested") {
1644
+ const id = event.params.data?.interrupt_id;
1645
+ if (typeof id !== "string") return;
1646
+ if (seq <= (this.#interruptRequestSeqs.get(id) ?? -1)) return;
1647
+ this.#interruptRequestSeqs.set(id, seq);
1648
+ } else if (event.method === "lifecycle" && require_namespace.isRootNamespace(event.params.namespace) && event.params.data?.event === "running") {
1649
+ const runId = synthesizedEventRunId(event.event_id);
1650
+ if (runId == null || this.#rootRunStartSeqs.has(runId)) return;
1651
+ this.#rootRunStartSeqs.set(runId, seq);
1652
+ } else return;
1653
+ this.#resolveRunBoundary();
1654
+ }
1655
+ /**
1656
+ * Place the barrier of a command that reported `applied_through_seq: 0`
1657
+ * once its run's `running` event has been seen, then classify the
1658
+ * interrupts buffered while it was unknown.
1659
+ */
1660
+ #resolveRunBoundary() {
1661
+ const command = this.#awaitingRunBoundary;
1662
+ if (command == null) return;
1663
+ let runStartSeq;
1664
+ if ("runId" in command) runStartSeq = this.#rootRunStartSeqs.get(command.runId);
1665
+ else {
1666
+ let lastRequested = -1;
1667
+ for (const id of command.answeredInterruptIds) {
1668
+ const seq = this.#interruptRequestSeqs.get(id);
1669
+ if (seq == null) return;
1670
+ lastRequested = Math.max(lastRequested, seq);
1671
+ }
1672
+ for (const seq of this.#rootRunStartSeqs.values()) if (seq > lastRequested && (runStartSeq == null || seq < runStartSeq)) runStartSeq = seq;
1673
+ }
1674
+ if (runStartSeq == null) return;
1675
+ this.#awaitingRunBoundary = void 0;
1676
+ this.#interruptReplayThroughSeq = runStartSeq - 1;
1677
+ this.#interruptCommandPending = false;
1678
+ this.#flushPendingInterruptEvents();
1679
+ }
1680
+ /**
1681
+ * Give up on a barrier whose run never showed up on the stream: a run
1682
+ * cancelled before it started, or a resume whose answered interrupts
1683
+ * were trimmed from the tape. Stop buffering and let the parked
1684
+ * interrupt state check decide what was held.
1685
+ */
1686
+ #abandonRunBoundary() {
1687
+ if (this.#awaitingRunBoundary == null) return;
1688
+ this.#awaitingRunBoundary = void 0;
1689
+ this.#interruptCommandPending = false;
1690
+ this.#flushPendingInterruptEvents();
1691
+ this.#settleParkedInterrupts();
1692
+ }
1693
+ /**
1614
1694
  * Process one root-pump event and update all root projections.
1615
1695
  *
1616
1696
  * @param event - Event yielded by the root subscription.
@@ -1902,24 +1982,38 @@ var StreamController = class {
1902
1982
  * Convert the hydrate-time allowlist into a replay barrier after a command
1903
1983
  * is accepted. Built-in transports update `lastAppliedThroughSeq` from the
1904
1984
  * command response before resolving `submitRun` / `respondInput`.
1985
+ *
1986
+ * @param command - The run the command started, used to find the barrier
1987
+ * on the thread stream when the response reports
1988
+ * `applied_through_seq: 0`.
1905
1989
  */
1906
- #advanceInterruptReplayBarrier() {
1990
+ #advanceInterruptReplayBarrier(command) {
1907
1991
  if (this.#hydratedActiveInterruptIds == null) {
1908
1992
  this.#discardPendingInterruptEvents();
1909
1993
  return;
1910
1994
  }
1911
1995
  const throughSeq = this.#thread?.ordering?.lastAppliedThroughSeq;
1996
+ if (throughSeq === 0 && command != null) {
1997
+ this.#awaitingRunBoundary = command;
1998
+ this.#resolveRunBoundary();
1999
+ return;
2000
+ }
1912
2001
  if (typeof throughSeq === "number") this.#interruptReplayThroughSeq = throughSeq;
1913
2002
  else {
1914
2003
  this.#hydratedActiveInterruptIds = null;
1915
2004
  this.#interruptReplayThroughSeq = void 0;
1916
2005
  }
1917
2006
  this.#interruptCommandPending = false;
2007
+ this.#flushPendingInterruptEvents();
2008
+ }
2009
+ /** Classify the interrupts buffered while a command was in flight. */
2010
+ #flushPendingInterruptEvents() {
1918
2011
  const pendingEvents = this.#pendingInterruptEvents;
1919
2012
  this.#pendingInterruptEvents = [];
1920
2013
  for (const event of pendingEvents) this.#recordInterrupt(event);
1921
2014
  }
1922
2015
  #discardPendingInterruptEvents() {
2016
+ this.#awaitingRunBoundary = void 0;
1923
2017
  this.#interruptCommandPending = false;
1924
2018
  this.#pendingInterruptEvents = [];
1925
2019
  }
@@ -2069,7 +2163,9 @@ var StreamController = class {
2069
2163
  * `stream.interrupts` empty while the checkpoint still has siblings.
2070
2164
  */
2071
2165
  async #awaitResumedRunTerminal(signal) {
2166
+ const generation = this.#submitGeneration;
2072
2167
  const terminal = await this.#awaitRootTerminal(signal, { skipInterruptedUntilRunning: true });
2168
+ if (this.#submitGeneration === generation) this.#abandonRunBoundary();
2073
2169
  if (!signal.aborted && !this.#disposed && (terminal.event === "interrupted" || terminal.event === "completed")) await this.#reconcilePendingInterruptsFromServer();
2074
2170
  return terminal;
2075
2171
  }