@langchain/langgraph-sdk 1.9.8 → 1.9.10

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 (55) hide show
  1. package/dist/client/stream/index.cjs +37 -5
  2. package/dist/client/stream/index.cjs.map +1 -1
  3. package/dist/client/stream/index.d.cts +7 -14
  4. package/dist/client/stream/index.d.cts.map +1 -1
  5. package/dist/client/stream/index.d.ts +7 -14
  6. package/dist/client/stream/index.d.ts.map +1 -1
  7. package/dist/client/stream/index.js +37 -5
  8. package/dist/client/stream/index.js.map +1 -1
  9. package/dist/client/stream/transport/utils.cjs +2 -1
  10. package/dist/client/stream/transport/utils.cjs.map +1 -1
  11. package/dist/client/stream/transport/utils.js +2 -1
  12. package/dist/client/stream/transport/utils.js.map +1 -1
  13. package/dist/client/stream/transport/websocket.cjs +1 -1
  14. package/dist/client/stream/transport/websocket.cjs.map +1 -1
  15. package/dist/client/stream/transport/websocket.d.cts.map +1 -1
  16. package/dist/client/stream/transport/websocket.d.ts.map +1 -1
  17. package/dist/client/stream/transport/websocket.js +2 -2
  18. package/dist/client/stream/transport/websocket.js.map +1 -1
  19. package/dist/client/stream/types.d.cts +8 -3
  20. package/dist/client/stream/types.d.cts.map +1 -1
  21. package/dist/client/stream/types.d.ts +8 -3
  22. package/dist/client/stream/types.d.ts.map +1 -1
  23. package/dist/headless-tools.cjs +0 -24
  24. package/dist/headless-tools.cjs.map +1 -1
  25. package/dist/headless-tools.d.cts.map +1 -1
  26. package/dist/headless-tools.d.ts.map +1 -1
  27. package/dist/headless-tools.js +1 -24
  28. package/dist/headless-tools.js.map +1 -1
  29. package/dist/stream/controller.cjs +156 -19
  30. package/dist/stream/controller.cjs.map +1 -1
  31. package/dist/stream/controller.d.cts +113 -10
  32. package/dist/stream/controller.d.cts.map +1 -1
  33. package/dist/stream/controller.d.ts +113 -10
  34. package/dist/stream/controller.d.ts.map +1 -1
  35. package/dist/stream/controller.js +157 -20
  36. package/dist/stream/controller.js.map +1 -1
  37. package/dist/stream/index.d.cts +2 -2
  38. package/dist/stream/index.d.ts +2 -2
  39. package/dist/stream/message-metadata-tracker.cjs +1 -1
  40. package/dist/stream/message-metadata-tracker.cjs.map +1 -1
  41. package/dist/stream/message-metadata-tracker.d.cts +1 -1
  42. package/dist/stream/message-metadata-tracker.d.ts +1 -1
  43. package/dist/stream/message-metadata-tracker.js +1 -1
  44. package/dist/stream/message-metadata-tracker.js.map +1 -1
  45. package/dist/stream/submit-coordinator.cjs +45 -79
  46. package/dist/stream/submit-coordinator.cjs.map +1 -1
  47. package/dist/stream/submit-coordinator.d.cts.map +1 -1
  48. package/dist/stream/submit-coordinator.d.ts.map +1 -1
  49. package/dist/stream/submit-coordinator.js +45 -79
  50. package/dist/stream/submit-coordinator.js.map +1 -1
  51. package/dist/stream/types.d.cts +52 -30
  52. package/dist/stream/types.d.cts.map +1 -1
  53. package/dist/stream/types.d.ts +52 -30
  54. package/dist/stream/types.d.ts.map +1 -1
  55. package/package.json +2 -2
@@ -1,6 +1,6 @@
1
1
  import { ThreadStream } from "../client/stream/index.cjs";
2
2
  import { StreamStore } from "./store.cjs";
3
- import { RootSnapshot, StreamControllerOptions, StreamStopOptions, StreamSubmitOptions } from "./types.cjs";
3
+ import { RootSnapshot, StreamControllerOptions, StreamRespondAllOptions, StreamRespondOptions, StreamStopOptions, StreamSubmitOptions } from "./types.cjs";
4
4
  import { ChannelRegistry } from "./channel-registry.cjs";
5
5
  import { SubagentMap } from "./discovery/subagents.cjs";
6
6
  import { SubgraphByNodeMap, SubgraphMap } from "./discovery/subgraphs.cjs";
@@ -61,9 +61,11 @@ declare class StreamController<StateType extends object = Record<string, unknown
61
61
  */
