@langchain/langgraph-sdk 1.10.3-rc.0 → 1.10.3-rc.2
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/stream/index.cjs +23 -1
- package/dist/client/stream/index.cjs.map +1 -1
- package/dist/client/stream/index.d.cts +6 -0
- package/dist/client/stream/index.d.cts.map +1 -1
- package/dist/client/stream/index.d.ts +6 -0
- package/dist/client/stream/index.d.ts.map +1 -1
- package/dist/client/stream/index.js +23 -1
- package/dist/client/stream/index.js.map +1 -1
- package/dist/client/stream/transport/http.cjs +14 -3
- 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 +14 -3
- package/dist/client/stream/transport/http.js.map +1 -1
- package/dist/stream/controller.cjs +94 -20
- 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 +94 -20
- package/dist/stream/controller.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controller.d.cts","names":[],"sources":["../../src/stream/controller.ts"],"mappings":";;;;;;;;;;;;;;AA6KA;;cAAa,kBAAA,WAA6B,OAAA;;
|
|
1
|
+
{"version":3,"file":"controller.d.cts","names":[],"sources":["../../src/stream/controller.ts"],"mappings":";;;;;;;;;;;;;;AA6KA;;cAAa,kBAAA,WAA6B,OAAA;;AAwC1C;;;;;;;;;;;;cAAa,gBAAA,4BACgB,MAAA,8EAEO,MAAA;EAAA;WAEzB,SAAA,EAAW,WAAA,CAAY,YAAA,CAAa,SAAA,EAAW,aAAA;EAAA,SAC/C,aAAA,EAAe,WAAA,CAAY,WAAA;EAAA,SAC3B,aAAA,EAAe,WAAA,CAAY,WAAA;EAAA,SAC3B,mBAAA,EAAqB,WAAA,CAAY,iBAAA;EAAA,SACjC,oBAAA,EAAsB,WAAA,CAAY,kBAAA;EAAA,SAClC,UAAA,EAAY,WAAA,CAAY,uBAAA,CAAwB,SAAA;EAAA,SAChD,QAAA,EAAU,eAAA;EAiK0B;;;;;EAA7C,WAAA,CAAY,OAAA,EAAS,uBAAA,CAAwB,SAAA;EAiyBF;;;;;;;EAAA,IAtqBvC,gBAAA,CAAA,GAAoB,OAAA;EA20BS;;;;;;;;EAlxB3B,OAAA,CAAQ,QAAA,mBAA2B,OAAA;EAogCpB;;;;;;;;;;;;;;EA7pBf,wBAAA,CAAyB,UAAA,WAAqB,OAAA;EAjsB5B;;;;;;;;EAq8BlB,MAAA,CACJ,KAAA,WACA,OAAA,GAAU,mBAAA,CAAoB,SAAA,EAAW,gBAAA,IACxC,OAAA;EAr8B4B;;;;;;EA+8BzB,IAAA,CAAK,OAAA,GAAU,iBAAA,GAAoB,OAAA;EA78BtB;;;;EAi+Bb,UAAA,CAAA,GAAc,OAAA;EArsBhB;;;;;;;;;;;EAqwBE,YAAA,CAAa,EAAA,WAAa,OAAA;EA/FW;;;EAsGrC,UAAA,CAAA,GAAc,OAAA;EA3FC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAwJf,OAAA,CACJ,QAAA,WACA,OAAA,GAAU,oBAAA,CAAqB,gBAAA,IAC9B,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiHG,UAAA,CACJ,aAAA,EAAe,MAAA,mBACf,OAAA,GAAU,uBAAA,CAAwB,gBAAA,IACjC,OAAA;;;;EA4DG,OAAA,CAAA,GAAW,OAAA;;;;;;;;;;;;;;;;EAyBjB,QAAA,CAAA;;;;;;EA4BA,SAAA,CAAA,GAAa,YAAA;;;;;;;;EAWb,eAAA,CACE,QAAA,GAAW,MAAA,EAAQ,YAAA;AAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controller.d.ts","names":[],"sources":["../../src/stream/controller.ts"],"mappings":";;;;;;;;;;;;;;;AA6KA;cAAa,kBAAA,WAA6B,OAAA;;;
|
|
1
|
+
{"version":3,"file":"controller.d.ts","names":[],"sources":["../../src/stream/controller.ts"],"mappings":";;;;;;;;;;;;;;;AA6KA;cAAa,kBAAA,WAA6B,OAAA;;;AAwC1C;;;;;;;;;;;cAAa,gBAAA,4BACgB,MAAA,8EAEO,MAAA;EAAA;WAEzB,SAAA,EAAW,WAAA,CAAY,YAAA,CAAa,SAAA,EAAW,aAAA;EAAA,SAC/C,aAAA,EAAe,WAAA,CAAY,WAAA;EAAA,SAC3B,aAAA,EAAe,WAAA,CAAY,WAAA;EAAA,SAC3B,mBAAA,EAAqB,WAAA,CAAY,iBAAA;EAAA,SACjC,oBAAA,EAAsB,WAAA,CAAY,kBAAA;EAAA,SAClC,UAAA,EAAY,WAAA,CAAY,uBAAA,CAAwB,SAAA;EAAA,SAChD,QAAA,EAAU,eAAA;EAAA;;;;;EAiKnB,WAAA,CAAY,OAAA,EAAS,uBAAA,CAAwB,SAAA;EAiyBb;;;;;;;EAAA,IAtqB5B,gBAAA,CAAA,GAAoB,OAAA;EA4wBJ;;;;;;;;EAntBd,OAAA,CAAQ,QAAA,mBAA2B,OAAA;EAw/B5B;;;;;;;;;;;;;;EAjpBP,wBAAA,CAAyB,UAAA,WAAqB,OAAA;EAjsB3C;;;;;;;;EAq8BH,MAAA,CACJ,KAAA,WACA,OAAA,GAAU,mBAAA,CAAoB,SAAA,EAAW,gBAAA,IACxC,OAAA;EAr8BM;;;;;;EA+8BH,IAAA,CAAK,OAAA,GAAU,iBAAA,GAAoB,OAAA;EA78BhC;;;;EAi+BH,UAAA,CAAA,GAAc,OAAA;EAh0BR;;;;;;;;;;;EAg4BN,YAAA,CAAa,EAAA,WAAa,OAAA;EA/FA;;;EAsG1B,UAAA,CAAA,GAAc,OAAA;EA3Fd;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAwJA,OAAA,CACJ,QAAA,WACA,OAAA,GAAU,oBAAA,CAAqB,gBAAA,IAC9B,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiHG,UAAA,CACJ,aAAA,EAAe,MAAA,mBACf,OAAA,GAAU,uBAAA,CAAwB,gBAAA,IACjC,OAAA;;;;EA4DG,OAAA,CAAA,GAAW,OAAA;;;;;;;;;;;;;;;;EAyBjB,QAAA,CAAA;;;;;;EA4BA,SAAA,CAAA,GAAa,YAAA;;;;;;;;EAWb,eAAA,CACE,QAAA,GAAW,MAAA,EAAQ,YAAA;AAAA"}
|
|
@@ -104,6 +104,8 @@ const ROOT_PUMP_CHANNELS = [
|
|
|
104
104
|
"messages",
|
|
105
105
|
"tools"
|
|
106
106
|
];
|
|
107
|
+
const PARKED_INTERRUPT_SETTLE_MS = 5e3;
|
|
108
|
+
const PARKED_INTERRUPT_SETTLE_POLL_MS = 500;
|
|
107
109
|
/**
|
|
108
110
|
* Coordinates one thread's protocol-v2 stream and exposes stable
|
|
109
111
|
* observable projections for framework bindings.
|
|
@@ -142,17 +144,16 @@ var StreamController = class {
|
|
|
142
144
|
*/
|
|
143
145
|
#rootPumpDeferred = false;
|
|
144
146
|
#threadEventUnsubscribe;
|
|
147
|
+
#threadErrorUnsubscribe;
|
|
145
148
|
#disposed = false;
|
|
146
149
|
#pendingDisposeTimer = null;
|
|
147
150
|
/**
|
|
148
151
|
* Interrupt ids this controller has locally marked resolved via
|
|
149
152
|
* {@link respond} / {@link respondAll}. Used only to suppress
|
|
150
153
|
* *historical* SSE replay of those `input.requested` events (there
|
|
151
|
-
* is no `input.responded` protocol event).
|
|
152
|
-
* resume barrier
|
|
153
|
-
*
|
|
154
|
-
* still-pending server interrupt can reappear on
|
|
155
|
-
* `rootStore.interrupts`.
|
|
154
|
+
* is no `input.responded` protocol event). A live event after the
|
|
155
|
+
* resume barrier clears an id from this set so an interrupt raised
|
|
156
|
+
* again by a later run can reappear on `rootStore.interrupts`.
|
|
156
157
|
*/
|
|
157
158
|
#resolvedInterrupts = /* @__PURE__ */ new Set();
|
|
158
159
|
/**
|
|
@@ -175,6 +176,9 @@ var StreamController = class {
|
|
|
175
176
|
* from the newly-started run and must be accepted.
|
|
176
177
|
*/
|
|
177
178
|
#interruptReplayThroughSeq;
|
|
179
|
+
/** Unknown inputs waiting to be classified as live or replayed history. */
|
|
180
|
+
#pendingHydrationInterruptEvents = [];
|
|
181
|
+
#settleParkedInterruptsPromise;
|
|
178
182
|
/**
|
|
179
183
|
* Unknown interrupt events received between local command dispatch and its
|
|
180
184
|
* response. The response supplies the sequence barrier needed to classify
|
|
@@ -326,7 +330,8 @@ var StreamController = class {
|
|
|
326
330
|
onSubmitStart: () => {
|
|
327
331
|
this.#submitGeneration += 1;
|
|
328
332
|
this.#interruptCommandPending = this.#hydratedActiveInterruptIds != null;
|
|
329
|
-
this.#pendingInterruptEvents =
|
|
333
|
+
this.#pendingInterruptEvents = this.#pendingHydrationInterruptEvents;
|
|
334
|
+
this.#pendingHydrationInterruptEvents = [];
|
|
330
335
|
},
|
|
331
336
|
onRunStart: () => this.#markLocalRunStart(),
|
|
332
337
|
onRunCreated: (runId) => {
|
|
@@ -1281,6 +1286,8 @@ var StreamController = class {
|
|
|
1281
1286
|
this.registry.bind(void 0);
|
|
1282
1287
|
this.#threadEventUnsubscribe?.();
|
|
1283
1288
|
this.#threadEventUnsubscribe = void 0;
|
|
1289
|
+
this.#threadErrorUnsubscribe?.();
|
|
1290
|
+
this.#threadErrorUnsubscribe = void 0;
|
|
1284
1291
|
/**
|
|
1285
1292
|
* Persistent lifecycle driver is scoped to the current thread
|
|
1286
1293
|
* stream. Remove it so a swap to a new thread starts with a clean
|
|
@@ -1309,6 +1316,7 @@ var StreamController = class {
|
|
|
1309
1316
|
this.#messageMetadata.reset();
|
|
1310
1317
|
this.#hydratedActiveInterruptIds = null;
|
|
1311
1318
|
this.#interruptReplayThroughSeq = void 0;
|
|
1319
|
+
this.#pendingHydrationInterruptEvents = [];
|
|
1312
1320
|
this.#discardPendingInterruptEvents();
|
|
1313
1321
|
this.queueStore.setState(() => EMPTY_QUEUE);
|
|
1314
1322
|
try {
|
|
@@ -1355,6 +1363,13 @@ var StreamController = class {
|
|
|
1355
1363
|
* pump's narrow filter.
|
|
1356
1364
|
*/
|
|
1357
1365
|
this.#threadEventUnsubscribe = thread.onEvent((event) => this.#onWildcardEvent(event));
|
|
1366
|
+
this.#threadErrorUnsubscribe = thread.onError((error) => {
|
|
1367
|
+
this.rootStore.setState((s) => ({
|
|
1368
|
+
...s,
|
|
1369
|
+
error,
|
|
1370
|
+
isLoading: false
|
|
1371
|
+
}));
|
|
1372
|
+
});
|
|
1358
1373
|
/**
|
|
1359
1374
|
* Persistent isLoading driver. Drives `isLoading` from
|
|
1360
1375
|
* root-namespace lifecycle events so that in-flight runs observed
|
|
@@ -1493,6 +1508,7 @@ var StreamController = class {
|
|
|
1493
1508
|
* resume targeting.
|
|
1494
1509
|
*/
|
|
1495
1510
|
this.#recordInterrupt(event);
|
|
1511
|
+
this.#settleParkedInterruptsOnTerminal(event);
|
|
1496
1512
|
}
|
|
1497
1513
|
/**
|
|
1498
1514
|
* Process one root-pump event and update all root projections.
|
|
@@ -1733,7 +1749,12 @@ var StreamController = class {
|
|
|
1733
1749
|
this.#pendingInterruptEvents.push(event);
|
|
1734
1750
|
return;
|
|
1735
1751
|
}
|
|
1736
|
-
|
|
1752
|
+
const barrier = this.#interruptReplayThroughSeq;
|
|
1753
|
+
if (isUnknownInterrupt && barrier == null) {
|
|
1754
|
+
this.#pendingHydrationInterruptEvents.push(event);
|
|
1755
|
+
return;
|
|
1756
|
+
}
|
|
1757
|
+
if (isUnknownInterrupt && barrier != null && eventSeq != null && eventSeq <= barrier) return;
|
|
1737
1758
|
const namespace = Array.isArray(event.params.namespace) ? [...event.params.namespace] : [...ROOT_NAMESPACE];
|
|
1738
1759
|
const interrupt = normalizeInterruptForClient({
|
|
1739
1760
|
id: interruptId,
|
|
@@ -1795,8 +1816,7 @@ var StreamController = class {
|
|
|
1795
1816
|
* mirror the removal into the root snapshot the framework hooks read.
|
|
1796
1817
|
*
|
|
1797
1818
|
* Tombstones are not permanent: a live `input.requested` after the
|
|
1798
|
-
* resume barrier
|
|
1799
|
-
* clears the id when the server still reports it as pending.
|
|
1819
|
+
* resume barrier clears the id when the same interrupt is raised again.
|
|
1800
1820
|
*/
|
|
1801
1821
|
#markInterruptResolvedInRootStore(interruptId) {
|
|
1802
1822
|
this.#resolvedInterrupts.add(interruptId);
|
|
@@ -1815,10 +1835,11 @@ var StreamController = class {
|
|
|
1815
1835
|
* `state.tasks[].interrupts` after a resumed run settles.
|
|
1816
1836
|
*
|
|
1817
1837
|
* Sequential `respond()` of parallel interrupts optimistically clears
|
|
1818
|
-
* each id client-side.
|
|
1819
|
-
*
|
|
1820
|
-
*
|
|
1821
|
-
*
|
|
1838
|
+
* each targeted id client-side. The server checkpoint can continue to
|
|
1839
|
+
* list the original interrupt batch after accepting a partial response,
|
|
1840
|
+
* so locally-resolved ids remain filtered while untouched siblings are
|
|
1841
|
+
* restored. This keeps `stream.interrupts` truthful without re-exposing
|
|
1842
|
+
* an already-consumed interrupt.
|
|
1822
1843
|
*
|
|
1823
1844
|
* Uses the same transport-aware state fetch as {@link hydrate}, and
|
|
1824
1845
|
* drops the result if the thread swapped or a new submit/respond
|
|
@@ -1826,21 +1847,69 @@ var StreamController = class {
|
|
|
1826
1847
|
*/
|
|
1827
1848
|
async #reconcilePendingInterruptsFromServer() {
|
|
1828
1849
|
const threadId = this.#currentThreadId;
|
|
1829
|
-
if (threadId == null || this.#disposed) return;
|
|
1850
|
+
if (threadId == null || this.#disposed) return null;
|
|
1830
1851
|
const generationAtFetch = this.#submitGeneration;
|
|
1831
1852
|
try {
|
|
1832
1853
|
const state = await this.#fetchHydrationState();
|
|
1833
|
-
if (this.#disposed || this.#currentThreadId !== threadId || this.#submitGeneration !== generationAtFetch) return;
|
|
1834
|
-
if (!Array.isArray(state?.tasks)) return;
|
|
1854
|
+
if (this.#disposed || this.#currentThreadId !== threadId || this.#submitGeneration !== generationAtFetch) return null;
|
|
1855
|
+
if (!Array.isArray(state?.tasks)) return null;
|
|
1835
1856
|
const { activeInterrupts, activeIds } = collectActiveInterruptsFromTasks(state.tasks);
|
|
1836
|
-
|
|
1857
|
+
const pendingInterrupts = activeInterrupts.filter((interrupt) => interrupt.id == null || !this.#resolvedInterrupts.has(interrupt.id));
|
|
1837
1858
|
this.rootStore.setState((s) => ({
|
|
1838
1859
|
...s,
|
|
1839
|
-
interrupts:
|
|
1840
|
-
interrupt:
|
|
1860
|
+
interrupts: pendingInterrupts,
|
|
1861
|
+
interrupt: pendingInterrupts[0]
|
|
1841
1862
|
}));
|
|
1842
1863
|
this.#hydratedActiveInterruptIds = activeIds;
|
|
1843
|
-
|
|
1864
|
+
return state;
|
|
1865
|
+
} catch {
|
|
1866
|
+
return null;
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
#settleParkedInterruptsOnTerminal(event) {
|
|
1870
|
+
if (this.#pendingHydrationInterruptEvents.length === 0) return;
|
|
1871
|
+
if (event.method !== "lifecycle") return;
|
|
1872
|
+
if (!isRootNamespace(event.params.namespace)) return;
|
|
1873
|
+
const status = event.params.data?.event;
|
|
1874
|
+
if (status !== "interrupted" && status !== "completed" && status !== "failed") return;
|
|
1875
|
+
this.#settleParkedInterrupts();
|
|
1876
|
+
}
|
|
1877
|
+
/**
|
|
1878
|
+
* Decide parked interrupts with server state instead of a stream marker.
|
|
1879
|
+
*
|
|
1880
|
+
* After a passive rejoin the stream cannot tell a replayed, already
|
|
1881
|
+
* resolved `input.requested` from one the current run just raised: the
|
|
1882
|
+
* SSE replays the thread buffer on connect with no marker for where the
|
|
1883
|
+
* replay ends. `state.tasks[].interrupts` is authoritative: parked ids it
|
|
1884
|
+
* lists are live, the rest are history.
|
|
1885
|
+
*/
|
|
1886
|
+
async #settleParkedInterrupts() {
|
|
1887
|
+
const makePromise = async () => {
|
|
1888
|
+
try {
|
|
1889
|
+
const threadId = this.#currentThreadId;
|
|
1890
|
+
const generation = this.#submitGeneration;
|
|
1891
|
+
const deadline = Date.now() + PARKED_INTERRUPT_SETTLE_MS;
|
|
1892
|
+
for (;;) {
|
|
1893
|
+
if (this.#disposed || this.#currentThreadId !== threadId || this.#submitGeneration !== generation || this.#pendingHydrationInterruptEvents.length === 0) return;
|
|
1894
|
+
const parkedIds = new Set(this.#pendingHydrationInterruptEvents.map((event) => event.params.data.interrupt_id));
|
|
1895
|
+
const state = await this.#reconcilePendingInterruptsFromServer();
|
|
1896
|
+
if (state != null) {
|
|
1897
|
+
if ([...this.#hydratedActiveInterruptIds ?? []].some((id) => parkedIds.has(id)) || !isThreadStateActive(state)) {
|
|
1898
|
+
this.#pendingHydrationInterruptEvents = [];
|
|
1899
|
+
return;
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
if (Date.now() >= deadline) return;
|
|
1903
|
+
await new Promise((resolve) => {
|
|
1904
|
+
setTimeout(resolve, PARKED_INTERRUPT_SETTLE_POLL_MS);
|
|
1905
|
+
});
|
|
1906
|
+
}
|
|
1907
|
+
} finally {
|
|
1908
|
+
this.#settleParkedInterruptsPromise = void 0;
|
|
1909
|
+
}
|
|
1910
|
+
};
|
|
1911
|
+
this.#settleParkedInterruptsPromise ??= makePromise();
|
|
1912
|
+
return this.#settleParkedInterruptsPromise;
|
|
1844
1913
|
}
|
|
1845
1914
|
/**
|
|
1846
1915
|
* Resolve on the next root-namespace terminal lifecycle event
|
|
@@ -1886,6 +1955,7 @@ var StreamController = class {
|
|
|
1886
1955
|
settled = true;
|
|
1887
1956
|
unsubscribeRoot?.();
|
|
1888
1957
|
unsubscribeThread?.();
|
|
1958
|
+
unsubscribeError?.();
|
|
1889
1959
|
signal.removeEventListener("abort", finishAborted);
|
|
1890
1960
|
resolve(result);
|
|
1891
1961
|
}
|
|
@@ -1911,6 +1981,10 @@ var StreamController = class {
|
|
|
1911
1981
|
};
|
|
1912
1982
|
const unsubscribeRoot = this.#rootBus.subscribe(onEvent);
|
|
1913
1983
|
const unsubscribeThread = this.#thread?.onEvent(onEvent);
|
|
1984
|
+
const unsubscribeError = this.#thread?.onError((error) => finish({
|
|
1985
|
+
event: "failed",
|
|
1986
|
+
error: error.message
|
|
1987
|
+
}));
|
|
1914
1988
|
if (signal.aborted) finishAborted();
|
|
1915
1989
|
else signal.addEventListener("abort", finishAborted, { once: true });
|
|
1916
1990
|
});
|