@langchain/langgraph-sdk 1.9.27 → 1.9.29

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 (37) hide show
  1. package/dist/client/stream/types.d.cts +4 -4
  2. package/dist/client/stream/types.d.ts +4 -4
  3. package/dist/client/threads/index.cjs +3 -0
  4. package/dist/client/threads/index.cjs.map +1 -1
  5. package/dist/client/threads/index.js +3 -0
  6. package/dist/client/threads/index.js.map +1 -1
  7. package/dist/schema.d.cts +7 -0
  8. package/dist/schema.d.cts.map +1 -1
  9. package/dist/schema.d.ts +7 -0
  10. package/dist/schema.d.ts.map +1 -1
  11. package/dist/stream/controller.cjs +87 -57
  12. package/dist/stream/controller.cjs.map +1 -1
  13. package/dist/stream/controller.d.cts +12 -23
  14. package/dist/stream/controller.d.cts.map +1 -1
  15. package/dist/stream/controller.d.ts +12 -23
  16. package/dist/stream/controller.d.ts.map +1 -1
  17. package/dist/stream/controller.js +87 -57
  18. package/dist/stream/controller.js.map +1 -1
  19. package/dist/stream/projections/channel.cjs +4 -6
  20. package/dist/stream/projections/channel.cjs.map +1 -1
  21. package/dist/stream/projections/channel.d.cts.map +1 -1
  22. package/dist/stream/projections/channel.d.ts.map +1 -1
  23. package/dist/stream/projections/channel.js +4 -6
  24. package/dist/stream/projections/channel.js.map +1 -1
  25. package/dist/stream/submit-coordinator.cjs +7 -5
  26. package/dist/stream/submit-coordinator.cjs.map +1 -1
  27. package/dist/stream/submit-coordinator.js +7 -5
  28. package/dist/stream/submit-coordinator.js.map +1 -1
  29. package/dist/stream/types.d.cts +3 -3
  30. package/dist/stream/types.d.ts +3 -3
  31. package/dist/utils/async_caller.cjs +1 -1
  32. package/dist/utils/async_caller.cjs.map +1 -1
  33. package/dist/utils/async_caller.d.cts.map +1 -1
  34. package/dist/utils/async_caller.d.ts.map +1 -1
  35. package/dist/utils/async_caller.js +1 -1
  36. package/dist/utils/async_caller.js.map +1 -1
  37. package/package.json +10 -10
@@ -120,14 +120,15 @@ declare class StreamController<StateType extends object = Record<string, unknown
120
120
  * oldest and picks the first entry whose `interruptId` has not already
121
121
  * been resolved by a prior `respond()` call. That entry may be at the
122
122
  * root (`namespace: []`) or inside a subgraph (non-empty `namespace`).
123
- * This is **not** the same as {@link RootSnapshot.interrupts
124
- * `rootStore.interrupts[0]`} / framework `stream.interrupt`, which only
125
- * mirrors root-namespace interrupts for UI convenience.
123
+ * {@link RootSnapshot.interrupts `rootStore.interrupts`} /
124
+ * framework `stream.interrupts` mirrors the same pending interrupts
125
+ * (including nested namespaces) for UI rendering.
126
126
  *
127
127
  * Omitting `interruptId` is fine when exactly one interrupt is pending.
128
128
  * When several can be active (parallel subagents, fan-out, nested
129
- * graphs), pass an explicit `interruptId` (and `namespace` for subgraph
130
- * interrupts) so you resume the interrupt the user acted on.
129
+ * graphs), pass an explicit `interruptId` so you resume the interrupt
130
+ * the user acted on. `namespace` is resolved automatically from
131
+ * `thread.interrupts` / `stream.interrupts` when omitted.
131
132
  *
132
133
  * To resume several interrupts pending at the same checkpoint in one
133
134
  * command, use {@link respondAll} — sequential single `respond()` calls
@@ -135,9 +136,8 @@ declare class StreamController<StateType extends object = Record<string, unknown
135
136
  * others with no interrupted run to respond to.
136
137
  *
137
138
  * The server validates `namespace` against the pending interrupt. Root
138
- * interrupts use `namespace: []` (the default when `namespace` is
139
- * omitted). Subgraph interrupts require the exact tuple from
140
- * `getThread()?.interrupts` — see the example below.
139
+ * interrupts use `namespace: []`. Subgraph interrupts carry the exact
140
+ * tuple on each {@link Interrupt} / {@link InterruptPayload} entry.
141
141
  *