62
62
  hydrate(threadId?: string | null): Promise<void>;
63
63
  /**
64
- * Submit input or a resume command to the active thread.
64
+ * Submit input to the active thread.
65
65
  *
66
- * @param input - Input payload for a new run; `null`/`undefined` submits no input.
66
+ * To resume a pending interrupt, use {@link respond} instead.
67
+ *
68
+ * @param input - Input payload for a new run.
67
69
  * @param options - Per-run config, metadata, multitask behavior, and callbacks.
68
70
  */
69
71
  submit(input: unknown, options?: StreamSubmitOptions<StateType, ConfigurableType>): Promise<void>;
@@ -96,15 +98,116 @@ declare class StreamController<StateType extends object = Record<string, unknown
96
98
  */
97
99
  clearQueue(): Promise<void>;
98
100
  /**
99
- * Respond to a pending protocol interrupt.
101
+ * Respond to a single pending protocol interrupt.
102
+ *
103
+ * When `options.interruptId` is omitted, resolution walks
104
+ * {@link ThreadStream.interrupts `thread.interrupts`} from newest to
105
+ * oldest and picks the first entry whose `interruptId` has not already
106
+ * been resolved by a prior `respond()` call. That entry may be at the
107
+ * root (`namespace: []`) or inside a subgraph (non-empty `namespace`).
108
+ * This is **not** the same as {@link RootSnapshot.interrupts
109
+ * `rootStore.interrupts[0]`} / framework `stream.interrupt`, which only
110
+ * mirrors root-namespace interrupts for UI convenience.
111
+ *
112
+ * Omitting `interruptId` is fine when exactly one interrupt is pending.
113
+ * When several can be active (parallel subagents, fan-out, nested
114
+ * graphs), pass an explicit `interruptId` (and `namespace` for subgraph
115
+ * interrupts) so you resume the interrupt the user acted on.
116
+ *
117
+ * To resume several interrupts pending at the same checkpoint in one
118
+ * command, use {@link respondAll} — sequential single `respond()` calls
119
+ * would not work, since the first resume starts a run, leaving the
120
+ * others with no interrupted run to respond to.
121
+ *
122
+ * The server validates `namespace` against the pending interrupt. Root
123
+ * interrupts use `namespace: []` (the default when `namespace` is
124
+ * omitted). Subgraph interrupts require the exact tuple from
125
+ * `getThread()?.interrupts` — see the example below.
126
+ *
127
+ * @param response - Payload sent back to the interrupted namespace.
128
+ * @param options - Optional target (`interruptId` / `namespace`) and
129
+ * run-level `config` / `metadata` folded into the run that services
130
+ * the resume (model/user config, trigger source, test flags, …).
131
+ * Equivalent to the same fields on {@link StreamSubmitOptions}.
132
+ *
133
+ * @example Single pending interrupt (safe to omit a target)
134
+ * ```ts
135
+ * await controller.respond({ approved: true });
136
+ * ```
137
+ *
138
+ * @example Carry run config / metadata onto the resume
139
+ * ```ts
140
+ * await controller.respond(
141
+ * { approved: true },
142
+ * { config: { configurable: { model: "gpt-4o" } }, metadata: { source: "ui" } },
143
+ * );
144
+ * ```
145
+ *
146
+ * @example Multiple root interrupts — target by id
147
+ * ```tsx
148
+ * for (const intr of stream.interrupts) {
149
+ * await stream.respond(decide(intr.value), { interruptId: intr.id! });
150
+ * }
151
+ * ```
152
+ *
153
+ * @example Subgraph interrupt — read `namespace` from the thread stream
154
+ * ```tsx
155
+ * const thread = stream.getThread();
156
+ * for (const entry of thread?.interrupts ?? []) {
157
+ * await stream.respond(buildResponse(entry.payload), {
158
+ * interruptId: entry.interruptId,
159
+ * namespace: entry.namespace,
160
+ * });
161
+ * }
162
+ * ```
163
+ *
164
+ * Each {@link InterruptPayload} on `thread.interrupts` mirrors an
165
+ * `input.requested` event: `{ interruptId, payload, namespace }`.
166
+ * Nested interrupts may appear here but not on `stream.interrupts`.
167
+ */
168
+ respond(response: unknown, options?: StreamRespondOptions<ConfigurableType>): Promise<void>;
169
+ /**
170
+ * Resume several pending interrupts at the same checkpoint in a single
171
+ * command.
172
+ *
173
+ * Required when a run pauses on multiple interrupts simultaneously
174
+ * (e.g. parallel tool-authorization prompts): a single
175
+ * `Command({ resume })` carrying every interrupt's payload resumes them
176
+ * together. Sequential {@link respond} calls would fail because the
177
+ * first resume starts a run, leaving the rest with no interrupted run to
178
+ * respond to.
179
+ *
180
+ * `responsesById` maps each pending `interruptId` to the payload sent
181
+ * back to it, so different interrupts can receive different responses
182
+ * (approve one, deny another). To send the *same* payload to several
183
+ * interrupts, build the map with that value for each id, e.g.
184
+ * `Object.fromEntries(ids.map((id) => [id, response]))`.
185
+ *
186
+ * The server resumes by `interruptId`, so namespaces are resolved
187
+ * internally from `getThread()?.interrupts` and need not be supplied.
188
+ *
189
+ * @param responsesById - Map of pending `interruptId` to its response
190
+ * payload. Must contain at least one entry.
191
+ * @param options - Optional run-level `config` / `metadata` folded into
192
+ * the single run that services the batched resume. Equivalent to the
193
+ * same fields on {@link StreamSubmitOptions}.
194
+ *
195
+ * @example Distinct payloads per interrupt
196
+ * ```tsx
197
+ * await stream.respondAll({
198
+ * [interruptA.id]: { approved: true },
199
+ * [interruptB.id]: { approved: false },
200
+ * });
201
+ * ```
100
202
  *
101
- * @param response - Payload to send back to the interrupted namespace.
102
- * @param target - Optional explicit interrupt id and namespace; defaults to the latest unresolved interrupt.
203
+ * @example Same payload to every pending interrupt
204
+ * ```tsx
205
+ * await stream.respondAll(
206
+ * Object.fromEntries(stream.interrupts.map((i) => [i.id!, { approved: true }])),
207
+ * );
208
+ * ```
103
209
  */
