@langchain/langgraph-sdk 1.11.2 → 1.12.0-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.
Files changed (52) hide show
  1. package/dist/client/base.cjs +7 -0
  2. package/dist/client/base.cjs.map +1 -1
  3. package/dist/client/base.d.cts.map +1 -1
  4. package/dist/client/base.d.ts.map +1 -1
  5. package/dist/client/base.js +7 -0
  6. package/dist/client/base.js.map +1 -1
  7. package/dist/stream/controller.cjs +39 -10
  8. package/dist/stream/controller.cjs.map +1 -1
  9. package/dist/stream/controller.d.cts +7 -6
  10. package/dist/stream/controller.d.cts.map +1 -1
  11. package/dist/stream/controller.d.ts +7 -6
  12. package/dist/stream/controller.d.ts.map +1 -1
  13. package/dist/stream/controller.js +37 -8
  14. package/dist/stream/controller.js.map +1 -1
  15. package/dist/stream/dispatch-config.cjs +23 -0
  16. package/dist/stream/dispatch-config.cjs.map +1 -0
  17. package/dist/stream/dispatch-config.js +23 -0
  18. package/dist/stream/dispatch-config.js.map +1 -0
  19. package/dist/stream/index.d.cts +1 -1
  20. package/dist/stream/index.d.ts +1 -1
  21. package/dist/stream/lifecycle-loading-tracker.cjs +8 -0
  22. package/dist/stream/lifecycle-loading-tracker.cjs.map +1 -1
  23. package/dist/stream/lifecycle-loading-tracker.js +8 -0
  24. package/dist/stream/lifecycle-loading-tracker.js.map +1 -1
  25. package/dist/stream/queue-adapter-agent-server.cjs +204 -0
  26. package/dist/stream/queue-adapter-agent-server.cjs.map +1 -0
  27. package/dist/stream/queue-adapter-agent-server.js +203 -0
  28. package/dist/stream/queue-adapter-agent-server.js.map +1 -0
  29. package/dist/stream/queue-adapter-local.cjs +50 -0
  30. package/dist/stream/queue-adapter-local.cjs.map +1 -0
  31. package/dist/stream/queue-adapter-local.js +49 -0
  32. package/dist/stream/queue-adapter-local.js.map +1 -0
  33. package/dist/stream/queue-adapter.cjs +7 -0
  34. package/dist/stream/queue-adapter.cjs.map +1 -0
  35. package/dist/stream/{submit-coordinator.d.cts → queue-adapter.d.cts} +6 -9
  36. package/dist/stream/queue-adapter.d.cts.map +1 -0
  37. package/dist/stream/queue-adapter.d.ts +24 -0
  38. package/dist/stream/queue-adapter.d.ts.map +1 -0
  39. package/dist/stream/queue-adapter.js +7 -0
  40. package/dist/stream/queue-adapter.js.map +1 -0
  41. package/dist/stream/submit-coordinator.cjs +82 -86
  42. package/dist/stream/submit-coordinator.cjs.map +1 -1
  43. package/dist/stream/submit-coordinator.d.ts +1 -26
  44. package/dist/stream/submit-coordinator.js +82 -85
  45. package/dist/stream/submit-coordinator.js.map +1 -1
  46. package/dist/stream/types.d.cts +25 -4
  47. package/dist/stream/types.d.cts.map +1 -1
  48. package/dist/stream/types.d.ts +25 -4
  49. package/dist/stream/types.d.ts.map +1 -1
  50. package/package.json +1 -1
  51. package/dist/stream/submit-coordinator.d.cts.map +0 -1
  52. package/dist/stream/submit-coordinator.d.ts.map +0 -1
@@ -0,0 +1,23 @@
1
+ //#region src/stream/dispatch-config.ts
2
+ /**
3
+ * Merge `thread_id` into a user-supplied `config.configurable` blob.
4
+ *
5
+ * The platform expects `config.configurable.thread_id` on every run
6
+ * dispatch. Applied last so a user-supplied value can't override the
7
+ * active thread id.
8
+ */
9
+ function bindThreadConfig(config, threadId) {
10
+ const base = config != null && typeof config === "object" ? config : {};
11
+ const configurable = base.configurable != null && typeof base.configurable === "object" ? base.configurable : {};
12
+ return {
13
+ ...base,
14
+ configurable: {
15
+ ...configurable,
16
+ thread_id: threadId
17
+ }
18
+ };
19
+ }
20
+ //#endregion
21
+ exports.bindThreadConfig = bindThreadConfig;
22
+
23
+ //# sourceMappingURL=dispatch-config.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatch-config.cjs","names":[],"sources":["../../src/stream/dispatch-config.ts"],"sourcesContent":["/**\n * Merge `thread_id` into a user-supplied `config.configurable` blob.\n *\n * The platform expects `config.configurable.thread_id` on every run\n * dispatch. Applied last so a user-supplied value can't override the\n * active thread id.\n */\nexport function bindThreadConfig(\n config: unknown,\n threadId: string\n): Record<string, unknown> {\n const base =\n config != null && typeof config === \"object\"\n ? (config as Record<string, unknown>)\n : {};\n const configurable =\n base.configurable != null && typeof base.configurable === \"object\"\n ? (base.configurable as Record<string, unknown>)\n : {};\n return {\n ...base,\n configurable: {\n ...configurable,\n thread_id: threadId,\n },\n };\n}\n"],"mappings":";;;;;;;;AAOA,SAAgB,iBACd,QACA,UACyB;CACzB,MAAM,OACJ,UAAU,QAAQ,OAAO,WAAW,WAC/B,SACD,EAAE;CACR,MAAM,eACJ,KAAK,gBAAgB,QAAQ,OAAO,KAAK,iBAAiB,WACrD,KAAK,eACN,EAAE;AACR,QAAO;EACL,GAAG;EACH,cAAc;GACZ,GAAG;GACH,WAAW;GACZ;EACF"}
@@ -0,0 +1,23 @@
1
+ //#region src/stream/dispatch-config.ts
2
+ /**
3
+ * Merge `thread_id` into a user-supplied `config.configurable` blob.
4
+ *
5
+ * The platform expects `config.configurable.thread_id` on every run
6
+ * dispatch. Applied last so a user-supplied value can't override the
7
+ * active thread id.
8
+ */
9
+ function bindThreadConfig(config, threadId) {
10
+ const base = config != null && typeof config === "object" ? config : {};
11
+ const configurable = base.configurable != null && typeof base.configurable === "object" ? base.configurable : {};
12
+ return {
13
+ ...base,
14
+ configurable: {
15
+ ...configurable,
16
+ thread_id: threadId
17
+ }
18
+ };
19
+ }
20
+ //#endregion
21
+ export { bindThreadConfig };
22
+
23
+ //# sourceMappingURL=dispatch-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatch-config.js","names":[],"sources":["../../src/stream/dispatch-config.ts"],"sourcesContent":["/**\n * Merge `thread_id` into a user-supplied `config.configurable` blob.\n *\n * The platform expects `config.configurable.thread_id` on every run\n * dispatch. Applied last so a user-supplied value can't override the\n * active thread id.\n */\nexport function bindThreadConfig(\n config: unknown,\n threadId: string\n): Record<string, unknown> {\n const base =\n config != null && typeof config === \"object\"\n ? (config as Record<string, unknown>)\n : {};\n const configurable =\n base.configurable != null && typeof base.configurable === \"object\"\n ? (base.configurable as Record<string, unknown>)\n : {};\n return {\n ...base,\n configurable: {\n ...configurable,\n thread_id: threadId,\n },\n };\n}\n"],"mappings":";;;;;;;;AAOA,SAAgB,iBACd,QACA,UACyB;CACzB,MAAM,OACJ,UAAU,QAAQ,OAAO,WAAW,WAC/B,SACD,EAAE;CACR,MAAM,eACJ,KAAK,gBAAgB,QAAQ,OAAO,KAAK,iBAAiB,WACrD,KAAK,eACN,EAAE;AACR,QAAO;EACL,GAAG;EACH,cAAc;GACZ,GAAG;GACH,WAAW;GACZ;EACF"}
@@ -12,7 +12,7 @@ import { ChannelRegistry } from "./channel-registry.cjs";
12
12
  import { SubagentDiscovery, SubagentMap } from "./discovery/subagents.cjs";
13
13
  import { SubgraphByNodeMap, SubgraphDiscovery, SubgraphMap } from "./discovery/subgraphs.cjs";
14
14
  import { MessageMetadata, MessageMetadataMap } from "./message-metadata-tracker.cjs";
15
- import { SubmissionQueueEntry, SubmissionQueueSnapshot } from "./submit-coordinator.cjs";
15
+ import { SubmissionQueueEntry, SubmissionQueueSnapshot } from "./queue-adapter.cjs";
16
16
  import { ROOT_PUMP_CHANNELS, StreamController } from "./controller.cjs";
17
17
  import { messagesProjection } from "./projections/messages.cjs";