142
142
  * @param response - Payload sent back to the interrupted namespace.
143
143
  * @param options - Optional target (`interruptId` / `namespace`) and
@@ -158,27 +158,16 @@ declare class StreamController<StateType extends object = Record<string, unknown
158
158
  * );
159
159
  * ```
160
160
  *
161
- * @example Multiple root interrupts — target by id
161
+ * @example Multiple / nested interrupts — target by id
162
162
  * ```tsx
163
163
  * for (const intr of stream.interrupts) {
164
164
  * await stream.respond(decide(intr.value), { interruptId: intr.id! });
165
165
  * }
166
166
  * ```
167
167
  *
168
- * @example Subgraph interrupt — read `namespace` from the thread stream
169
- * ```tsx
170
- * const thread = stream.getThread();
171
- * for (const entry of thread?.interrupts ?? []) {
172
- * await stream.respond(buildResponse(entry.payload), {
173
- * interruptId: entry.interruptId,
174
- * namespace: entry.namespace,
175
- * });
176
- * }
177
- * ```
178
- *
179
- * Each {@link InterruptPayload} on `thread.interrupts` mirrors an
180
- * `input.requested` event: `{ interruptId, payload, namespace }`.
181
- * Nested interrupts may appear here but not on `stream.interrupts`.
168
+ * Each {@link InterruptPayload} on `thread.interrupts` and each
169
+ * {@link Interrupt} on `stream.interrupts` mirrors an
170
+ * `input.requested` event with its protocol `namespace`.
182
171
  */
183
172
  respond(response: unknown, options?: StreamRespondOptions<ConfigurableType>): Promise<void>;
184
173
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"controller.d.cts","names":[],"sources":["../../src/stream/controller.ts"],"mappings":";;;;;;;;;;;;;;AA6KA;;cAAa,kBAAA,WAA6B,OAAA;;AAkC1C;;;;;;;;;;;;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;EAsH0B;;;;;EAA7C,WAAA,CAAY,OAAA,EAAS,uBAAA,CAAwB,SAAA;EA+uBF;;;;;;;EAAA,IAroBvC,gBAAA,CAAA,GAAoB,OAAA;EAqzBS;;;;;;;;EA5vB3B,OAAA,CAAQ,QAAA,mBAA2B,OAAA;EAy+BpB;;;;;;;;;;;;;;EAlqBf,wBAAA,CAAyB,UAAA,WAAqB,OAAA;EArmB5B;;;;;;;;EAw2BlB,MAAA,CACJ,KAAA,WACA,OAAA,GAAU,mBAAA,CAAoB,SAAA,EAAW,gBAAA,IACxC,OAAA;EAx2B4B;;;;;;EAk3BzB,IAAA,CAAK,OAAA,GAAU,iBAAA,GAAoB,OAAA;EAh3BtB;;;;EAo4Bb,UAAA,CAAA,GAAc,OAAA;EApqBhB;;;;;;;;;;;EAouBE,YAAA,CAAa,EAAA,WAAa,OAAA;EA/FW;;;EAsGrC,UAAA,CAAA,GAAc,OAAA;EA3FC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmKf,OAAA,CACJ,QAAA,WACA,OAAA,GAAU,oBAAA,CAAqB,gBAAA,IAC9B,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4GG,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
+ {"version":3,"file":"controller.d.cts","names":[],"sources":["../../src/stream/controller.ts"],"mappings":";;;;;;;;;;;;;;AA6KA;;cAAa,kBAAA,WAA6B,OAAA;;AAkC1C;;;;;;;;;;;;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;EAgI0B;;;;;EAA7C,WAAA,CAAY,OAAA,EAAS,uBAAA,CAAwB,SAAA;EAsyBF;;;;;;;EAAA,IAvrBvC,gBAAA,CAAA,GAAoB,OAAA;EA41BS;;;;;;;;EAnyB3B,OAAA,CAAQ,QAAA,mBAA2B,OAAA;EAihCpB;;;;;;;;;;;;;;EAzpBf,wBAAA,CAAyB,UAAA,WAAqB,OAAA;EArqB5B;;;;;;;;EAy6BlB,MAAA,CACJ,KAAA,WACA,OAAA,GAAU,mBAAA,CAAoB,SAAA,EAAW,gBAAA,IACxC,OAAA;EAz6B4B;;;;;;EAm7BzB,IAAA,CAAK,OAAA,GAAU,iBAAA,GAAoB,OAAA;EAj7BtB;;;;EAq8Bb,UAAA,CAAA,GAAc,OAAA;EAttBhB;;;;;;;;;;;EAsxBE,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+GG,UAAA,CACJ,aAAA,EAAe,MAAA,mBACf,OAAA,GAAU,uBAAA,CAAwB,gBAAA,IACjC,OAAA;;;;EA0DG,OAAA,CAAA,GAAW,OAAA;;;;;;;;;;;;;;;;EAyBjB,QAAA,CAAA;;;;;;EA4BA,SAAA,CAAA,GAAa,YAAA;;;;;;;;EAWb,eAAA,CACE,QAAA,GAAW,MAAA,EAAQ,YAAA;AAAA"}
@@ -120,14 +120,15 @@ declare class StreamController<StateType extends object = Record<string, unknown
120
120
  * oldest and picks the first entry whose `interruptId` has not already
121
121
  * been resolved by a prior `respond()` call. That entry may be at the
122
122
  * root (`namespace: []`) or inside a subgraph (non-empty `namespace`).
123
- * This is **not** the same as {@link RootSnapshot.interrupts
124
- * `rootStore.interrupts[0]`} / framework `stream.interrupt`, which only
125
- * mirrors root-namespace interrupts for UI convenience.
123
+ * {@link RootSnapshot.interrupts `rootStore.interrupts`} /
124
+ * framework `stream.interrupts` mirrors the same pending interrupts
125
+ * (including nested namespaces) for UI rendering.
126
126
  *
127
127
  * Omitting `interruptId` is fine when exactly one interrupt is pending.
128
128
  * When several can be active (parallel subagents, fan-out, nested
129
- * graphs), pass an explicit `interruptId` (and `namespace` for subgraph
130
- * interrupts) so you resume the interrupt the user acted on.
129
+ * graphs), pass an explicit `interruptId` so you resume the interrupt
130
+ * the user acted on. `namespace` is resolved automatically from
131
+ * `thread.interrupts` / `stream.interrupts` when omitted.
131
132
  *
132
133
  * To resume several interrupts pending at the same checkpoint in one
133
134
  * command, use {@link respondAll} — sequential single `respond()` calls
@@ -135,9 +136,8 @@ declare class StreamController<StateType extends object = Record<string, unknown
135
136
  * others with no interrupted run to respond to.
136
137
  *
137
138
  * The server validates `namespace` against the pending interrupt. Root
138
- * interrupts use `namespace: []` (the default when `namespace` is
139
- * omitted). Subgraph interrupts require the exact tuple from
140
- * `getThread()?.interrupts` — see the example below.
139
+ * interrupts use `namespace: []`. Subgraph interrupts carry the exact
140
+ * tuple on each {@link Interrupt} / {@link InterruptPayload} entry.
141
141
  *
142
142
  * @param response - Payload sent back to the interrupted namespace.
143
143
  * @param options - Optional target (`interruptId` / `namespace`) and
@@ -158,27 +158,16 @@ declare class StreamController<StateType extends object = Record<string, unknown
158
158
  * );
159
159
  * ```