104
- respond(response: unknown, target?: {
105
- interruptId: string;
106
- namespace?: string[];
107
- }): Promise<void>;
210
+ respondAll(responsesById: Record<string, unknown>, options?: StreamRespondAllOptions<ConfigurableType>): Promise<void>;
108
211
  /**
109
212
  * Dispose the active thread, subscriptions, registry entries, and listeners.
110
213
  */
@@ -1 +1 @@
1
- {"version":3,"file":"controller.d.cts","names":[],"sources":["../../src/stream/controller.ts"],"mappings":";;;;;;;;;;;;;;AA0GA;;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;EAyF0B;;;;;EAA7C,WAAA,CAAY,OAAA,EAAS,uBAAA,CAAwB,SAAA;EA6UjC;;;;;;;EAAA,IA9NR,gBAAA,CAAA,GAAoB,OAAA;EAiXP;;;;;;;;EA5UX,OAAA,CAAQ,QAAA,mBAA2B,OAAA;EArPP;;;;;;EA4a5B,MAAA,CACJ,KAAA,WACA,OAAA,GAAU,mBAAA,CAAoB,SAAA,EAAW,gBAAA,IACxC,OAAA;EA5aM;;;;;;EAsbH,IAAA,CAAK,OAAA,GAAU,iBAAA,GAAoB,OAAA;EApbX;;;;EAwcxB,UAAA,CAAA,GAAc,OAAA;EAtcX;;;;;;;;;;;EAsgBH,YAAA,CAAa,EAAA,WAAa,OAAA;EAxR1B;;;EA+RA,UAAA,CAAA,GAAc,OAAA;EAvGlB;;;;;;EAiHI,OAAA,CACJ,QAAA,WACA,MAAA;IAAW,WAAA;IAAqB,SAAA;EAAA,IAC/B,OAAA;EApFG;;;EAoHA,OAAA,CAAA,GAAW,OAAA;EApDe;;;;;;;;;;;;;;;EA6EhC,QAAA,CAAA;EAwCa;;;;;EAZb,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":";;;;;;;;;;;;;;AAyGA;;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;EAyF0B;;;;;EAA7C,WAAA,CAAY,OAAA,EAAS,uBAAA,CAAwB,SAAA;EAmUjC;;;;;;;EAAA,IAhOR,gBAAA,CAAA,GAAoB,OAAA;EAgZZ;;;;;;;;EA3WN,OAAA,CAAQ,QAAA,mBAA2B,OAAA;EA4hBR;;;;;;;;EAnW3B,MAAA,CACJ,KAAA,WACA,OAAA,GAAU,mBAAA,CAAoB,SAAA,EAAW,gBAAA,IACxC,OAAA;EAnaiB;;;;;;EA6ad,IAAA,CAAK,OAAA,GAAU,iBAAA,GAAoB,OAAA;EA3ahC;;;;EA+bH,UAAA,CAAA,GAAc,OAAA;EA9bsB;;;;;;;;;;;EA8fpC,YAAA,CAAa,EAAA,WAAa,OAAA;EAlaa;;;EAyavC,UAAA,CAAA,GAAc,OAAA;EAjSd;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyWA,OAAA,CACJ,QAAA,WACA,OAAA,GAAU,oBAAA,CAAqB,gBAAA,IAC9B,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyEG,UAAA,CACJ,aAAA,EAAe,MAAA,mBACf,OAAA,GAAU,uBAAA,CAAwB,gBAAA,IACjC,OAAA;;;;EAmCG,OAAA,CAAA,GAAW,OAAA;;;;;;;;;;;;;;;;EAyBjB,QAAA,CAAA;;;;;;EA4BA,SAAA,CAAA,GAAa,YAAA;;;;;;;;EAWb,eAAA,CACE,QAAA,GAAW,MAAA,EAAQ,YAAA;AAAA"}
@@ -1,6 +1,6 @@
1
1
  import { ThreadStream } from "../client/stream/index.js";