18
18
  import { toolCallsProjection } from "./projections/tool-calls.cjs";
@@ -12,7 +12,7 @@ import { ChannelRegistry } from "./channel-registry.js";
12
12
  import { SubagentDiscovery, SubagentMap } from "./discovery/subagents.js";
13
13
  import { SubgraphByNodeMap, SubgraphDiscovery, SubgraphMap } from "./discovery/subgraphs.js";
14
14
  import { MessageMetadata, MessageMetadataMap } from "./message-metadata-tracker.js";
15
- import { SubmissionQueueEntry, SubmissionQueueSnapshot } from "./submit-coordinator.js";
15
+ import { SubmissionQueueEntry, SubmissionQueueSnapshot } from "./queue-adapter.js";
16
16
  import { ROOT_PUMP_CHANNELS, StreamController } from "./controller.js";
17
17
  import { messagesProjection } from "./projections/messages.js";
18
18
  import { toolCallsProjection } from "./projections/tool-calls.js";
@@ -26,13 +26,19 @@ var LifecycleLoadingTracker = class {
26
26
  * newer `running` does not incorrectly clear `isLoading`.
27
27
  */
28
28
  #lastRunningLifecycleSeq = -1;
29
+ /** Notified once `isLoading` actually settles to `false` on a terminal event. */
30
+ #onSettled;
29
31
  /**
30
32
  * @param params.store - Store whose `isLoading` slot we drive.
31
33
  * @param params.isDisposed - Disposal probe consulted from deferred callbacks.
34
+ * @param params.onSettled - Called once `isLoading` flips to `false`,
35
+ * including for a run this client only observed and never
36
+ * dispatched itself.
32
37
  */
33
38
  constructor(params) {
34
39
  this.#store = params.store;
35
40
  this.#isDisposed = params.isDisposed;
41
+ this.#onSettled = params.onSettled;
36
42
  }
37
43
  /**
38
44
  * Bound listener suitable for `EventBus.subscribe`. Re-exposed as a
@@ -79,10 +85,12 @@ var LifecycleLoadingTracker = class {
79
85
  setTimeout(() => {
80
86
  if (this.#isDisposed()) return;
81
87
  if (seq != null && this.#lastRunningLifecycleSeq > seq) return;
88
+ const wasLoading = this.#store.getSnapshot().isLoading;
82
89
  this.#store.setState((s) => s.isLoading ? {
83
90
  ...s,
84
91
  isLoading: false
85
92
  } : s);
93
+ if (wasLoading) this.#onSettled?.();
86
94
  }, 0);
87
95
  }
88
96
  }
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle-loading-tracker.cjs","names":["#store","#isDisposed","#lastTerminalLifecycleSeq","#lastRunningLifecycleSeq","isRootNamespace"],"sources":["../../src/stream/lifecycle-loading-tracker.ts"],"sourcesContent":["/**\n * Drives the {@link RootSnapshot.isLoading} flag from root lifecycle\n * events.\n *\n * # What it does\n *\n * The tracker watches a stream of protocol events and flips the\n * `isLoading` slot of a {@link StreamStore} based on root-namespace\n * `lifecycle` payloads:\n *\n * - `running` → `isLoading = true`\n * - `completed` / `failed` / `interrupted`\n * → `isLoading = false`\n *\n * Non-root, non-lifecycle, and unknown events are ignored.\n *\n * # Why it lives in its own class\n *\n * Lifecycle handling has two subtleties that we want to keep out of\n * the {@link StreamController}'s critical path:\n *\n * 1. **Stale `running` filtering.** SSE replays older events on\n * reconnect — including a `running` lifecycle that fired before\n * the run terminated. Without filtering, that replay would flip\n * `isLoading` back to `true` after a `completed` already brought\n * it down. We track the highest terminal `seq` we've seen and\n * drop any `running` whose `seq` is at or below it.\n * 2. **Deferred terminal flip.** The flip from `true → false` is\n * pushed to the next macrotask (`setTimeout(..., 0)`). This\n * gives synchronous consumers — most notably `for await`\n * iterators in framework bindings — one event-loop tick to\n * observe terminal-related state (e.g. the final assistant\n * message landing in `values`) before `isLoading` settles.\n * The deferred callback re-checks whether a newer `running`\n * arrived in the meantime (HITL interrupt → resume, or SSE\n * history replay of interrupted → running) and bails if so,\n * so a stale terminal reset cannot stomp an active run.\n *\n * # Why it's safe to register the listener as `controller.onEvent`\n *\n * The tracker subscribes to the controller's root event bus via the\n * exported {@link listener} arrow. Because the listener is bound at\n * construction time, removing it later (`bus.delete(tracker.listener)`)\n * works without `bind()` gymnastics in the controller.\n *\n * @typeParam T - The snapshot shape; must contain an `isLoading` flag.\n */\nimport type { Event, LifecycleEvent } from \"@langchain/protocol\";\nimport { StreamStore } from \"./store.js\";\nimport { isRootNamespace } from \"./namespace.js\";\n\n/**\n * Minimal contract the snapshot must satisfy. The tracker only\n * touches `isLoading`, leaving everything else for the controller.\n */\ntype LoadingSnapshot = { readonly isLoading: boolean };\n\n/**\n * Drives root-snapshot `isLoading` from root lifecycle events.\n */\nexport class LifecycleLoadingTracker<T extends LoadingSnapshot> {\n /** Snapshot store whose `isLoading` slot we manage. */\n readonly #store: StreamStore<T>;\n\n /**\n * Disposal probe. Consulted from the deferred `setTimeout` so a\n * controller torn down between scheduling and firing doesn't end\n * up writing to a defunct store.\n */\n readonly #isDisposed: () => boolean;\n\n /**\n * Highest sequence number of a terminal lifecycle we've observed.\n * `running` events at or below this seq are stale replays and\n * are dropped to avoid flipping the loading flag back on after the\n * run has already ended.\n */\n #lastTerminalLifecycleSeq = -1;\n\n /**\n * Highest sequence number of a `running` lifecycle we've observed.\n * Deferred terminal resets compare against this so an\n * `interrupted`/`completed`/`failed` whose macrotask fires after a\n * newer `running` does not incorrectly clear `isLoading`.\n */\n #lastRunningLifecycleSeq = -1;\n\n /**\n * @param params.store - Store whose `isLoading` slot we drive.\n * @param params.isDisposed - Disposal probe consulted from deferred callbacks.\n */\n constructor(params: { store: StreamStore<T>; isDisposed: () => boolean }) {\n this.#store = params.store;\n this.#isDisposed = params.isDisposed;\n }\n\n /**\n * Bound listener suitable for `EventBus.subscribe`. Re-exposed as a\n * stable property so the controller can later remove the same\n * function reference from the bus on teardown.\n */\n readonly listener = (event: Event): void => {\n this.handle(event);\n };\n\n /**\n * Reset internal state when rebinding to a new thread.\n *\n * The seq guards are per-thread: a new thread's lifecycle events\n * are not stale relative to the old thread's.\n */\n reset(): void {\n this.#lastTerminalLifecycleSeq = -1;\n this.#lastRunningLifecycleSeq = -1;\n }\n\n /**\n * Process a single protocol event.\n *\n * Filters down to root-namespace lifecycle events, then mutates the\n * store's `isLoading` slot. All other events are ignored.\n *\n * @param event - Any protocol event from the controller's root bus.\n */\n handle(event: Event): void {\n if (event.method !== \"lifecycle\") return;\n if (!isRootNamespace(event.params.namespace)) return;\n const lifecycle = (event as LifecycleEvent).params.data as {\n event?: string;\n };\n const seq = typeof event.seq === \"number\" ? event.seq : undefined;\n if (lifecycle?.event === \"running\") {\n // Drop stale `running` replays that arrive *after* a terminal\n // for the same run. SSE re-streams history on reconnect; without\n // this filter the loading flag would oscillate.\n if (seq != null && seq <= this.#lastTerminalLifecycleSeq) {\n return;\n }\n if (seq != null) {\n this.#lastRunningLifecycleSeq = Math.max(\n this.#lastRunningLifecycleSeq,\n seq\n );\n }\n this.#store.setState((s) =>\n s.isLoading ? s : { ...s, isLoading: true }\n );\n return;\n }\n if (\n lifecycle?.event === \"completed\" ||\n lifecycle?.event === \"failed\" ||\n lifecycle?.event === \"interrupted\"\n ) {\n if (seq != null) {\n this.#lastTerminalLifecycleSeq = Math.max(\n this.#lastTerminalLifecycleSeq,\n seq\n );\n }\n // Flip `isLoading=false` on the next macrotask so synchronous\n // consumers iterating events get one tick to observe terminal\n // state (the final values snapshot etc.) before the loading\n // indicator drops. Bail if a newer `running` arrived since this\n // terminal was scheduled (answered HITL interrupt, back-to-back\n // runs, or history replay of interrupted → running).\n setTimeout(() => {\n if (this.#isDisposed()) return;\n if (seq != null && this.#lastRunningLifecycleSeq > seq) return;\n this.#store.setState((s) =>\n s.isLoading ? { ...s, isLoading: false } : s\n );\n }, 0);\n }\n }\n}\n"],"mappings":";;;;;AA4DA,IAAa,0BAAb,MAAgE;;CAE9D;;;;;;CAOA;;;;;;;CAQA,4BAA4B;;;;;;;CAQ5B,2BAA2B;;;;;CAM3B,YAAY,QAA8D;AACxE,QAAA,QAAc,OAAO;AACrB,QAAA,aAAmB,OAAO;;;;;;;CAQ5B,YAAqB,UAAuB;AAC1C,OAAK,OAAO,MAAM;;;;;;;;CASpB,QAAc;AACZ,QAAA,2BAAiC;AACjC,QAAA,0BAAgC;;;;;;;;;;CAWlC,OAAO,OAAoB;AACzB,MAAI,MAAM,WAAW,YAAa;AAClC,MAAI,CAACI,kBAAAA,gBAAgB,MAAM,OAAO,UAAU,CAAE;EAC9C,MAAM,YAAa,MAAyB,OAAO;EAGnD,MAAM,MAAM,OAAO,MAAM,QAAQ,WAAW,MAAM,MAAM,KAAA;AACxD,MAAI,WAAW,UAAU,WAAW;AAIlC,OAAI,OAAO,QAAQ,OAAO,MAAA,yBACxB;AAEF,OAAI,OAAO,KACT,OAAA,0BAAgC,KAAK,IACnC,MAAA,yBACA,IACD;AAEH,SAAA,MAAY,UAAU,MACpB,EAAE,YAAY,IAAI;IAAE,GAAG;IAAG,WAAW;IAAM,CAC5C;AACD;;AAEF,MACE,WAAW,UAAU,eACrB,WAAW,UAAU,YACrB,WAAW,UAAU,eACrB;AACA,OAAI,OAAO,KACT,OAAA,2BAAiC,KAAK,IACpC,MAAA,0BACA,IACD;AAQH,oBAAiB;AACf,QAAI,MAAA,YAAkB,CAAE;AACxB,QAAI,OAAO,QAAQ,MAAA,0BAAgC,IAAK;AACxD,UAAA,MAAY,UAAU,MACpB,EAAE,YAAY;KAAE,GAAG;KAAG,WAAW;KAAO,GAAG,EAC5C;MACA,EAAE"}
1
+ {"version":3,"file":"lifecycle-loading-tracker.cjs","names":["#store","#isDisposed","#onSettled","#lastTerminalLifecycleSeq","#lastRunningLifecycleSeq","isRootNamespace"],"sources":["../../src/stream/lifecycle-loading-tracker.ts"],"sourcesContent":["/**\n * Drives the {@link RootSnapshot.isLoading} flag from root lifecycle\n * events.\n *\n * # What it does\n *\n * The tracker watches a stream of protocol events and flips the\n * `isLoading` slot of a {@link StreamStore} based on root-namespace\n * `lifecycle` payloads:\n *\n * - `running` → `isLoading = true`\n * - `completed` / `failed` / `interrupted`\n * → `isLoading = false`\n *\n * Non-root, non-lifecycle, and unknown events are ignored.\n *\n * # Why it lives in its own class\n *\n * Lifecycle handling has two subtleties that we want to keep out of\n * the {@link StreamController}'s critical path:\n *\n * 1. **Stale `running` filtering.** SSE replays older events on\n * reconnect — including a `running` lifecycle that fired before\n * the run terminated. Without filtering, that replay would flip\n * `isLoading` back to `true` after a `completed` already brought\n * it down. We track the highest terminal `seq` we've seen and\n * drop any `running` whose `seq` is at or below it.\n * 2. **Deferred terminal flip.** The flip from `true → false` is\n * pushed to the next macrotask (`setTimeout(..., 0)`). This\n * gives synchronous consumers — most notably `for await`\n * iterators in framework bindings — one event-loop tick to\n * observe terminal-related state (e.g. the final assistant\n * message landing in `values`) before `isLoading` settles.\n * The deferred callback re-checks whether a newer `running`\n * arrived in the meantime (HITL interrupt → resume, or SSE\n * history replay of interrupted → running) and bails if so,\n * so a stale terminal reset cannot stomp an active run.\n *\n * # Why it's safe to register the listener as `controller.onEvent`\n *\n * The tracker subscribes to the controller's root event bus via the\n * exported {@link listener} arrow. Because the listener is bound at\n * construction time, removing it later (`bus.delete(tracker.listener)`)\n * works without `bind()` gymnastics in the controller.\n *\n * @typeParam T - The snapshot shape; must contain an `isLoading` flag.\n */\nimport type { Event, LifecycleEvent } from \"@langchain/protocol\";\nimport { StreamStore } from \"./store.js\";\nimport { isRootNamespace } from \"./namespace.js\";\n\n/**\n * Minimal contract the snapshot must satisfy. The tracker only\n * touches `isLoading`, leaving everything else for the controller.\n */\ntype LoadingSnapshot = { readonly isLoading: boolean };\n\n/**\n * Drives root-snapshot `isLoading` from root lifecycle events.\n */\nexport class LifecycleLoadingTracker<T extends LoadingSnapshot> {\n /** Snapshot store whose `isLoading` slot we manage. */\n readonly #store: StreamStore<T>;\n\n /**\n * Disposal probe. Consulted from the deferred `setTimeout` so a\n * controller torn down between scheduling and firing doesn't end\n * up writing to a defunct store.\n */\n readonly #isDisposed: () => boolean;\n\n /**\n * Highest sequence number of a terminal lifecycle we've observed.\n * `running` events at or below this seq are stale replays and\n * are dropped to avoid flipping the loading flag back on after the\n * run has already ended.\n */\n #lastTerminalLifecycleSeq = -1;\n\n /**\n * Highest sequence number of a `running` lifecycle we've observed.\n * Deferred terminal resets compare against this so an\n * `interrupted`/`completed`/`failed` whose macrotask fires after a\n * newer `running` does not incorrectly clear `isLoading`.\n */\n #lastRunningLifecycleSeq = -1;\n\n /** Notified once `isLoading` actually settles to `false` on a terminal event. */\n readonly #onSettled: (() => void) | undefined;\n\n /**\n * @param params.store - Store whose `isLoading` slot we drive.\n * @param params.isDisposed - Disposal probe consulted from deferred callbacks.\n * @param params.onSettled - Called once `isLoading` flips to `false`,\n * including for a run this client only observed and never\n * dispatched itself.\n */\n constructor(params: {\n store: StreamStore<T>;\n isDisposed: () => boolean;\n onSettled?: () => void;\n }) {\n this.#store = params.store;\n this.#isDisposed = params.isDisposed;\n this.#onSettled = params.onSettled;\n }\n\n /**\n * Bound listener suitable for `EventBus.subscribe`. Re-exposed as a\n * stable property so the controller can later remove the same\n * function reference from the bus on teardown.\n */\n readonly listener = (event: Event): void => {\n this.handle(event);\n };\n\n /**\n * Reset internal state when rebinding to a new thread.\n *\n * The seq guards are per-thread: a new thread's lifecycle events\n * are not stale relative to the old thread's.\n */\n reset(): void {\n this.#lastTerminalLifecycleSeq = -1;\n this.#lastRunningLifecycleSeq = -1;\n }\n\n /**\n * Process a single protocol event.\n *\n * Filters down to root-namespace lifecycle events, then mutates the\n * store's `isLoading` slot. All other events are ignored.\n *\n * @param event - Any protocol event from the controller's root bus.\n */\n handle(event: Event): void {\n if (event.method !== \"lifecycle\") return;\n if (!isRootNamespace(event.params.namespace)) return;\n const lifecycle = (event as LifecycleEvent).params.data as {\n event?: string;\n };\n const seq = typeof event.seq === \"number\" ? event.seq : undefined;\n if (lifecycle?.event === \"running\") {\n // Drop stale `running` replays that arrive *after* a terminal\n // for the same run. SSE re-streams history on reconnect; without\n // this filter the loading flag would oscillate.\n if (seq != null && seq <= this.#lastTerminalLifecycleSeq) {\n return;\n }\n if (seq != null) {\n this.#lastRunningLifecycleSeq = Math.max(\n this.#lastRunningLifecycleSeq,\n seq\n );\n }\n this.#store.setState((s) =>\n s.isLoading ? s : { ...s, isLoading: true }\n );\n return;\n }\n if (\n lifecycle?.event === \"completed\" ||\n lifecycle?.event === \"failed\" ||\n lifecycle?.event === \"interrupted\"\n ) {\n if (seq != null) {\n this.#lastTerminalLifecycleSeq = Math.max(\n this.#lastTerminalLifecycleSeq,\n seq\n );\n }\n // Flip `isLoading=false` on the next macrotask so synchronous\n // consumers iterating events get one tick to observe terminal\n // state (the final values snapshot etc.) before the loading\n // indicator drops. Bail if a newer `running` arrived since this\n // terminal was scheduled (answered HITL interrupt, back-to-back\n // runs, or history replay of interrupted → running).\n setTimeout(() => {\n if (this.#isDisposed()) return;\n if (seq != null && this.#lastRunningLifecycleSeq > seq) return;\n const wasLoading = this.#store.getSnapshot().isLoading;\n this.#store.setState((s) =>\n s.isLoading ? { ...s, isLoading: false } : s\n );\n if (wasLoading) this.#onSettled?.();\n }, 0);\n }\n }\n}\n"],"mappings":";;;;;AA4DA,IAAa,0BAAb,MAAgE;;CAE9D;;;;;;CAOA;;;;;;;CAQA,4BAA4B;;;;;;;CAQ5B,2BAA2B;;CAG3B;;;;;;;;CASA,YAAY,QAIT;AACD,QAAA,QAAc,OAAO;AACrB,QAAA,aAAmB,OAAO;AAC1B,QAAA,YAAkB,OAAO;;;;;;;CAQ3B,YAAqB,UAAuB;AAC1C,OAAK,OAAO,MAAM;;;;;;;;CASpB,QAAc;AACZ,QAAA,2BAAiC;AACjC,QAAA,0BAAgC;;;;;;;;;;CAWlC,OAAO,OAAoB;AACzB,MAAI,MAAM,WAAW,YAAa;AAClC,MAAI,CAACK,kBAAAA,gBAAgB,MAAM,OAAO,UAAU,CAAE;EAC9C,MAAM,YAAa,MAAyB,OAAO;EAGnD,MAAM,MAAM,OAAO,MAAM,QAAQ,WAAW,MAAM,MAAM,KAAA;AACxD,MAAI,WAAW,UAAU,WAAW;AAIlC,OAAI,OAAO,QAAQ,OAAO,MAAA,yBACxB;AAEF,OAAI,OAAO,KACT,OAAA,0BAAgC,KAAK,IACnC,MAAA,yBACA,IACD;AAEH,SAAA,MAAY,UAAU,MACpB,EAAE,YAAY,IAAI;IAAE,GAAG;IAAG,WAAW;IAAM,CAC5C;AACD;;AAEF,MACE,WAAW,UAAU,eACrB,WAAW,UAAU,YACrB,WAAW,UAAU,eACrB;AACA,OAAI,OAAO,KACT,OAAA,2BAAiC,KAAK,IACpC,MAAA,0BACA,IACD;AAQH,oBAAiB;AACf,QAAI,MAAA,YAAkB,CAAE;AACxB,QAAI,OAAO,QAAQ,MAAA,0BAAgC,IAAK;IACxD,MAAM,aAAa,MAAA,MAAY,aAAa,CAAC;AAC7C,UAAA,MAAY,UAAU,MACpB,EAAE,YAAY;KAAE,GAAG;KAAG,WAAW;KAAO,GAAG,EAC5C;AACD,QAAI,WAAY,OAAA,aAAmB;MAClC,EAAE"}
@@ -26,13 +26,19 @@ var LifecycleLoadingTracker = class {
26
26
  * newer `running` does not incorrectly clear `isLoading`.
27
27
  */
28
28
  #lastRunningLifecycleSeq = -1;
29
+ /** Notified once `isLoading` actually settles to `false` on a terminal event. */
30
+ #onSettled;
29
31
  /**
30
32
  * @param params.store - Store whose `isLoading` slot we drive.
31
33
  * @param params.isDisposed - Disposal probe consulted from deferred callbacks.
34
+ * @param params.onSettled - Called once `isLoading` flips to `false`,
35
+ * including for a run this client only observed and never
36
+ * dispatched itself.
32
37
  */
33
38
  constructor(params) {
34
39
  this.#store = params.store;
35
40
  this.#isDisposed = params.isDisposed;
41
+ this.#onSettled = params.onSettled;
36
42
  }
37
43
  /**
38
44
  * Bound listener suitable for `EventBus.subscribe`. Re-exposed as a
@@ -79,10 +85,12 @@ var LifecycleLoadingTracker = class {
79
85
  setTimeout(() => {
80
86
  if (this.#isDisposed()) return;
81
87
  if (seq != null && this.#lastRunningLifecycleSeq > seq) return;
88
+ const wasLoading = this.#store.getSnapshot().isLoading;
82
89
  this.#store.setState((s) => s.isLoading ? {
83
90
  ...s,
84
91
  isLoading: false
85
92
  } : s);
93
+ if (wasLoading) this.#onSettled?.();
86
94
  }, 0);
87
95
  }
88
96
  }
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle-loading-tracker.js","names":["#store","#isDisposed","#lastTerminalLifecycleSeq","#lastRunningLifecycleSeq"],"sources":["../../src/stream/lifecycle-loading-tracker.ts"],"sourcesContent":["/**\n * Drives the {@link RootSnapshot.isLoading} flag from root lifecycle\n * events.\n *\n * # What it does\n *\n * The tracker watches a stream of protocol events and flips the\n * `isLoading` slot of a {@link StreamStore} based on root-namespace\n * `lifecycle` payloads:\n *\n * - `running` → `isLoading = true`\n * - `completed` / `failed` / `interrupted`\n * → `isLoading = false`\n *\n * Non-root, non-lifecycle, and unknown events are ignored.\n *\n * # Why it lives in its own class\n *\n * Lifecycle handling has two subtleties that we want to keep out of\n * the {@link StreamController}'s critical path:\n *\n * 1. **Stale `running` filtering.** SSE replays older events on\n * reconnect — including a `running` lifecycle that fired before\n * the run terminated. Without filtering, that replay would flip\n * `isLoading` back to `true` after a `completed` already brought\n * it down. We track the highest terminal `seq` we've seen and\n * drop any `running` whose `seq` is at or below it.\n * 2. **Deferred terminal flip.** The flip from `true → false` is\n * pushed to the next macrotask (`setTimeout(..., 0)`). This\n * gives synchronous consumers — most notably `for await`\n * iterators in framework bindings — one event-loop tick to\n * observe terminal-related state (e.g. the final assistant\n * message landing in `values`) before `isLoading` settles.\n * The deferred callback re-checks whether a newer `running`\n * arrived in the meantime (HITL interrupt → resume, or SSE\n * history replay of interrupted → running) and bails if so,\n * so a stale terminal reset cannot stomp an active run.\n *\n * # Why it's safe to register the listener as `controller.onEvent`\n *\n * The tracker subscribes to the controller's root event bus via the\n * exported {@link listener} arrow. Because the listener is bound at\n * construction time, removing it later (`bus.delete(tracker.listener)`)\n * works without `bind()` gymnastics in the controller.\n *\n * @typeParam T - The snapshot shape; must contain an `isLoading` flag.\n */\nimport type { Event, LifecycleEvent } from \"@langchain/protocol\";\nimport { StreamStore } from \"./store.js\";\nimport { isRootNamespace } from \"./namespace.js\";\n\n/**\n * Minimal contract the snapshot must satisfy. The tracker only\n * touches `isLoading`, leaving everything else for the controller.\n */\ntype LoadingSnapshot = { readonly isLoading: boolean };\n\n/**\n * Drives root-snapshot `isLoading` from root lifecycle events.\n */\nexport class LifecycleLoadingTracker<T extends LoadingSnapshot> {\n /** Snapshot store whose `isLoading` slot we manage. */\n readonly #store: StreamStore<T>;\n\n /**\n * Disposal probe. Consulted from the deferred `setTimeout` so a\n * controller torn down between scheduling and firing doesn't end\n * up writing to a defunct store.\n */\n readonly #isDisposed: () => boolean;\n\n /**\n * Highest sequence number of a terminal lifecycle we've observed.\n * `running` events at or below this seq are stale replays and\n * are dropped to avoid flipping the loading flag back on after the\n * run has already ended.\n */\n #lastTerminalLifecycleSeq = -1;\n\n /**\n * Highest sequence number of a `running` lifecycle we've observed.\n * Deferred terminal resets compare against this so an\n * `interrupted`/`completed`/`failed` whose macrotask fires after a\n * newer `running` does not incorrectly clear `isLoading`.\n */\n #lastRunningLifecycleSeq = -1;\n\n /**\n * @param params.store - Store whose `isLoading` slot we drive.\n * @param params.isDisposed - Disposal probe consulted from deferred callbacks.\n */\n constructor(params: { store: StreamStore<T>; isDisposed: () => boolean }) {\n this.#store = params.store;\n this.#isDisposed = params.isDisposed;\n }\n\n /**\n * Bound listener suitable for `EventBus.subscribe`. Re-exposed as a\n * stable property so the controller can later remove the same\n * function reference from the bus on teardown.\n */\n readonly listener = (event: Event): void => {\n this.handle(event);\n };\n\n /**\n * Reset internal state when rebinding to a new thread.\n *\n * The seq guards are per-thread: a new thread's lifecycle events\n * are not stale relative to the old thread's.\n */\n reset(): void {\n this.#lastTerminalLifecycleSeq = -1;\n this.#lastRunningLifecycleSeq = -1;\n }\n\n /**\n * Process a single protocol event.\n *\n * Filters down to root-namespace lifecycle events, then mutates the\n * store's `isLoading` slot. All other events are ignored.\n *\n * @param event - Any protocol event from the controller's root bus.\n */\n handle(event: Event): void {\n if (event.method !== \"lifecycle\") return;\n if (!isRootNamespace(event.params.namespace)) return;\n const lifecycle = (event as LifecycleEvent).params.data as {\n event?: string;\n };\n const seq = typeof event.seq === \"number\" ? event.seq : undefined;\n if (lifecycle?.event === \"running\") {\n // Drop stale `running` replays that arrive *after* a terminal\n // for the same run. SSE re-streams history on reconnect; without\n // this filter the loading flag would oscillate.\n if (seq != null && seq <= this.#lastTerminalLifecycleSeq) {\n return;\n }\n if (seq != null) {\n this.#lastRunningLifecycleSeq = Math.max(\n this.#lastRunningLifecycleSeq,\n seq\n );\n }\n this.#store.setState((s) =>\n s.isLoading ? s : { ...s, isLoading: true }\n );\n return;\n }\n if (\n lifecycle?.event === \"completed\" ||\n lifecycle?.event === \"failed\" ||\n lifecycle?.event === \"interrupted\"\n ) {\n if (seq != null) {\n this.#lastTerminalLifecycleSeq = Math.max(\n this.#lastTerminalLifecycleSeq,\n seq\n );\n }\n // Flip `isLoading=false` on the next macrotask so synchronous\n // consumers iterating events get one tick to observe terminal\n // state (the final values snapshot etc.) before the loading\n // indicator drops. Bail if a newer `running` arrived since this\n // terminal was scheduled (answered HITL interrupt, back-to-back\n // runs, or history replay of interrupted → running).\n setTimeout(() => {\n if (this.#isDisposed()) return;\n if (seq != null && this.#lastRunningLifecycleSeq > seq) return;\n this.#store.setState((s) =>\n s.isLoading ? { ...s, isLoading: false } : s\n );\n }, 0);\n }\n }\n}\n"],"mappings":";;;;;AA4DA,IAAa,0BAAb,MAAgE;;CAE9D;;;;;;CAOA;;;;;;;CAQA,4BAA4B;;;;;;;CAQ5B,2BAA2B;;;;;CAM3B,YAAY,QAA8D;AACxE,QAAA,QAAc,OAAO;AACrB,QAAA,aAAmB,OAAO;;;;;;;CAQ5B,YAAqB,UAAuB;AAC1C,OAAK,OAAO,MAAM;;;;;;;;CASpB,QAAc;AACZ,QAAA,2BAAiC;AACjC,QAAA,0BAAgC;;;;;;;;;;CAWlC,OAAO,OAAoB;AACzB,MAAI,MAAM,WAAW,YAAa;AAClC,MAAI,CAAC,gBAAgB,MAAM,OAAO,UAAU,CAAE;EAC9C,MAAM,YAAa,MAAyB,OAAO;EAGnD,MAAM,MAAM,OAAO,MAAM,QAAQ,WAAW,MAAM,MAAM,KAAA;AACxD,MAAI,WAAW,UAAU,WAAW;AAIlC,OAAI,OAAO,QAAQ,OAAO,MAAA,yBACxB;AAEF,OAAI,OAAO,KACT,OAAA,0BAAgC,KAAK,IACnC,MAAA,yBACA,IACD;AAEH,SAAA,MAAY,UAAU,MACpB,EAAE,YAAY,IAAI;IAAE,GAAG;IAAG,WAAW;IAAM,CAC5C;AACD;;AAEF,MACE,WAAW,UAAU,eACrB,WAAW,UAAU,YACrB,WAAW,UAAU,eACrB;AACA,OAAI,OAAO,KACT,OAAA,2BAAiC,KAAK,IACpC,MAAA,0BACA,IACD;AAQH,oBAAiB;AACf,QAAI,MAAA,YAAkB,CAAE;AACxB,QAAI,OAAO,QAAQ,MAAA,0BAAgC,IAAK;AACxD,UAAA,MAAY,UAAU,MACpB,EAAE,YAAY;KAAE,GAAG;KAAG,WAAW;KAAO,GAAG,EAC5C;MACA,EAAE"}
1
+ {"version":3,"file":"lifecycle-loading-tracker.js","names":["#store","#isDisposed","#onSettled","#lastTerminalLifecycleSeq","#lastRunningLifecycleSeq"],"sources":["../../src/stream/lifecycle-loading-tracker.ts"],"sourcesContent":["/**\n * Drives the {@link RootSnapshot.isLoading} flag from root lifecycle\n * events.\n *\n * # What it does\n *\n * The tracker watches a stream of protocol events and flips the\n * `isLoading` slot of a {@link StreamStore} based on root-namespace\n * `lifecycle` payloads:\n *\n * - `running` → `isLoading = true`\n * - `completed` / `failed` / `interrupted`\n * → `isLoading = false`\n *\n * Non-root, non-lifecycle, and unknown events are ignored.\n *\n * # Why it lives in its own class\n *\n * Lifecycle handling has two subtleties that we want to keep out of\n * the {@link StreamController}'s critical path:\n *\n * 1. **Stale `running` filtering.** SSE replays older events on\n * reconnect — including a `running` lifecycle that fired before\n * the run terminated. Without filtering, that replay would flip\n * `isLoading` back to `true` after a `completed` already brought\n * it down. We track the highest terminal `seq` we've seen and\n * drop any `running` whose `seq` is at or below it.\n * 2. **Deferred terminal flip.** The flip from `true → false` is\n * pushed to the next macrotask (`setTimeout(..., 0)`). This\n * gives synchronous consumers — most notably `for await`\n * iterators in framework bindings — one event-loop tick to\n * observe terminal-related state (e.g. the final assistant\n * message landing in `values`) before `isLoading` settles.\n * The deferred callback re-checks whether a newer `running`\n * arrived in the meantime (HITL interrupt → resume, or SSE\n * history replay of interrupted → running) and bails if so,\n * so a stale terminal reset cannot stomp an active run.\n *\n * # Why it's safe to register the listener as `controller.onEvent`\n *\n * The tracker subscribes to the controller's root event bus via the\n * exported {@link listener} arrow. Because the listener is bound at\n * construction time, removing it later (`bus.delete(tracker.listener)`)\n * works without `bind()` gymnastics in the controller.\n *\n * @typeParam T - The snapshot shape; must contain an `isLoading` flag.\n */\nimport type { Event, LifecycleEvent } from \"@langchain/protocol\";\nimport { StreamStore } from \"./store.js\";\nimport { isRootNamespace } from \"./namespace.js\";\n\n/**\n * Minimal contract the snapshot must satisfy. The tracker only\n * touches `isLoading`, leaving everything else for the controller.\n */\ntype LoadingSnapshot = { readonly isLoading: boolean };\n\n/**\n * Drives root-snapshot `isLoading` from root lifecycle events.\n */\nexport class LifecycleLoadingTracker<T extends LoadingSnapshot> {\n /** Snapshot store whose `isLoading` slot we manage. */\n readonly #store: StreamStore<T>;\n\n /**\n * Disposal probe. Consulted from the deferred `setTimeout` so a\n * controller torn down between scheduling and firing doesn't end\n * up writing to a defunct store.\n */\n readonly #isDisposed: () => boolean;\n\n /**\n * Highest sequence number of a terminal lifecycle we've observed.\n * `running` events at or below this seq are stale replays and\n * are dropped to avoid flipping the loading flag back on after the\n * run has already ended.\n */\n #lastTerminalLifecycleSeq = -1;\n\n /**\n * Highest sequence number of a `running` lifecycle we've observed.\n * Deferred terminal resets compare against this so an\n * `interrupted`/`completed`/`failed` whose macrotask fires after a\n * newer `running` does not incorrectly clear `isLoading`.\n */\n #lastRunningLifecycleSeq = -1;\n\n /** Notified once `isLoading` actually settles to `false` on a terminal event. */\n readonly #onSettled: (() => void) | undefined;\n\n /**\n * @param params.store - Store whose `isLoading` slot we drive.\n * @param params.isDisposed - Disposal probe consulted from deferred callbacks.\n * @param params.onSettled - Called once `isLoading` flips to `false`,\n * including for a run this client only observed and never\n * dispatched itself.\n */\n constructor(params: {\n store: StreamStore<T>;\n isDisposed: () => boolean;\n onSettled?: () => void;\n }) {\n this.#store = params.store;\n this.#isDisposed = params.isDisposed;\n this.#onSettled = params.onSettled;\n }\n\n /**\n * Bound listener suitable for `EventBus.subscribe`. Re-exposed as a\n * stable property so the controller can later remove the same\n * function reference from the bus on teardown.\n */\n readonly listener = (event: Event): void => {\n this.handle(event);\n };\n\n /**\n * Reset internal state when rebinding to a new thread.\n *\n * The seq guards are per-thread: a new thread's lifecycle events\n * are not stale relative to the old thread's.\n */\n reset(): void {\n this.#lastTerminalLifecycleSeq = -1;\n this.#lastRunningLifecycleSeq = -1;\n }\n\n /**\n * Process a single protocol event.\n *\n * Filters down to root-namespace lifecycle events, then mutates the\n * store's `isLoading` slot. All other events are ignored.\n *\n * @param event - Any protocol event from the controller's root bus.\n */\n handle(event: Event): void {\n if (event.method !== \"lifecycle\") return;\n if (!isRootNamespace(event.params.namespace)) return;\n const lifecycle = (event as LifecycleEvent).params.data as {\n event?: string;\n };\n const seq = typeof event.seq === \"number\" ? event.seq : undefined;\n if (lifecycle?.event === \"running\") {\n // Drop stale `running` replays that arrive *after* a terminal\n // for the same run. SSE re-streams history on reconnect; without\n // this filter the loading flag would oscillate.\n if (seq != null && seq <= this.#lastTerminalLifecycleSeq) {\n return;\n }\n if (seq != null) {\n this.#lastRunningLifecycleSeq = Math.max(\n this.#lastRunningLifecycleSeq,\n seq\n );\n }\n this.#store.setState((s) =>\n s.isLoading ? s : { ...s, isLoading: true }\n );\n return;\n }\n if (\n lifecycle?.event === \"completed\" ||\n lifecycle?.event === \"failed\" ||\n lifecycle?.event === \"interrupted\"\n ) {\n if (seq != null) {\n this.#lastTerminalLifecycleSeq = Math.max(\n this.#lastTerminalLifecycleSeq,\n seq\n );\n }\n // Flip `isLoading=false` on the next macrotask so synchronous\n // consumers iterating events get one tick to observe terminal\n // state (the final values snapshot etc.) before the loading\n // indicator drops. Bail if a newer `running` arrived since this\n // terminal was scheduled (answered HITL interrupt, back-to-back\n // runs, or history replay of interrupted → running).\n setTimeout(() => {\n if (this.#isDisposed()) return;\n if (seq != null && this.#lastRunningLifecycleSeq > seq) return;\n const wasLoading = this.#store.getSnapshot().isLoading;\n this.#store.setState((s) =>\n s.isLoading ? { ...s, isLoading: false } : s\n );\n if (wasLoading) this.#onSettled?.();\n }, 0);\n }\n }\n}\n"],"mappings":";;;;;AA4DA,IAAa,0BAAb,MAAgE;;CAE9D;;;;;;CAOA;;;;;;;CAQA,4BAA4B;;;;;;;CAQ5B,2BAA2B;;CAG3B;;;;;;;;CASA,YAAY,QAIT;AACD,QAAA,QAAc,OAAO;AACrB,QAAA,aAAmB,OAAO;AAC1B,QAAA,YAAkB,OAAO;;;;;;;CAQ3B,YAAqB,UAAuB;AAC1C,OAAK,OAAO,MAAM;;;;;;;;CASpB,QAAc;AACZ,QAAA,2BAAiC;AACjC,QAAA,0BAAgC;;;;;;;;;;CAWlC,OAAO,OAAoB;AACzB,MAAI,MAAM,WAAW,YAAa;AAClC,MAAI,CAAC,gBAAgB,MAAM,OAAO,UAAU,CAAE;EAC9C,MAAM,YAAa,MAAyB,OAAO;EAGnD,MAAM,MAAM,OAAO,MAAM,QAAQ,WAAW,MAAM,MAAM,KAAA;AACxD,MAAI,WAAW,UAAU,WAAW;AAIlC,OAAI,OAAO,QAAQ,OAAO,MAAA,yBACxB;AAEF,OAAI,OAAO,KACT,OAAA,0BAAgC,KAAK,IACnC,MAAA,yBACA,IACD;AAEH,SAAA,MAAY,UAAU,MACpB,EAAE,YAAY,IAAI;IAAE,GAAG;IAAG,WAAW;IAAM,CAC5C;AACD;;AAEF,MACE,WAAW,UAAU,eACrB,WAAW,UAAU,YACrB,WAAW,UAAU,eACrB;AACA,OAAI,OAAO,KACT,OAAA,2BAAiC,KAAK,IACpC,MAAA,0BACA,IACD;AAQH,oBAAiB;AACf,QAAI,MAAA,YAAkB,CAAE;AACxB,QAAI,OAAO,QAAQ,MAAA,0BAAgC,IAAK;IACxD,MAAM,aAAa,MAAA,MAAY,aAAa,CAAC;AAC7C,UAAA,MAAY,UAAU,MACpB,EAAE,YAAY;KAAE,GAAG;KAAG,WAAW;KAAO,GAAG,EAC5C;AACD,QAAI,WAAY,OAAA,aAAmB;MAClC,EAAE"}
@@ -0,0 +1,204 @@
1
+ require("../_virtual/_rolldown/runtime.cjs");
2
+ const require_dispatch_config = require("./dispatch-config.cjs");
3
+ const require_queue_adapter = require("./queue-adapter.cjs");
4
+ let _langchain_core_utils_uuid = require("@langchain/core/utils/uuid");
5
+ //#region src/stream/queue-adapter-agent-server.ts
6
+ /**
7
+ * Backs "enqueue" with real, durable runs. An entry is a genuine
8
+ * server-side pending run the instant `enqueue()` resolves.
9
+ *
10
+ * Selected when `queue: "server"` is set (see {@link SubmitCoordinator}'s
11
+ * constructor).
12
+ */
13
+ var AgentServerQueueAdapter = class {
14
+ #runs;
15
+ #assistantId;
16
+ #store;
17
+ #onError;
18
+ #getThread;
19
+ /** One shared subscription for this adapter's lifetime, not one per run. */
20
+ #unsubscribe;
21
+ /** ids canceled while their create() was still in flight. */
22
+ #pendingCancel = /* @__PURE__ */ new Set();
23
+ #threadId;
24
+ /** Guards against a stale hydrate() landing after a rapid thread-switch. */
25
+ #hydrateGeneration = 0;
26
+ /** Aborts in-flight bookkeeping `list()` calls on detach(); replaced (never reused) afterward. */
27
+ #abortController = new AbortController();
28
+ /** Handle for #refreshPending's pending retry, if any; cleared on detach(). */
29
+ #refreshRetryTimer;
30
+ /** Lets enqueue() detect a "started" event during its own create(). */
31
+ #startedEventCount = 0;
32
+ constructor(runs, assistantId, store, onError, getThread) {
33
+ this.#runs = runs;
34
+ this.#assistantId = assistantId;
35
+ this.#store = store;
36
+ this.#onError = onError;
37
+ this.#getThread = getThread;
38
+ }
39
+ async hydrate(threadId) {
40
+ this.#threadId = threadId;
41
+ this.#ensureWatching(threadId);
42
+ const generation = ++this.#hydrateGeneration;
43
+ const knownAtRequestTime = this.#knownRunIds();
44
+ let pending;
45
+ try {
46
+ pending = await this.#runs.list(threadId, {
47
+ status: "pending",
48
+ limit: 1e3,
49
+ select: [
50
+ "run_id",
51
+ "kwargs",
52
+ "created_at",
53
+ "multitask_strategy"
54
+ ],
55
+ signal: this.#abortController.signal
56
+ });
57
+ } catch (error) {
58
+ if (generation === this.#hydrateGeneration) this.#onError(error);
59
+ return;
60
+ }
61
+ if (generation !== this.#hydrateGeneration) return;
62
+ const remoteIds = new Set(pending.map((run) => run.run_id));
63
+ this.#store.setState((current) => {
64
+ const knownRunIds = new Set(current.flatMap((e) => e.runId ? [e.runId] : []));
65
+ const stillValid = current.filter((e) => this.#isStillQueued(e, remoteIds, knownAtRequestTime));
66
+ return [...pending.filter((run) => !knownRunIds.has(run.run_id)).map((run) => this.#toEntry(run)), ...stillValid];
67
+ });
68
+ }
69
+ /**
70
+ * On a self-created thread, this can race the active run's own
71
+ * `run.start` request that creates the thread row: if this
72
+ * `runs.create()` reaches the server first, it may 404. The local
73
+ * adapter has no such race (it never hits the server while queued).
74
+ */
75
+ async enqueue(threadId, values, options) {
76
+ this.#threadId = threadId;
77
+ this.#ensureWatching(threadId);
78
+ const startedEventsBefore = this.#startedEventCount;
79
+ const generationAtEnqueue = this.#hydrateGeneration;
80
+ const id = (0, _langchain_core_utils_uuid.v7)();
81
+ this.#store.setState((current) => [...current, {
82
+ id,
83
+ values,
84
+ options,
85
+ createdAt: /* @__PURE__ */ new Date()
86
+ }]);
87
+ try {
88
+ const run = await this.#runs.create(threadId, this.#assistantId, {
89
+ input: values,
90
+ config: require_dispatch_config.bindThreadConfig(options?.config, threadId),
91
+ metadata: options?.metadata,
92
+ checkpointId: options?.forkFrom,
93
+ multitaskStrategy: "enqueue"
94
+ });
95
+ if (this.#pendingCancel.delete(id)) {
96
+ await this.#runs.cancel(threadId, run.run_id);
97
+ return;
98
+ }
99
+ this.#store.setState((current) => current.map((entry) => entry.id === id ? {
100
+ ...entry,
101
+ runId: run.run_id
102
+ } : entry));
103
+ if (this.#startedEventCount !== startedEventsBefore && this.#hydrateGeneration === generationAtEnqueue) this.#refreshPending(threadId);
104
+ } catch (error) {
105
+ this.#pendingCancel.delete(id);
106
+ this.#store.setState((current) => current.filter((entry) => entry.id !== id));
107
+ this.#onError(error);
108
+ throw error;
109
+ }
110
+ }
111
+ async cancel(id) {
112
+ const entry = this.#store.getSnapshot().find((e) => e.id === id);
113
+ if (!entry) return false;
114
+ this.#store.setState((current) => current.filter((e) => e.id !== id));
115
+ await this.#cancelEntry(entry);
116
+ return true;
117
+ }
118
+ async clear() {
119
+ const entries = this.#store.getSnapshot();
120
+ this.#store.setState(() => require_queue_adapter.EMPTY_QUEUE);
121
+ const failures = (await Promise.allSettled(entries.map((entry) => this.#cancelEntry(entry)))).filter((r) => r.status === "rejected");
122
+ if (failures.length === 1) this.#onError(failures[0].reason);
123
+ else if (failures.length > 1) this.#onError(new AggregateError(failures.map((f) => f.reason), `${failures.length} of ${entries.length} queued runs failed to cancel`));
124
+ }
125
+ detach() {
126
+ this.#hydrateGeneration++;
127
+ this.#unsubscribe?.();
128
+ this.#unsubscribe = void 0;
129
+ this.#threadId = void 0;
130
+ this.#abortController.abort();
131
+ this.#abortController = new AbortController();
132
+ clearTimeout(this.#refreshRetryTimer);
133
+ this.#refreshRetryTimer = void 0;
134
+ }
135
+ async #cancelEntry(entry) {
136
+ if (entry.runId && this.#threadId) await this.#runs.cancel(this.#threadId, entry.runId);
137
+ else this.#pendingCancel.add(entry.id);
138
+ }
139
+ /**
140
+ * Lazily opens ONE subscription for this adapter's lifetime, not one
141
+ * per run. `LifecycleEvent` carries no `run_id`, so a `"running"`
142
+ * event triggers a `runs.list` re-check instead of a direct match.
143
+ */
144
+ #ensureWatching(threadId) {
145
+ if (this.#unsubscribe) return;
146
+ this.#unsubscribe = this.#getThread(threadId).onEvent((event) => {
147
+ if (event.method !== "lifecycle") return;
148
+ if (event.params.data.event !== "running") return;
149
+ this.#startedEventCount++;
150
+ this.#refreshPending(threadId);
151
+ });
152
+ }
153
+ /**
154
+ * Re-lists pending runs and drops any queue entry no longer reported
155
+ * pending: it's running now. Retried once after a network failure
156
+ * before surfacing an error — otherwise a single blip would leave a
157
+ * now-running entry stuck showing "queued" until the next promotion
158
+ * or a manual hydrate.
159
+ */
160
+ async #refreshPending(threadId, attempt = 0) {
161
+ if (this.#store.getSnapshot().length === 0) return;
162
+ const generation = this.#hydrateGeneration;
163
+ const knownAtRequestTime = this.#knownRunIds();
164
+ try {
165
+ const pending = await this.#runs.list(threadId, {
166
+ status: "pending",
167
+ limit: 1e3,
168
+ signal: this.#abortController.signal
169
+ });
170
+ if (generation !== this.#hydrateGeneration) return;
171
+ const stillPendingIds = new Set(pending.map((run) => run.run_id));
172
+ this.#store.setState((current) => current.filter((entry) => this.#isStillQueued(entry, stillPendingIds, knownAtRequestTime)));
173
+ } catch (error) {
174
+ if (generation !== this.#hydrateGeneration) return;
175
+ if (attempt < 1) {
176
+ this.#refreshRetryTimer = setTimeout(() => {
177
+ this.#refreshRetryTimer = void 0;
178
+ this.#refreshPending(threadId, attempt + 1);
179
+ }, 2e3);
180
+ return;
181
+ }
182
+ this.#onError(error);
183
+ }
184
+ }
185
+ #toEntry(run) {
186
+ return {
187
+ id: run.run_id,
188
+ runId: run.run_id,
189
+ values: run.kwargs?.input,
190
+ createdAt: new Date(run.created_at)
191
+ };
192
+ }
193
+ #knownRunIds() {
194
+ return new Set(this.#store.getSnapshot().flatMap((e) => e.runId ? [e.runId] : []));
195
+ }
196
+ /** Shared by hydrate() and #refreshPending(): is a list() response grounds to evict this entry? */
197
+ #isStillQueued(entry, stillPendingIds, knownAtRequestTime) {
198
+ return !entry.runId || stillPendingIds.has(entry.runId) || !knownAtRequestTime.has(entry.runId);
199
+ }
200
+ };
201
+ //#endregion
202
+ exports.AgentServerQueueAdapter = AgentServerQueueAdapter;
203
+
204
+ //# sourceMappingURL=queue-adapter-agent-server.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue-adapter-agent-server.cjs","names":["#runs","#assistantId","#store","#onError","#getThread","#threadId","#ensureWatching","#hydrateGeneration","#knownRunIds","#abortController","#isStillQueued","#toEntry","#startedEventCount","bindThreadConfig","#pendingCancel","#refreshPending","#cancelEntry","EMPTY_QUEUE","#unsubscribe","#refreshRetryTimer"],"sources":["../../src/stream/queue-adapter-agent-server.ts"],"sourcesContent":["import { v7 as uuidv7 } from \"@langchain/core/utils/uuid\";\nimport type { ThreadStream } from \"../client/index.js\";\nimport type { Run } from \"../schema.js\";\nimport { bindThreadConfig } from \"./dispatch-config.js\";\nimport {\n EMPTY_QUEUE,\n type QueueAdapter,\n type ServerQueueCapability,\n type SubmissionQueueEntry,\n type SubmissionQueueSnapshot,\n} from \"./queue-adapter.js\";\nimport { StreamStore } from \"./store.js\";\nimport type { StreamSubmitOptions } from \"./types.js\";\n\n/** `runs.list()`'s `Run` type doesn't declare `kwargs` by default; it's an opt-in `select` field. */\ntype RunWithKwargs = Run & { kwargs?: { input?: unknown } };\n\n/**\n * Backs \"enqueue\" with real, durable runs. An entry is a genuine\n * server-side pending run the instant `enqueue()` resolves.\n *\n * Selected when `queue: \"server\"` is set (see {@link SubmitCoordinator}'s\n * constructor).\n */\nexport class AgentServerQueueAdapter<\n StateType extends object = Record<string, unknown>,\n> implements QueueAdapter<StateType> {\n readonly #runs: ServerQueueCapability;\n readonly #assistantId: string;\n readonly #store: StreamStore<SubmissionQueueSnapshot<StateType>>;\n readonly #onError: (error: unknown) => void;\n readonly #getThread: (threadId: string) => Pick<ThreadStream, \"onEvent\">;\n /** One shared subscription for this adapter's lifetime, not one per run. */\n #unsubscribe: (() => void) | undefined;\n /** ids canceled while their create() was still in flight. */\n #pendingCancel = new Set<string>();\n #threadId: string | undefined;\n /** Guards against a stale hydrate() landing after a rapid thread-switch. */\n #hydrateGeneration = 0;\n /** Aborts in-flight bookkeeping `list()` calls on detach(); replaced (never reused) afterward. */\n #abortController = new AbortController();\n /** Handle for #refreshPending's pending retry, if any; cleared on detach(). */\n #refreshRetryTimer: ReturnType<typeof setTimeout> | undefined;\n /** Lets enqueue() detect a \"started\" event during its own create(). */\n #startedEventCount = 0;\n\n constructor(\n runs: ServerQueueCapability,\n assistantId: string,\n store: StreamStore<SubmissionQueueSnapshot<StateType>>,\n onError: (error: unknown) => void,\n getThread: (threadId: string) => Pick<ThreadStream, \"onEvent\">\n ) {\n this.#runs = runs;\n this.#assistantId = assistantId;\n this.#store = store;\n this.#onError = onError;\n this.#getThread = getThread;\n }\n\n async hydrate(threadId: string): Promise<void> {\n this.#threadId = threadId;\n this.#ensureWatching(threadId);\n const generation = ++this.#hydrateGeneration;\n // Baseline before asking: an entry assigned after this can't be in the response below.\n const knownAtRequestTime = this.#knownRunIds();\n let pending: RunWithKwargs[];\n try {\n pending = (await this.#runs.list(threadId, {\n status: \"pending\",\n // `list()` defaults to 10; a real cap would drop older entries.\n limit: 1000,\n // \"kwargs\" isn't part of the base `Run` shape returned by list();\n // it must be explicitly selected to get the original input back.\n select: [\"run_id\", \"kwargs\", \"created_at\", \"multitask_strategy\"],\n signal: this.#abortController.signal,\n })) as RunWithKwargs[];\n } catch (error) {\n // A generation mismatch means detach() aborted this on purpose.\n if (generation === this.#hydrateGeneration) this.#onError(error);\n return;\n }\n if (generation !== this.#hydrateGeneration) return; // superseded by a later hydrate()/detach()\n\n const remoteIds = new Set(pending.map((run) => run.run_id));\n this.#store.setState((current) => {\n const knownRunIds = new Set(\n current.flatMap((e) => (e.runId ? [e.runId] : []))\n );\n // Local entries are never overwritten by a server reconstruction\n // — a consumer may already be keying UI on their id.\n const stillValid = current.filter((e) =>\n this.#isStillQueued(e, remoteIds, knownAtRequestTime)\n );\n const newFromRemote = pending\n .filter((run) => !knownRunIds.has(run.run_id))\n .map((run) => this.#toEntry(run));\n return [\n ...newFromRemote,\n ...stillValid,\n ] as SubmissionQueueSnapshot<StateType>;\n });\n }\n\n /**\n * On a self-created thread, this can race the active run's own\n * `run.start` request that creates the thread row: if this\n * `runs.create()` reaches the server first, it may 404. The local\n * adapter has no such race (it never hits the server while queued).\n */\n async enqueue(\n threadId: string,\n values: Partial<StateType> | null | undefined,\n options: StreamSubmitOptions<StateType> | undefined\n ): Promise<void> {\n // A brand-new self-created thread may never be hydrated first.\n this.#threadId = threadId;\n this.#ensureWatching(threadId);\n const startedEventsBefore = this.#startedEventCount;\n const generationAtEnqueue = this.#hydrateGeneration;\n const id = uuidv7();\n this.#store.setState((current) => [\n ...current,\n { id, values, options, createdAt: new Date() },\n ]);\n try {\n const run = await this.#runs.create(threadId, this.#assistantId, {\n input: values,\n config: bindThreadConfig(options?.config, threadId),\n metadata: options?.metadata,\n checkpointId: options?.forkFrom,\n multitaskStrategy: \"enqueue\",\n });\n if (this.#pendingCancel.delete(id)) {\n await this.#runs.cancel(threadId, run.run_id); // canceled while creating; don't resurrect it\n return;\n }\n this.#store.setState((current) =>\n current.map((entry) =>\n entry.id === id ? { ...entry, runId: run.run_id } : entry\n )\n );\n // A run may have started before we learned its id; re-check now.\n // Skip if detach()/hydrate() rebound the adapter to another thread\n // in the meantime — `threadId` here is this call's own thread, not\n // necessarily the one the adapter is currently watching.\n if (\n this.#startedEventCount !== startedEventsBefore &&\n this.#hydrateGeneration === generationAtEnqueue\n ) {\n void this.#refreshPending(threadId);\n }\n } catch (error) {\n this.#pendingCancel.delete(id); // avoid leaking a pending-cancel marker nothing will ever consult\n this.#store.setState((current) =>\n current.filter((entry) => entry.id !== id)\n );\n this.#onError(error);\n throw error;\n }\n }\n\n async cancel(id: string): Promise<boolean> {\n const entry = this.#store.getSnapshot().find((e) => e.id === id);\n if (!entry) return false;\n this.#store.setState((current) => current.filter((e) => e.id !== id));\n await this.#cancelEntry(entry);\n return true;\n }\n\n async clear(): Promise<void> {\n const entries = this.#store.getSnapshot();\n this.#store.setState(\n () => EMPTY_QUEUE as SubmissionQueueSnapshot<StateType>\n );\n // `allSettled`, not `all`: one failed cancel must not stop the rest\n // from being attempted, nor reject `clear()` itself.\n const results = await Promise.allSettled(\n entries.map((entry) => this.#cancelEntry(entry))\n );\n const failures = results.filter(\n (r): r is PromiseRejectedResult => r.status === \"rejected\"\n );\n if (failures.length === 1) {\n this.#onError(failures[0].reason);\n } else if (failures.length > 1) {\n this.#onError(\n new AggregateError(\n failures.map((f) => f.reason),\n `${failures.length} of ${entries.length} queued runs failed to cancel`\n )\n );\n }\n }\n\n detach(): void {\n this.#hydrateGeneration++; // invalidate any in-flight hydrate() for this thread\n this.#unsubscribe?.();\n this.#unsubscribe = undefined;\n // #pendingCancel is not cleared: dropping it would silently un-cancel an in-flight create().\n this.#threadId = undefined;\n this.#abortController.abort(); // stop any in-flight hydrate()/#refreshPending() list() call\n this.#abortController = new AbortController(); // an aborted controller can't be reused\n clearTimeout(this.#refreshRetryTimer); // a scheduled retry outliving detach() would target a stale thread\n this.#refreshRetryTimer = undefined;\n }\n\n async #cancelEntry(entry: SubmissionQueueEntry<StateType>): Promise<void> {\n if (entry.runId && this.#threadId) {\n await this.#runs.cancel(this.#threadId, entry.runId); // already real, idempotent\n } else {\n this.#pendingCancel.add(entry.id); // still being created; cancel on arrival\n }\n }\n\n /**\n * Lazily opens ONE subscription for this adapter's lifetime, not one\n * per run. `LifecycleEvent` carries no `run_id`, so a `\"running\"`\n * event triggers a `runs.list` re-check instead of a direct match.\n */\n #ensureWatching(threadId: string): void {\n if (this.#unsubscribe) return;\n this.#unsubscribe = this.#getThread(threadId).onEvent((event) => {\n if (event.method !== \"lifecycle\") return;\n if (event.params.data.event !== \"running\") return;\n this.#startedEventCount++;\n void this.#refreshPending(threadId);\n });\n }\n\n /**\n * Re-lists pending runs and drops any queue entry no longer reported\n * pending: it's running now. Retried once after a network failure\n * before surfacing an error — otherwise a single blip would leave a\n * now-running entry stuck showing \"queued\" until the next promotion\n * or a manual hydrate.\n */\n async #refreshPending(threadId: string, attempt = 0): Promise<void> {\n if (this.#store.getSnapshot().length === 0) return; // nothing left to refresh\n const generation = this.#hydrateGeneration;\n const knownAtRequestTime = this.#knownRunIds(); // same reasoning as hydrate()\n try {\n const pending = await this.#runs.list(threadId, {\n status: \"pending\",\n limit: 1000, // list() defaults to 10; see the matching note in hydrate()\n signal: this.#abortController.signal,\n });\n if (generation !== this.#hydrateGeneration) return; // superseded; thread id is stale\n const stillPendingIds = new Set(pending.map((run) => run.run_id));\n this.#store.setState((current) =>\n current.filter((entry) =>\n this.#isStillQueued(entry, stillPendingIds, knownAtRequestTime)\n )\n );\n } catch (error) {\n if (generation !== this.#hydrateGeneration) return; // detach() aborted this on purpose\n if (attempt < 1) {\n this.#refreshRetryTimer = setTimeout(() => {\n this.#refreshRetryTimer = undefined;\n void this.#refreshPending(threadId, attempt + 1);\n }, 2000);\n return;\n }\n this.#onError(error);\n }\n }\n\n #toEntry(run: RunWithKwargs): SubmissionQueueEntry<StateType> {\n return {\n id: run.run_id,\n runId: run.run_id,\n values: run.kwargs?.input as Partial<StateType> | null | undefined,\n createdAt: new Date(run.created_at),\n };\n }\n\n #knownRunIds(): Set<string> {\n return new Set(\n this.#store.getSnapshot().flatMap((e) => (e.runId ? [e.runId] : []))\n );\n }\n\n /** Shared by hydrate() and #refreshPending(): is a list() response grounds to evict this entry? */\n #isStillQueued(\n entry: SubmissionQueueEntry<StateType>,\n stillPendingIds: Set<string>,\n knownAtRequestTime: Set<string>\n ): boolean {\n return (\n !entry.runId ||\n stillPendingIds.has(entry.runId) ||\n !knownAtRequestTime.has(entry.runId)\n );\n }\n}\n"],"mappings":"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"}