160
160
  *
161
- * @example Multiple root interrupts — target by id
161
+ * @example Multiple / nested interrupts — target by id
162
162
  * ```tsx
163
163
  * for (const intr of stream.interrupts) {
164
164
  * await stream.respond(decide(intr.value), { interruptId: intr.id! });
165
165
  * }
166
166
  * ```
167
167
  *
168
- * @example Subgraph interrupt — read `namespace` from the thread stream
169
- * ```tsx
170
- * const thread = stream.getThread();
171
- * for (const entry of thread?.interrupts ?? []) {
172
- * await stream.respond(buildResponse(entry.payload), {
173
- * interruptId: entry.interruptId,
174
- * namespace: entry.namespace,
175
- * });
176
- * }
177
- * ```
178
- *
179
- * Each {@link InterruptPayload} on `thread.interrupts` mirrors an
180
- * `input.requested` event: `{ interruptId, payload, namespace }`.
181
- * Nested interrupts may appear here but not on `stream.interrupts`.
168
+ * Each {@link InterruptPayload} on `thread.interrupts` and each
169
+ * {@link Interrupt} on `stream.interrupts` mirrors an
170
+ * `input.requested` event with its protocol `namespace`.
182
171
  */
183
172
  respond(response: unknown, options?: StreamRespondOptions<ConfigurableType>): Promise<void>;