2
2
  import { StreamStore } from "./store.js";
3
- import { RootSnapshot, StreamControllerOptions, StreamStopOptions, StreamSubmitOptions } from "./types.js";
3
+ import { RootSnapshot, StreamControllerOptions, StreamRespondAllOptions, StreamRespondOptions, StreamStopOptions, StreamSubmitOptions } from "./types.js";
4
4
  import { ChannelRegistry } from "./channel-registry.js";
5
5
  import { SubagentMap } from "./discovery/subagents.js";
6
6
  import { SubgraphByNodeMap, SubgraphMap } from "./discovery/subgraphs.js";
@@ -61,9 +61,11 @@ declare class StreamController<StateType extends object = Record<string, unknown
61
61
  */
62
62
  hydrate(threadId?: string | null): Promise<void>;
63
63
  /**
64
- * Submit input or a resume command to the active thread.
64
+ * Submit input to the active thread.
65
65
  *
66
- * @param input - Input payload for a new run; `null`/`undefined` submits no input.
66
+ * To resume a pending interrupt, use {@link respond} instead.
67
+ *
68
+ * @param input - Input payload for a new run.
67
69
  * @param options - Per-run config, metadata, multitask behavior, and callbacks.
68
70
  */
69
71
  submit(input: unknown, options?: StreamSubmitOptions<StateType, ConfigurableType>): Promise<void>;
@@ -96,15 +98,116 @@ declare class StreamController<StateType extends object = Record<string, unknown
96
98
  */
97
99
  clearQueue(): Promise<void>;
