@langchain/langgraph-sdk 1.10.0 → 1.10.3-rc.0
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.
- package/dist/client/runs/index.cjs +1 -0
- package/dist/client/runs/index.cjs.map +1 -1
- package/dist/client/runs/index.d.cts.map +1 -1
- package/dist/client/runs/index.d.ts.map +1 -1
- package/dist/client/runs/index.js +1 -0
- package/dist/client/runs/index.js.map +1 -1
- package/dist/client/stream/transport/http.cjs +2 -0
- package/dist/client/stream/transport/http.cjs.map +1 -1
- package/dist/client/stream/transport/http.d.cts.map +1 -1
- package/dist/client/stream/transport/http.d.ts.map +1 -1
- package/dist/client/stream/transport/http.js +2 -0
- package/dist/client/stream/transport/http.js.map +1 -1
- package/dist/stream/controller.cjs +95 -23
- package/dist/stream/controller.cjs.map +1 -1
- package/dist/stream/controller.d.cts.map +1 -1
- package/dist/stream/controller.d.ts.map +1 -1
- package/dist/stream/controller.js +95 -23
- package/dist/stream/controller.js.map +1 -1
- package/dist/stream/message-reconciliation.cjs +47 -22
- package/dist/stream/message-reconciliation.cjs.map +1 -1
- package/dist/stream/message-reconciliation.js +47 -22
- package/dist/stream/message-reconciliation.js.map +1 -1
- package/dist/stream/root-message-projection.cjs +7 -5
- package/dist/stream/root-message-projection.cjs.map +1 -1
- package/dist/stream/root-message-projection.js +7 -5
- package/dist/stream/root-message-projection.js.map +1 -1
- package/package.json +4 -4
|
@@ -145,6 +145,16 @@ var StreamController = class {
|
|
|
145
145
|
#threadEventUnsubscribe;
|
|
146
146
|
#disposed = false;
|
|
147
147
|
#pendingDisposeTimer = null;
|
|
148
|
+
/**
|
|
149
|
+
* Interrupt ids this controller has locally marked resolved via
|
|
150
|
+
* {@link respond} / {@link respondAll}. Used only to suppress
|
|
151
|
+
* *historical* SSE replay of those `input.requested` events (there
|
|
152
|
+
* is no `input.responded` protocol event). Live events after the
|
|
153
|
+
* resume barrier — or a post-resume reconcile against
|
|
154
|
+
* `state.tasks[].interrupts` — clear an id from this set so a
|
|
155
|
+
* still-pending server interrupt can reappear on
|
|
156
|
+
* `rootStore.interrupts`.
|
|
157
|
+
*/
|
|
148
158
|
#resolvedInterrupts = /* @__PURE__ */ new Set();
|
|
149
159
|
/**
|
|
150
160
|
* Set of interrupt IDs the server reports as currently *active* on
|
|
@@ -559,24 +569,8 @@ var StreamController = class {
|
|
|
559
569
|
*/
|
|
560
570
|
if (Array.isArray(state?.tasks)) {
|
|
561
571
|
const generationAtFetch = this.#submitGeneration;
|
|
562
|
-
const activeInterrupts =
|
|
563
|
-
const
|
|
564
|
-
for (const task of state.tasks) {
|
|
565
|
-
if (!Array.isArray(task?.interrupts)) continue;
|
|
566
|
-
const checkpointNs = task?.checkpoint?.checkpoint_ns;
|
|
567
|
-
const namespace = typeof checkpointNs === "string" && checkpointNs.length > 0 ? checkpointNs.split("|").filter((segment) => segment.length > 0) : [...ROOT_NAMESPACE];
|
|
568
|
-
for (const interrupt of task.interrupts) {
|
|
569
|
-
const typed = interrupt;
|
|
570
|
-
const id = typed?.id;
|
|
571
|
-
if (typeof id !== "string" || activeIds.has(id)) continue;
|
|
572
|
-
activeIds.add(id);
|
|
573
|
-
activeInterrupts.push(require_interrupts.normalizeInterruptForClient({
|
|
574
|
-
id,
|
|
575
|
-
value: typed?.value,
|
|
576
|
-
namespace: Array.isArray(typed?.namespace) ? [...typed.namespace] : namespace
|
|
577
|
-
}));
|
|
578
|
-
}
|
|
579
|
-
}
|
|
572
|
+
const { activeInterrupts, activeIds } = collectActiveInterruptsFromTasks(state.tasks);
|
|
573
|
+
for (const id of activeIds) this.#resolvedInterrupts.delete(id);
|
|
580
574
|
this.rootStore.setState((s) => ({
|
|
581
575
|
...s,
|
|
582
576
|
interrupts: activeInterrupts,
|
|
@@ -1729,8 +1723,12 @@ var StreamController = class {
|
|
|
1729
1723
|
if (event.method !== "input.requested") return;
|
|
1730
1724
|
const data = event.params.data;
|
|
1731
1725
|
const interruptId = data?.interrupt_id;
|
|
1732
|
-
if (typeof interruptId !== "string"
|
|
1726
|
+
if (typeof interruptId !== "string") return;
|
|
1733
1727
|
const eventSeq = typeof event.seq === "number" ? event.seq : void 0;
|
|
1728
|
+
if (this.#resolvedInterrupts.has(interruptId)) {
|
|
1729
|
+
if (!(this.#hydratedActiveInterruptIds == null || typeof this.#interruptReplayThroughSeq === "number" && eventSeq != null && eventSeq > this.#interruptReplayThroughSeq)) return;
|
|
1730
|
+
this.#resolvedInterrupts.delete(interruptId);
|
|
1731
|
+
}
|
|
1734
1732
|
const isUnknownInterrupt = this.#hydratedActiveInterruptIds != null && !this.#hydratedActiveInterruptIds.has(interruptId);
|
|
1735
1733
|
if (isUnknownInterrupt && this.#interruptCommandPending) {
|
|
1736
1734
|
this.#pendingInterruptEvents.push(event);
|
|
@@ -1794,8 +1792,12 @@ var StreamController = class {
|
|
|
1794
1792
|
return [...ROOT_NAMESPACE];
|
|
1795
1793
|
}
|
|
1796
1794
|
/**
|
|
1797
|
-
* Mark an interrupt resolved for replay filtering and
|
|
1798
|
-
* removal into the root snapshot the framework hooks read.
|
|
1795
|
+
* Mark an interrupt resolved for historical-replay filtering and
|
|
1796
|
+
* mirror the removal into the root snapshot the framework hooks read.
|
|
1797
|
+
*
|
|
1798
|
+
* Tombstones are not permanent: a live `input.requested` after the
|
|
1799
|
+
* resume barrier, or {@link #reconcilePendingInterruptsFromServer},
|
|
1800
|
+
* clears the id when the server still reports it as pending.
|
|
1799
1801
|
*/
|
|
1800
1802
|
#markInterruptResolvedInRootStore(interruptId) {
|
|
1801
1803
|
this.#resolvedInterrupts.add(interruptId);
|
|
@@ -1810,6 +1812,38 @@ var StreamController = class {
|
|
|
1810
1812
|
});
|
|
1811
1813
|
}
|
|
1812
1814
|
/**
|
|
1815
|
+
* Re-sync `rootStore.interrupts` from the server's authoritative
|
|
1816
|
+
* `state.tasks[].interrupts` after a resumed run settles.
|
|
1817
|
+
*
|
|
1818
|
+
* Sequential `respond()` of parallel interrupts optimistically clears
|
|
1819
|
+
* each id client-side. When the server still has siblings pending and
|
|
1820
|
+
* does not re-emit `input.requested` for them (session map already
|
|
1821
|
+
* holds the id), this restore keeps `stream.interrupts` truthful so
|
|
1822
|
+
* callers do not free-text `submit()` into an ambiguous resume.
|
|
1823
|
+
*
|
|
1824
|
+
* Uses the same transport-aware state fetch as {@link hydrate}, and
|
|
1825
|
+
* drops the result if the thread swapped or a new submit/respond
|
|
1826
|
+
* started while the fetch was in flight.
|
|
1827
|
+
*/
|
|
1828
|
+
async #reconcilePendingInterruptsFromServer() {
|
|
1829
|
+
const threadId = this.#currentThreadId;
|
|
1830
|
+
if (threadId == null || this.#disposed) return;
|
|
1831
|
+
const generationAtFetch = this.#submitGeneration;
|
|
1832
|
+
try {
|
|
1833
|
+
const state = await this.#fetchHydrationState();
|
|
1834
|
+
if (this.#disposed || this.#currentThreadId !== threadId || this.#submitGeneration !== generationAtFetch) return;
|
|
1835
|
+
if (!Array.isArray(state?.tasks)) return;
|
|
1836
|
+
const { activeInterrupts, activeIds } = collectActiveInterruptsFromTasks(state.tasks);
|
|
1837
|
+
for (const id of activeIds) this.#resolvedInterrupts.delete(id);
|
|
1838
|
+
this.rootStore.setState((s) => ({
|
|
1839
|
+
...s,
|
|
1840
|
+
interrupts: activeInterrupts,
|
|
1841
|
+
interrupt: activeInterrupts[0]
|
|
1842
|
+
}));
|
|
1843
|
+
this.#hydratedActiveInterruptIds = activeIds;
|
|
1844
|
+
} catch {}
|
|
1845
|
+
}
|
|
1846
|
+
/**
|
|
1813
1847
|
* Resolve on the next root-namespace terminal lifecycle event
|
|
1814
1848
|
* (`completed` / `failed` / `interrupted`) or on abort.
|
|
1815
1849
|
*
|
|
@@ -1834,9 +1868,15 @@ var StreamController = class {
|
|
|
1834
1868
|
* but before `respondInput` calls `#prepareForNextRun`; accepting that
|
|
1835
1869
|
* terminal would unsubscribe the watcher before the resumed run's `failed`
|
|
1836
1870
|
* terminal arrives.
|
|
1871
|
+
*
|
|
1872
|
+
* On `interrupted` / `completed`, reconciles pending interrupts from
|
|
1873
|
+
* server state so optimistic clears from {@link respond} cannot leave
|
|
1874
|
+
* `stream.interrupts` empty while the checkpoint still has siblings.
|
|
1837
1875
|
*/
|
|
1838
|
-
#awaitResumedRunTerminal(signal) {
|
|
1839
|
-
|
|
1876
|
+
async #awaitResumedRunTerminal(signal) {
|
|
1877
|
+
const terminal = await this.#awaitRootTerminal(signal, { skipInterruptedUntilRunning: true });
|
|
1878
|
+
if (!signal.aborted && !this.#disposed && (terminal.event === "interrupted" || terminal.event === "completed")) await this.#reconcilePendingInterruptsFromServer();
|
|
1879
|
+
return terminal;
|
|
1840
1880
|
}
|
|
1841
1881
|
#awaitRootTerminal(signal, options) {
|
|
1842
1882
|
return new Promise((resolve) => {
|
|
@@ -1928,6 +1968,38 @@ function extractAndCoerceMessagesWithFallback(values, messagesKey) {
|
|
|
1928
1968
|
if (!Array.isArray(raw)) return null;
|
|
1929
1969
|
return require_message_coercion.ensureMessageInstances(raw);
|
|
1930
1970
|
}
|
|
1971
|
+
/**
|
|
1972
|
+
* Collect pending interrupts from a `threads.getState()` `tasks` array.
|
|
1973
|
+
*
|
|
1974
|
+
* Shared by {@link StreamController.hydrate} and the post-resume
|
|
1975
|
+
* reconcile path so both stay aligned on namespace derivation and
|
|
1976
|
+
* id dedupe.
|
|
1977
|
+
*/
|
|
1978
|
+
function collectActiveInterruptsFromTasks(tasks) {
|
|
1979
|
+
const activeInterrupts = [];
|
|
1980
|
+
const activeIds = /* @__PURE__ */ new Set();
|
|
1981
|
+
for (const task of tasks) {
|
|
1982
|
+
if (!Array.isArray(task?.interrupts)) continue;
|
|
1983
|
+
const typedTask = task;
|
|
1984
|
+
const checkpointNs = typedTask.checkpoint?.checkpoint_ns;
|
|
1985
|
+
const namespace = typeof checkpointNs === "string" && checkpointNs.length > 0 ? checkpointNs.split("|").filter((segment) => segment.length > 0) : [...ROOT_NAMESPACE];
|
|
1986
|
+
for (const interrupt of typedTask.interrupts) {
|
|
1987
|
+
const typed = interrupt;
|
|
1988
|
+
const id = typed?.id;
|
|
1989
|
+
if (typeof id !== "string" || activeIds.has(id)) continue;
|
|
1990
|
+
activeIds.add(id);
|
|
1991
|
+
activeInterrupts.push(require_interrupts.normalizeInterruptForClient({
|
|
1992
|
+
id,
|
|
1993
|
+
value: typed?.value,
|
|
1994
|
+
namespace: Array.isArray(typed?.namespace) ? [...typed.namespace] : namespace
|
|
1995
|
+
}));
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
return {
|
|
1999
|
+
activeInterrupts,
|
|
2000
|
+
activeIds
|
|
2001
|
+
};
|
|
2002
|
+
}
|
|
1931
2003
|
//#endregion
|
|
1932
2004
|
exports.ROOT_PUMP_CHANNELS = ROOT_PUMP_CHANNELS;
|
|
1933
2005
|
exports.StreamController = StreamController;
|