184
173
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"controller.d.ts","names":[],"sources":["../../src/stream/controller.ts"],"mappings":";;;;;;;;;;;;;;;AA6KA;cAAa,kBAAA,WAA6B,OAAA;;;AAkC1C;;;;;;;;;;;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;;;;;EAsHnB,WAAA,CAAY,OAAA,EAAS,uBAAA,CAAwB,SAAA;EA+uBb;;;;;;;EAAA,IAroB5B,gBAAA,CAAA,GAAoB,OAAA;EA2uBJ;;;;;;;;EAlrBd,OAAA,CAAQ,QAAA,mBAA2B,OAAA;EA69B5B;;;;;;;;;;;;;;EAtpBP,wBAAA,CAAyB,UAAA,WAAqB,OAAA;EArmB3C;;;;;;;;EAw2BH,MAAA,CACJ,KAAA,WACA,OAAA,GAAU,mBAAA,CAAoB,SAAA,EAAW,gBAAA,IACxC,OAAA;EAx2BM;;;;;;EAk3BH,IAAA,CAAK,OAAA,GAAU,iBAAA,GAAoB,OAAA;EAh3BhC;;;;EAo4BH,UAAA,CAAA,GAAc,OAAA;EA9wBR;;;;;;;;;;;EA80BN,YAAA,CAAa,EAAA,WAAa,OAAA;EA/FA;;;EAsG1B,UAAA,CAAA,GAAc,OAAA;EA3Fd;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmKA,OAAA,CACJ,QAAA,WACA,OAAA,GAAU,oBAAA,CAAqB,gBAAA,IAC9B,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4GG,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
+ {"version":3,"file":"controller.d.ts","names":[],"sources":["../../src/stream/controller.ts"],"mappings":";;;;;;;;;;;;;;;AA6KA;cAAa,kBAAA,WAA6B,OAAA;;;AAkC1C;;;;;;;;;;;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;;;;;EAgInB,WAAA,CAAY,OAAA,EAAS,uBAAA,CAAwB,SAAA;EAsyBb;;;;;;;EAAA,IAvrB5B,gBAAA,CAAA,GAAoB,OAAA;EA6xBJ;;;;;;;;EApuBd,OAAA,CAAQ,QAAA,mBAA2B,OAAA;EAqgC5B;;;;;;;;;;;;;;EA7oBP,wBAAA,CAAyB,UAAA,WAAqB,OAAA;EArqB3C;;;;;;;;EAy6BH,MAAA,CACJ,KAAA,WACA,OAAA,GAAU,mBAAA,CAAoB,SAAA,EAAW,gBAAA,IACxC,OAAA;EAz6BM;;;;;;EAm7BH,IAAA,CAAK,OAAA,GAAU,iBAAA,GAAoB,OAAA;EAj7BhC;;;;EAq8BH,UAAA,CAAA,GAAc,OAAA;EAr0BR;;;;;;;;;;;EAq4BN,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+GG,UAAA,CACJ,aAAA,EAAe,MAAA,mBACf,OAAA,GAAU,uBAAA,CAAwB,gBAAA,IACjC,OAAA;;;;EA0DG,OAAA,CAAA,GAAW,OAAA;;;;;;;;;;;;;;;;EAyBjB,QAAA,CAAA;;;;;;EA4BA,SAAA,CAAA,GAAa,YAAA;;;;;;;;EAWb,eAAA,CACE,QAAA,GAAW,MAAA,EAAQ,YAAA;AAAA"}
@@ -169,13 +169,23 @@ var StreamController = class {
169
169
  */
170
170
  #submitGeneration = 0;
171
171
  /**
172
- * Thread ids we minted client-side on first `submit()`. Keeping them
173
- * here lets `hydrate()` skip the `threads.getState()` round-trip —
174
- * we know there is nothing checkpointed server-side yet (and the
175
- * request would 404 and surface a spurious error to the UI).
172
+ * Thread ids this controller minted client-side on first `submit()`.
173
+ * `hydrate()` skips `threads.getState()` for these — we know there
174
+ * is nothing checkpointed yet. Cleared once dispatch commits the
175
+ * thread server-side.
176
176
  */
177
177
  #selfCreatedThreadIds = /* @__PURE__ */ new Set();
178
178
  /**
179
+ * Caller-supplied thread ids that 404'd on hydrate. Unlike
180
+ * {@link #selfCreatedThreadIds}, these are revalidated on every
181
+ * hydrate: another client may create the same id between visits.
182
+ * While marked missing, `submit()` still defers the root SSE pump
183
+ * until `run.start` / `commands` commits the row — joining
184
+ * `/stream/events` beforehand leaves a dead subscription on
185
+ * langgraph_api's in-mem runtime.
186
+ */
187
+ #missingThreadIds = /* @__PURE__ */ new Set();
188
+ /**
179
189
  * In-flight per-subagent namespace resolutions, keyed by tool-call
180
190
  * id. De-dupes concurrent {@link resolveSubagentNamespace} calls so
181
191
  * re-renders / multiple consumers of the same subagent don't issue
@@ -270,12 +280,14 @@ var StreamController = class {
270
280
  rememberSelfCreatedThreadId: (threadId) => {
271
281
  this.#selfCreatedThreadIds.add(threadId);
272
282
  },
283
+ isSelfCreatedThreadId: (threadId) => this.#selfCreatedThreadIds.has(threadId) || this.#missingThreadIds.has(threadId),
273
284
  hydrate: (threadId) => this.hydrate(threadId),
274
285
  ensureThread: (threadId, deferRootPump) => this.#ensureThread(threadId, deferRootPump),
275
286
  startDeferredRootPump: () => this.#startDeferredRootPump(),
276
287
  abandonDeferredRootPump: () => this.#abandonDeferredRootPump(),
277
288
  forgetSelfCreatedThreadId: (threadId) => {
278
289
  this.#selfCreatedThreadIds.delete(threadId);
290
+ this.#missingThreadIds.delete(threadId);
279
291
  },
280
292
  waitForRootPumpReady: () => this.#rootPumpReady,
281
293
  awaitNextTerminal: (signal) => this.#awaitNextTerminal(signal),
@@ -423,13 +435,17 @@ var StreamController = class {
423
435
  ...s,
424
436
  isThreadLoading: true
425
437
  }));
438
+ const hydratedThreadId = this.#currentThreadId;
426
439
  let hydrationError;
427
440
  let threadExists = false;
441
+ let threadMissing = false;
428
442
  let threadActive = true;
429
443
  try {
430
444
  const state = await this.#fetchHydrationState();
445
+ if (this.#disposed || this.#currentThreadId !== hydratedThreadId) return;
431
446
  threadExists = state != null;
432
447
  threadActive = isThreadStateActive(state);
448
+ this.#missingThreadIds.delete(hydratedThreadId);
433
449
  if (state?.values != null) {
434
450
  /**
435
451
  * `threads.getState()` returns the legacy `ThreadState` shape
@@ -514,6 +530,8 @@ var StreamController = class {
514
530
  const activeIds = /* @__PURE__ */ new Set();
515
531
  for (const task of state.tasks) {
516
532
  if (!Array.isArray(task?.interrupts)) continue;
533
+ const checkpointNs = task?.checkpoint?.checkpoint_ns;
534
+ const namespace = typeof checkpointNs === "string" && checkpointNs.length > 0 ? checkpointNs.split("|").filter((segment) => segment.length > 0) : [...ROOT_NAMESPACE];
517
535
  for (const interrupt of task.interrupts) {
518
536
  const typed = interrupt;
519
537
  const id = typed?.id;
@@ -521,7 +539,8 @@ var StreamController = class {
521
539
  activeIds.add(id);
522
540
  activeInterrupts.push(normalizeInterruptForClient({
523
541
  id,
524
- value: typed?.value
542
+ value: typed?.value,
543
+ namespace: Array.isArray(typed?.namespace) ? [...typed.namespace] : namespace
525
544
  }));
526
545
  }
527
546
  }
@@ -539,7 +558,7 @@ var StreamController = class {
539
558
  ...s,
540
559
  error
541
560
  }));