98
100
  /**
99
- * Respond to a pending protocol interrupt.
101
+ * Respond to a single pending protocol interrupt.
102
+ *
103
+ * When `options.interruptId` is omitted, resolution walks
104
+ * {@link ThreadStream.interrupts `thread.interrupts`} from newest to
105
+ * oldest and picks the first entry whose `interruptId` has not already
106
+ * been resolved by a prior `respond()` call. That entry may be at the
107
+ * root (`namespace: []`) or inside a subgraph (non-empty `namespace`).
108
+ * This is **not** the same as {@link RootSnapshot.interrupts
109
+ * `rootStore.interrupts[0]`} / framework `stream.interrupt`, which only
110
+ * mirrors root-namespace interrupts for UI convenience.
111
+ *
112
+ * Omitting `interruptId` is fine when exactly one interrupt is pending.
113
+ * When several can be active (parallel subagents, fan-out, nested
114
+ * graphs), pass an explicit `interruptId` (and `namespace` for subgraph
115
+ * interrupts) so you resume the interrupt the user acted on.
116
+ *
117
+ * To resume several interrupts pending at the same checkpoint in one
118
+ * command, use {@link respondAll} — sequential single `respond()` calls
119
+ * would not work, since the first resume starts a run, leaving the
120
+ * others with no interrupted run to respond to.
121
+ *
122
+ * The server validates `namespace` against the pending interrupt. Root
123
+ * interrupts use `namespace: []` (the default when `namespace` is
124
+ * omitted). Subgraph interrupts require the exact tuple from
125
+ * `getThread()?.interrupts` — see the example below.
126
+ *
127
+ * @param response - Payload sent back to the interrupted namespace.
128
+ * @param options - Optional target (`interruptId` / `namespace`) and
129
+ * run-level `config` / `metadata` folded into the run that services
130
+ * the resume (model/user config, trigger source, test flags, …).
131
+ * Equivalent to the same fields on {@link StreamSubmitOptions}.
132
+ *
133
+ * @example Single pending interrupt (safe to omit a target)
134
+ * ```ts
135
+ * await controller.respond({ approved: true });
136
+ * ```
137
+ *
138
+ * @example Carry run config / metadata onto the resume
139
+ * ```ts
140
+ * await controller.respond(
141
+ * { approved: true },
142
+ * { config: { configurable: { model: "gpt-4o" } }, metadata: { source: "ui" } },
143
+ * );
144
+ * ```
145
+ *
146
+ * @example Multiple root interrupts — target by id
147
+ * ```tsx
148
+ * for (const intr of stream.interrupts) {
149
+ * await stream.respond(decide(intr.value), { interruptId: intr.id! });
150
+ * }
151
+ * ```
152
+ *
153
+ * @example Subgraph interrupt — read `namespace` from the thread stream
154
+ * ```tsx
155
+ * const thread = stream.getThread();
156
+ * for (const entry of thread?.interrupts ?? []) {
157
+ * await stream.respond(buildResponse(entry.payload), {
158
+ * interruptId: entry.interruptId,
159
+ * namespace: entry.namespace,
160
+ * });
161
+ * }
162
+ * ```
163
+ *
164
+ * Each {@link InterruptPayload} on `thread.interrupts` mirrors an
165
+ * `input.requested` event: `{ interruptId, payload, namespace }`.
166
+ * Nested interrupts may appear here but not on `stream.interrupts`.
167
+ */
168
+ respond(response: unknown, options?: StreamRespondOptions<ConfigurableType>): Promise<void>;
169
+ /**
170
+ * Resume several pending interrupts at the same checkpoint in a single
171
+ * command.
172
+ *
173
+ * Required when a run pauses on multiple interrupts simultaneously
174
+ * (e.g. parallel tool-authorization prompts): a single
175
+ * `Command({ resume })` carrying every interrupt's payload resumes them
176
+ * together. Sequential {@link respond} calls would fail because the
177
+ * first resume starts a run, leaving the rest with no interrupted run to
178
+ * respond to.
179
+ *
180
+ * `responsesById` maps each pending `interruptId` to the payload sent
181
+ * back to it, so different interrupts can receive different responses
182
+ * (approve one, deny another). To send the *same* payload to several
183
+ * interrupts, build the map with that value for each id, e.g.
184
+ * `Object.fromEntries(ids.map((id) => [id, response]))`.
185
+ *
186
+ * The server resumes by `interruptId`, so namespaces are resolved
187
+ * internally from `getThread()?.interrupts` and need not be supplied.
188
+ *
189
+ * @param responsesById - Map of pending `interruptId` to its response
190
+ * payload. Must contain at least one entry.
191
+ * @param options - Optional run-level `config` / `metadata` folded into
192
+ * the single run that services the batched resume. Equivalent to the
193
+ * same fields on {@link StreamSubmitOptions}.
194
+ *
195
+ * @example Distinct payloads per interrupt
196
+ * ```tsx
197
+ * await stream.respondAll({
198
+ * [interruptA.id]: { approved: true },
199
+ * [interruptB.id]: { approved: false },
200
+ * });
201
+ * ```
100
202
  *
101
- * @param response - Payload to send back to the interrupted namespace.
102
- * @param target - Optional explicit interrupt id and namespace; defaults to the latest unresolved interrupt.
203
+ * @example Same payload to every pending interrupt
204
+ * ```tsx
205
+ * await stream.respondAll(
206
+ * Object.fromEntries(stream.interrupts.map((i) => [i.id!, { approved: true }])),
207
+ * );
208
+ * ```
103
209
  */
104
- respond(response: unknown, target?: {
105
- interruptId: string;
106
- namespace?: string[];
107
- }): Promise<void>;
210
+ respondAll(responsesById: Record<string, unknown>, options?: StreamRespondAllOptions<ConfigurableType>): Promise<void>;
108
211
  /**
109
212
  * Dispose the active thread, subscriptions, registry entries, and listeners.
110
213
  */
@@ -1 +1 @@
1
- {"version":3,"file":"controller.d.ts","names":[],"sources":["../../src/stream/controller.ts"],"mappings":";;;;;;;;;;;;;;;AA0GA;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;;;;;EAyFnB,WAAA,CAAY,OAAA,EAAS,uBAAA,CAAwB,SAAA;EA6UF;;;;;;;EAAA,IA9NvC,gBAAA,CAAA,GAAoB,OAAA;EAiVrB;;;;;;;;EA5SG,OAAA,CAAQ,QAAA,mBAA2B,OAAA;EArPzC;;;;;;EA4aM,MAAA,CACJ,KAAA,WACA,OAAA,GAAU,mBAAA,CAAoB,SAAA,EAAW,gBAAA,IACxC,OAAA;EA7aqD;;;;;;EAublD,IAAA,CAAK,OAAA,GAAU,iBAAA,GAAoB,OAAA;EApbhC;;;;EAwcH,UAAA,CAAA,GAAc,OAAA;EAvcuB;;;;;;;;;;;EAugBrC,YAAA,CAAa,EAAA,WAAa,OAAA;EA7TR;;;EAoUlB,UAAA,CAAA,GAAc,OAAA;EAxGd;;;;;;EAkHA,OAAA,CACJ,QAAA,WACA,MAAA;IAAW,WAAA;IAAqB,SAAA;EAAA,IAC/B,OAAA;EAxGsC;;;EAwInC,OAAA,CAAA,GAAW,OAAA;EApDE;;;;;;;;;;;;;;;EA6EnB,QAAA,CAAA;EAwCqB;;;;;EAZrB,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":";;;;;;;;;;;;;;;AAyGA;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;;;;;EAyFnB,WAAA,CAAY,OAAA,EAAS,uBAAA,CAAwB,SAAA;EAmUF;;;;;;;EAAA,IAhOvC,gBAAA,CAAA,GAAoB,OAAA;EAgZS;;;;;;;;EA3W3B,OAAA,CAAQ,QAAA,mBAA2B,OAAA;EA4hBpB;;;;;;;;EAnWf,MAAA,CACJ,KAAA,WACA,OAAA,GAAU,mBAAA,CAAoB,SAAA,EAAW,gBAAA,IACxC,OAAA;EAnaM;;;;;;EA6aH,IAAA,CAAK,OAAA,GAAU,iBAAA,GAAoB,OAAA;EA5aL;;;;EAgc9B,UAAA,CAAA,GAAc,OAAA;EA9bU;;;;;;;;;;;EA8fxB,YAAA,CAAa,EAAA,WAAa,OAAA;EAlaX;;;EAyaf,UAAA,CAAA,GAAc,OAAA;EAtUI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8YlB,OAAA,CACJ,QAAA,WACA,OAAA,GAAU,oBAAA,CAAqB,gBAAA,IAC9B,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyEG,UAAA,CACJ,aAAA,EAAe,MAAA,mBACf,OAAA,GAAU,uBAAA,CAAwB,gBAAA,IACjC,OAAA;;;;EAmCG,OAAA,CAAA,GAAW,OAAA;;;;;;;;;;;;;;;;EAyBjB,QAAA,CAAA;;;;;;EA4BA,SAAA,CAAA,GAAa,YAAA;;;;;;;;EAWb,eAAA,CACE,QAAA,GAAW,MAAA,EAAQ,YAAA;AAAA"}
@@ -1,6 +1,6 @@
1
1
  import { ensureMessageInstances } from "../ui/messages.js";
2
2
  import { ToolCallAssembler } from "../client/stream/handles/tools.js";
3
- import { buildResumeRunInput, resolveInterruptTargetForHeadlessResume } from "../headless-tools.js";
3
+ import { resolveInterruptTargetForHeadlessResume } from "../headless-tools.js";
4
4
  import { StreamStore } from "./store.js";
5
5
  import { ChannelRegistry } from "./channel-registry.js";
6
6
  import { isInternalWorkNamespace, isLegacySubagentNamespace, isRootNamespace } from "./namespace.js";
@@ -186,14 +186,6 @@ var StreamController = class {
186
186
  },
187
187
  waitForRootPumpReady: () => this.#rootPumpReady,
188
188
  awaitNextTerminal: (signal) => this.#awaitNextTerminal(signal),