542
- }
561
+ } else threadMissing = true;
543
562
  } finally {
544
563
  this.rootStore.setState((s) => ({
545
564
  ...s,
@@ -549,6 +568,20 @@ var StreamController = class {
549
568
  else this.#resolveHydration();
550
569
  }
551
570
  /**
571
+ * Missing threads must not open `/stream/events`. The in-mem
572
+ * runtime accepts the join against a not-yet-created id but the
573
+ * subscription never attaches to the live channel — the first run
574
+ * then delivers 0 bytes until idle reconnect. Park the pump until
575
+ * `submit()`'s `run.start` creates the row (see
576
+ * {@link #startDeferredRootPump}). Tracked in `#missingThreadIds`
577
+ * (not `#selfCreatedThreadIds`) so a later hydrate revalidates —
578
+ * another client may create the same id between visits.
579
+ */
580
+ if (threadMissing) {
581
+ this.#missingThreadIds.add(hydratedThreadId);
582
+ return;
583
+ }
584
+ /**
552
585
  * Open the shared subscription on mount so in-flight server-side
553
586
  * runs are observed even when no local `submit()` is active — BUT
554
587
  * only when the thread is actually active (see
@@ -561,7 +594,7 @@ var StreamController = class {
561
594
  * The transport replays from `seq=0` on the deferred subscribe, so
562
595
  * nothing is missed.
563
596
  */
564
- const thread = this.#ensureThread(this.#currentThreadId, !threadActive);
597
+ const thread = this.#ensureThread(hydratedThreadId, !threadActive);
565
598
  /**
566
599
  * Start the wildcard lifecycle watcher up-front for existing,
567
600
  * active threads. The root content pump runs at `depth: 1`, which
@@ -685,7 +718,7 @@ var StreamController = class {
685
718
  */
686
719
  async #trySeedProjectionFromHistory(params) {
687
720
  const threadId = this.#currentThreadId;
688
- if (this.#disposed || threadId == null || params.namespace.length === 0 || !this.#rootPumpDeferred || this.#selfCreatedThreadIds.has(threadId)) return false;
721
+ if (this.#disposed || threadId == null || params.namespace.length === 0 || !this.#rootPumpDeferred || this.#selfCreatedThreadIds.has(threadId) || this.#missingThreadIds.has(threadId)) return false;
689
722
  if (await this.#skipDefaultSubagentProjection(params.namespace, threadId)) return true;
690
723
  if (this.#disposed || this.#currentThreadId !== threadId || !this.#rootPumpDeferred) return false;
691
724
  const seed = await this.#getScopedHistorySeed(threadId, params.namespace);
@@ -893,14 +926,15 @@ var StreamController = class {
893
926
  * oldest and picks the first entry whose `interruptId` has not already
894
927
  * been resolved by a prior `respond()` call. That entry may be at the
895
928
  * root (`namespace: []`) or inside a subgraph (non-empty `namespace`).
896
- * This is **not** the same as {@link RootSnapshot.interrupts
897
- * `rootStore.interrupts[0]`} / framework `stream.interrupt`, which only
898
- * mirrors root-namespace interrupts for UI convenience.
929
+ * {@link RootSnapshot.interrupts `rootStore.interrupts`} /
930
+ * framework `stream.interrupts` mirrors the same pending interrupts
931
+ * (including nested namespaces) for UI rendering.
899
932
  *
900
933
  * Omitting `interruptId` is fine when exactly one interrupt is pending.
901
934
  * When several can be active (parallel subagents, fan-out, nested
902
- * graphs), pass an explicit `interruptId` (and `namespace` for subgraph
903
- * interrupts) so you resume the interrupt the user acted on.
935
+ * graphs), pass an explicit `interruptId` so you resume the interrupt
936
+ * the user acted on. `namespace` is resolved automatically from
937
+ * `thread.interrupts` / `stream.interrupts` when omitted.
904
938
  *
905
939
  * To resume several interrupts pending at the same checkpoint in one
906
940
  * command, use {@link respondAll} — sequential single `respond()` calls
@@ -908,9 +942,8 @@ var StreamController = class {
908
942
  * others with no interrupted run to respond to.
909
943
  *
910
944
  * The server validates `namespace` against the pending interrupt. Root
911
- * interrupts use `namespace: []` (the default when `namespace` is
912
- * omitted). Subgraph interrupts require the exact tuple from
913
- * `getThread()?.interrupts` — see the example below.
945
+ * interrupts use `namespace: []`. Subgraph interrupts carry the exact
946
+ * tuple on each {@link Interrupt} / {@link InterruptPayload} entry.
914
947
  *
915
948
  * @param response - Payload sent back to the interrupted namespace.
916
949
  * @param options - Optional target (`interruptId` / `namespace`) and
@@ -931,33 +964,22 @@ var StreamController = class {
931
964
  * );
932
965
  * ```
933
966
  *
934
- * @example Multiple root interrupts — target by id
967
+ * @example Multiple / nested interrupts — target by id
935
968
  * ```tsx
936
969
  * for (const intr of stream.interrupts) {
937
970
  * await stream.respond(decide(intr.value), { interruptId: intr.id! });
938
971
  * }
939
972
  * ```
940
973
  *
941
- * @example Subgraph interrupt — read `namespace` from the thread stream
942
- * ```tsx
943
- * const thread = stream.getThread();
944
- * for (const entry of thread?.interrupts ?? []) {
945
- * await stream.respond(buildResponse(entry.payload), {
946
- * interruptId: entry.interruptId,
947
- * namespace: entry.namespace,
948
- * });
949
- * }
950
- * ```
951
- *
952
- * Each {@link InterruptPayload} on `thread.interrupts` mirrors an
953
- * `input.requested` event: `{ interruptId, payload, namespace }`.
954
- * Nested interrupts may appear here but not on `stream.interrupts`.
974
+ * Each {@link InterruptPayload} on `thread.interrupts` and each
975
+ * {@link Interrupt} on `stream.interrupts` mirrors an
976
+ * `input.requested` event with its protocol `namespace`.
955
977
  */
956
978
  async respond(response, options) {
957
979
  if (this.#disposed || this.#thread == null) throw new Error("No active thread to respond to.");
958
980
  const resolved = options?.interruptId != null ? {
959
981
  interruptId: options.interruptId,
960
- namespace: options.namespace ?? [...ROOT_NAMESPACE]
982
+ namespace: this.#resolveNamespaceForInterruptId(options.interruptId, options.namespace)
961
983
  } : this.#resolveInterruptForResume();
962
984
  if (resolved == null) throw new Error("No pending interrupt to respond to.");
963
985
  const thread = this.#thread;
@@ -1027,11 +1049,10 @@ var StreamController = class {
1027
1049
  const entries = Object.entries(responsesById);
1028
1050
  if (entries.length === 0) throw new Error("respondAll() requires at least one response.");
1029
1051
  const thread = this.#thread;
1030
- const pending = thread.interrupts;
1031
1052
  const responses = entries.map(([interruptId, response]) => ({
1032
1053
  interrupt_id: interruptId,
1033
1054
  response: normalizeHitlResponseForServer(response),
1034
- namespace: pending.find((entry) => entry.interruptId === interruptId)?.namespace ?? [...ROOT_NAMESPACE]
1055
+ namespace: this.#resolveNamespaceForInterruptId(interruptId)
1035
1056
  }));
1036
1057
  const prepared = options?.update != null ? this.#beginOptimistic(options.update) : void 0;
1037
1058
  const dispatchUpdate = this.#resolveDispatchUpdate(options?.update, prepared);
@@ -1198,16 +1219,11 @@ var StreamController = class {
1198
1219
  * Without this, the controller would be wedged in a state where:
1199
1220
  * - `#thread` is wired but no content pump is open
1200
1221
  * - `#rootPumpDeferred` stays `true`
1201
- * - `selfCreatedThreadIds` still holds the id
1202
1222
  *
1203
- * A retry submit on the same controller would see
1204
- * `wasSelfCreated=false` (because `currentThreadId` is no longer
1205
- * null), `#ensureThread(id, false)` would early-return because
1206
- * `#thread != null`, and the pump would never start. The thread
1207
- * would have an id committed to the URL but no live subscription.
1208
- *
1209
- * Tearing down `#thread` so the next submit re-runs `#ensureThread`
1210
- * from scratch is the simplest recovery — the failed dispatch
1223
+ * A retry submit must re-run `#ensureThread` from scratch (with
1224
+ * `deferRootPump` still true — the id stays in
1225
+ * `#selfCreatedThreadIds` until a successful dispatch). Tearing
1226
+ * down `#thread` is the simplest recovery; the failed dispatch
1211
1227
  * means there was nothing to subscribe to anyway.
1212
1228
  */
1213
1229
  #abandonDeferredRootPump() {
@@ -1410,16 +1426,14 @@ var StreamController = class {
1410
1426
  this.#subgraphs.push(event);
1411
1427
  this.#lifecycleLoading.handle(event);
1412
1428
  /**
1413
- * Nested `input.requested` events (HITL inside a subagent /
1414
- * subgraph) are not observable via the narrow content pump. The
1415
- * `ThreadStream` itself already records them into
1416
- * `thread.interrupts`, which `#latestUnresolvedInterrupt()`
1417
- * consults — so HITL respond() works for any depth. Root-level
1418
- * interrupts are also mirrored into `rootStore.interrupts` here so
1419
- * UI state does not depend on the narrower content pump being the
1420
- * first consumer to see the event.
1429
+ * `input.requested` events (including HITL inside a subagent /
1430
+ * subgraph) are not always observable via the narrow content pump.
1431
+ * Mirror every namespace into `rootStore.interrupts` here so UI
1432
+ * state does not depend on the content pump being the first
1433
+ * consumer. `ThreadStream.interrupts` is updated in parallel for
1434
+ * resume targeting.
1421
1435
  */
1422
- this.#recordRootInterrupt(event);
1436
+ this.#recordInterrupt(event);
1423
1437
  }
1424
1438
  /**
1425
1439
  * Process one root-pump event and update all root projections.
@@ -1512,7 +1526,7 @@ var StreamController = class {
1512
1526
  return;
1513
1527
  }
1514
1528
  if (event.method === "input.requested") {
1515
- this.#recordRootInterrupt(event);
1529
+ this.#recordInterrupt(event);
1516
1530
  return;
1517
1531
  }
1518
1532
  if (event.method === "lifecycle") event.params.data;
@@ -1640,21 +1654,22 @@ var StreamController = class {
1640
1654
  }
1641
1655
  }
1642
1656
  /**
1643
- * Mirror root protocol interrupts into the root snapshot.
1657
+ * Mirror protocol interrupts (any namespace) into the root snapshot.
1644
1658
  *
1645
1659
  * This can be called from both the wildcard lifecycle/input watcher and the
1646
1660
  * root content pump. Store-level dedup keeps the user-facing list stable.
1647
1661
  */
1648
- #recordRootInterrupt(event) {
1662
+ #recordInterrupt(event) {
1649
1663
  if (event.method !== "input.requested") return;
1650
- if (!isRootNamespace(event.params.namespace)) return;
1651
1664
  const data = event.params.data;
1652
1665
  const interruptId = data?.interrupt_id;
1653
1666
  if (typeof interruptId !== "string" || this.#resolvedInterrupts.has(interruptId)) return;
1654
1667
  if (this.#hydratedActiveInterruptIds != null && !this.#hydratedActiveInterruptIds.has(interruptId)) return;
1668
+ const namespace = Array.isArray(event.params.namespace) ? [...event.params.namespace] : [...ROOT_NAMESPACE];
1655
1669
  const interrupt = normalizeInterruptForClient({
1656
1670
  id: interruptId,
1657
- value: data.payload
1671
+ value: data.payload,
1672
+ namespace
1658
1673
  });
1659
1674
  this.rootStore.setState((s) => {
1660
1675
  if (s.interrupts.some((entry) => entry.id === interruptId)) return s;
@@ -1667,6 +1682,21 @@ var StreamController = class {
1667
1682
  });
1668
1683
  }
1669
1684
  /**
1685
+ * Resolve the protocol namespace for a targeted resume.
1686
+ *
1687
+ * Prefer an explicit caller-supplied namespace, then the matching
1688
+ * {@link ThreadStream.interrupts} entry, then the UI mirror on
1689
+ * `rootStore.interrupts`, finally root (`[]`).
1690
+ */
1691
+ #resolveNamespaceForInterruptId(interruptId, explicitNamespace) {
1692
+ if (explicitNamespace != null) return [...explicitNamespace];
1693
+ const fromThread = this.#thread?.interrupts.find((entry) => entry.interruptId === interruptId)?.namespace;
1694
+ if (fromThread != null) return [...fromThread];
1695
+ const fromRoot = this.rootStore.getSnapshot().interrupts.find((entry) => entry.id === interruptId)?.namespace;
1696
+ if (fromRoot != null) return [...fromRoot];
1697
+ return [...ROOT_NAMESPACE];
1698
+ }
1699
+ /**
1670
1700
  * Mark an interrupt resolved for replay filtering and mirror the
1671
1701
  * removal into the root snapshot the framework hooks read.
1672
1702
  */