189
- buildResumeRunInput: (resume) => {
190
- const thread = this.#thread;
191
- if (thread == null) return null;
192
- return buildResumeRunInput(resume, thread.interrupts, this.#resolvedInterrupts);
193
- },
194
- markInterruptResolved: (interruptId) => {
195
- this.#resolvedInterrupts.add(interruptId);
196
- },
197
189
  onSubmitStart: () => {
198
190
  this.#hydratedActiveInterruptIds = null;
199
191
  this.#submitGeneration += 1;
@@ -419,9 +411,11 @@ var StreamController = class {
419
411
  if (threadExists) thread.startLifecycleWatcher();
420
412
  }
421
413
  /**
422
- * Submit input or a resume command to the active thread.
414
+ * Submit input to the active thread.
415
+ *
416
+ * To resume a pending interrupt, use {@link respond} instead.
423
417
  *
424
- * @param input - Input payload for a new run; `null`/`undefined` submits no input.
418
+ * @param input - Input payload for a new run.
425
419
  * @param options - Per-run config, metadata, multitask behavior, and callbacks.
426
420
  */
427
421
  async submit(input, options) {
@@ -505,25 +499,152 @@ var StreamController = class {
505
499
  await this.#submitter.clearQueue();
506
500
  }
507
501
  /**
508
- * Respond to a pending protocol interrupt.
502
+ * Respond to a single pending protocol interrupt.
503
+ *
504
+ * When `options.interruptId` is omitted, resolution walks
505
+ * {@link ThreadStream.interrupts `thread.interrupts`} from newest to
506
+ * oldest and picks the first entry whose `interruptId` has not already
507
+ * been resolved by a prior `respond()` call. That entry may be at the
508
+ * root (`namespace: []`) or inside a subgraph (non-empty `namespace`).
509
+ * This is **not** the same as {@link RootSnapshot.interrupts
510
+ * `rootStore.interrupts[0]`} / framework `stream.interrupt`, which only
511
+ * mirrors root-namespace interrupts for UI convenience.
512
+ *
513
+ * Omitting `interruptId` is fine when exactly one interrupt is pending.
514
+ * When several can be active (parallel subagents, fan-out, nested
515
+ * graphs), pass an explicit `interruptId` (and `namespace` for subgraph
516
+ * interrupts) so you resume the interrupt the user acted on.
517
+ *
518
+ * To resume several interrupts pending at the same checkpoint in one
519
+ * command, use {@link respondAll} — sequential single `respond()` calls
520
+ * would not work, since the first resume starts a run, leaving the
521
+ * others with no interrupted run to respond to.
522
+ *
523
+ * The server validates `namespace` against the pending interrupt. Root
524
+ * interrupts use `namespace: []` (the default when `namespace` is
525
+ * omitted). Subgraph interrupts require the exact tuple from
526
+ * `getThread()?.interrupts` — see the example below.
527
+ *
528
+ * @param response - Payload sent back to the interrupted namespace.
529
+ * @param options - Optional target (`interruptId` / `namespace`) and
530
+ * run-level `config` / `metadata` folded into the run that services
531
+ * the resume (model/user config, trigger source, test flags, …).
532
+ * Equivalent to the same fields on {@link StreamSubmitOptions}.
533
+ *
534
+ * @example Single pending interrupt (safe to omit a target)
535
+ * ```ts
536
+ * await controller.respond({ approved: true });
537
+ * ```
538
+ *
539
+ * @example Carry run config / metadata onto the resume
540
+ * ```ts
541
+ * await controller.respond(
542
+ * { approved: true },
543
+ * { config: { configurable: { model: "gpt-4o" } }, metadata: { source: "ui" } },
544
+ * );
545
+ * ```
509
546
  *
510
- * @param response - Payload to send back to the interrupted namespace.
511
- * @param target - Optional explicit interrupt id and namespace; defaults to the latest unresolved interrupt.
547
+ * @example Multiple root interrupts target by id
548
+ * ```tsx
549
+ * for (const intr of stream.interrupts) {
550
+ * await stream.respond(decide(intr.value), { interruptId: intr.id! });
551
+ * }
552
+ * ```
553
+ *
554
+ * @example Subgraph interrupt — read `namespace` from the thread stream
555
+ * ```tsx
556
+ * const thread = stream.getThread();
557
+ * for (const entry of thread?.interrupts ?? []) {
558
+ * await stream.respond(buildResponse(entry.payload), {
559
+ * interruptId: entry.interruptId,
560
+ * namespace: entry.namespace,
561
+ * });
562
+ * }
563
+ * ```
564
+ *
565
+ * Each {@link InterruptPayload} on `thread.interrupts` mirrors an
566
+ * `input.requested` event: `{ interruptId, payload, namespace }`.
567
+ * Nested interrupts may appear here but not on `stream.interrupts`.
512
568
  */
513
- async respond(response, target) {
569
+ async respond(response, options) {
514
570
  if (this.#disposed || this.#thread == null) throw new Error("No active thread to respond to.");
515
- const resolved = target != null ? {
516
- interruptId: target.interruptId,
517
- namespace: target.namespace ?? [...ROOT_NAMESPACE]
571
+ const resolved = options?.interruptId != null ? {
572
+ interruptId: options.interruptId,
573
+ namespace: options.namespace ?? [...ROOT_NAMESPACE]
518
574
  } : this.#resolveInterruptForResume();
519
575
  if (resolved == null) throw new Error("No pending interrupt to respond to.");
520
576
  try {
521
577
  await this.#thread.respondInput({
522
578
  namespace: resolved.namespace,
523
579
  interrupt_id: resolved.interruptId,
524
- response
580
+ response,
581
+ config: options?.config,
582
+ metadata: options?.metadata
583
+ });
584
+ this.#markInterruptResolvedInRootStore(resolved.interruptId);
585
+ } catch (error) {
586
+ if (this.#disposed && isAbortLikeError(error)) return;
587
+ throw error;
588
+ }
589
+ }
590
+ /**
591
+ * Resume several pending interrupts at the same checkpoint in a single
592
+ * command.
593
+ *
594
+ * Required when a run pauses on multiple interrupts simultaneously
595
+ * (e.g. parallel tool-authorization prompts): a single
596
+ * `Command({ resume })` carrying every interrupt's payload resumes them
597
+ * together. Sequential {@link respond} calls would fail because the
598
+ * first resume starts a run, leaving the rest with no interrupted run to
599
+ * respond to.
600
+ *
601
+ * `responsesById` maps each pending `interruptId` to the payload sent
602
+ * back to it, so different interrupts can receive different responses
603
+ * (approve one, deny another). To send the *same* payload to several
604
+ * interrupts, build the map with that value for each id, e.g.
605
+ * `Object.fromEntries(ids.map((id) => [id, response]))`.
606
+ *
607
+ * The server resumes by `interruptId`, so namespaces are resolved
608
+ * internally from `getThread()?.interrupts` and need not be supplied.
609
+ *
610
+ * @param responsesById - Map of pending `interruptId` to its response
611
+ * payload. Must contain at least one entry.
612
+ * @param options - Optional run-level `config` / `metadata` folded into
613
+ * the single run that services the batched resume. Equivalent to the
614
+ * same fields on {@link StreamSubmitOptions}.
615
+ *
616
+ * @example Distinct payloads per interrupt
617
+ * ```tsx
618
+ * await stream.respondAll({
619
+ * [interruptA.id]: { approved: true },
620
+ * [interruptB.id]: { approved: false },
621
+ * });
622
+ * ```
623
+ *
624
+ * @example Same payload to every pending interrupt
625
+ * ```tsx
626
+ * await stream.respondAll(
627
+ * Object.fromEntries(stream.interrupts.map((i) => [i.id!, { approved: true }])),
628
+ * );
629
+ * ```
630
+ */
631
+ async respondAll(responsesById, options) {
632
+ if (this.#disposed || this.#thread == null) throw new Error("No active thread to respond to.");
633
+ const entries = Object.entries(responsesById);
634
+ if (entries.length === 0) throw new Error("respondAll() requires at least one response.");
635
+ const pending = this.#thread.interrupts;
636
+ const responses = entries.map(([interruptId, response]) => ({
637
+ interrupt_id: interruptId,
638
+ response,
639
+ namespace: pending.find((entry) => entry.interruptId === interruptId)?.namespace ?? [...ROOT_NAMESPACE]
640
+ }));
641
+ try {
642
+ await this.#thread.respondInput({
643
+ responses,
644
+ config: options?.config,
645
+ metadata: options?.metadata
525
646
  });
526
- this.#resolvedInterrupts.add(resolved.interruptId);
647
+ for (const { interrupt_id: interruptId } of responses) this.#markInterruptResolvedInRootStore(interruptId);
527
648
  } catch (error) {
528
649
  if (this.#disposed && isAbortLikeError(error)) return;
529
650
  throw error;
@@ -1061,6 +1182,22 @@ var StreamController = class {
1061
1182
  });
1062
1183
  }
1063
1184
  /**
1185
+ * Mark an interrupt resolved for replay filtering and mirror the
1186
+ * removal into the root snapshot the framework hooks read.
1187
+ */
1188
+ #markInterruptResolvedInRootStore(interruptId) {
1189
+ this.#resolvedInterrupts.add(interruptId);
1190
+ this.rootStore.setState((s) => {
1191
+ const interrupts = s.interrupts.filter((entry) => entry.id !== interruptId);
1192
+ if (interrupts.length === s.interrupts.length && s.interrupt?.id !== interruptId) return s;
1193
+ return {
1194
+ ...s,
1195
+ interrupts,
1196
+ interrupt: interrupts[0]
1197
+ };
1198
+ });
1199
+ }
1200
+ /**
1064
1201
  * Resolve on the next root-namespace terminal lifecycle event
1065
1202
  * (`completed` / `failed` / `interrupted`) or on abort.
1066
1203
  *