@langchain/langgraph-sdk 1.9.4 → 1.9.6

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 (110) hide show
  1. package/dist/client/base.cjs.map +1 -1
  2. package/dist/client/base.d.cts +37 -0
  3. package/dist/client/base.d.cts.map +1 -1
  4. package/dist/client/base.d.ts +37 -0
  5. package/dist/client/base.d.ts.map +1 -1
  6. package/dist/client/base.js.map +1 -1
  7. package/dist/client/index.d.cts +1 -1
  8. package/dist/client/index.d.ts +1 -1
  9. package/dist/client/stream/handles/index.d.ts +1 -1
  10. package/dist/client/stream/handles/subagents.cjs +1 -1
  11. package/dist/client/stream/handles/subagents.cjs.map +1 -1
  12. package/dist/client/stream/handles/subagents.d.cts +2 -2
  13. package/dist/client/stream/handles/subagents.d.cts.map +1 -1
  14. package/dist/client/stream/handles/subagents.d.ts +2 -2
  15. package/dist/client/stream/handles/subagents.d.ts.map +1 -1
  16. package/dist/client/stream/handles/subagents.js +2 -2
  17. package/dist/client/stream/handles/subagents.js.map +1 -1
  18. package/dist/client/stream/handles/subgraphs.cjs +1 -1
  19. package/dist/client/stream/handles/subgraphs.cjs.map +1 -1
  20. package/dist/client/stream/handles/subgraphs.d.cts +2 -2
  21. package/dist/client/stream/handles/subgraphs.d.cts.map +1 -1
  22. package/dist/client/stream/handles/subgraphs.d.ts +2 -2
  23. package/dist/client/stream/handles/subgraphs.d.ts.map +1 -1
  24. package/dist/client/stream/handles/subgraphs.js +2 -2
  25. package/dist/client/stream/handles/subgraphs.js.map +1 -1
  26. package/dist/client/stream/handles/tools.cjs +124 -52
  27. package/dist/client/stream/handles/tools.cjs.map +1 -1
  28. package/dist/client/stream/handles/tools.d.cts +59 -13
  29. package/dist/client/stream/handles/tools.d.cts.map +1 -1
  30. package/dist/client/stream/handles/tools.d.ts +59 -13
  31. package/dist/client/stream/handles/tools.d.ts.map +1 -1
  32. package/dist/client/stream/handles/tools.js +122 -53
  33. package/dist/client/stream/handles/tools.js.map +1 -1
  34. package/dist/client/stream/index.cjs +13 -5
  35. package/dist/client/stream/index.cjs.map +1 -1
  36. package/dist/client/stream/index.d.cts +3 -3
  37. package/dist/client/stream/index.d.cts.map +1 -1
  38. package/dist/client/stream/index.d.ts +3 -3
  39. package/dist/client/stream/index.d.ts.map +1 -1
  40. package/dist/client/stream/index.js +14 -6
  41. package/dist/client/stream/index.js.map +1 -1
  42. package/dist/headless-tools.cjs +131 -4
  43. package/dist/headless-tools.cjs.map +1 -1
  44. package/dist/headless-tools.d.cts +9 -1
  45. package/dist/headless-tools.d.cts.map +1 -1
  46. package/dist/headless-tools.d.ts +9 -1
  47. package/dist/headless-tools.d.ts.map +1 -1
  48. package/dist/headless-tools.js +129 -5
  49. package/dist/headless-tools.js.map +1 -1
  50. package/dist/index.cjs +1 -0
  51. package/dist/index.d.cts +3 -3
  52. package/dist/index.d.ts +3 -3
  53. package/dist/index.js +2 -2
  54. package/dist/stream/controller.cjs +77 -16
  55. package/dist/stream/controller.cjs.map +1 -1
  56. package/dist/stream/controller.d.cts +3 -1
  57. package/dist/stream/controller.d.cts.map +1 -1
  58. package/dist/stream/controller.d.ts +3 -1
  59. package/dist/stream/controller.d.ts.map +1 -1
  60. package/dist/stream/controller.js +78 -17
  61. package/dist/stream/controller.js.map +1 -1
  62. package/dist/stream/discovery/subagents.cjs +13 -0
  63. package/dist/stream/discovery/subagents.cjs.map +1 -1
  64. package/dist/stream/discovery/subagents.d.cts +5 -0
  65. package/dist/stream/discovery/subagents.d.cts.map +1 -1
  66. package/dist/stream/discovery/subagents.d.ts +5 -0
  67. package/dist/stream/discovery/subagents.d.ts.map +1 -1
  68. package/dist/stream/discovery/subagents.js +13 -0
  69. package/dist/stream/discovery/subagents.js.map +1 -1
  70. package/dist/stream/discovery/subgraphs.cjs +13 -0
  71. package/dist/stream/discovery/subgraphs.cjs.map +1 -1
  72. package/dist/stream/discovery/subgraphs.d.cts +5 -0
  73. package/dist/stream/discovery/subgraphs.d.cts.map +1 -1
  74. package/dist/stream/discovery/subgraphs.d.ts +5 -0
  75. package/dist/stream/discovery/subgraphs.d.ts.map +1 -1
  76. package/dist/stream/discovery/subgraphs.js +13 -0
  77. package/dist/stream/discovery/subgraphs.js.map +1 -1
  78. package/dist/stream/index.cjs +3 -0
  79. package/dist/stream/index.d.cts +5 -5
  80. package/dist/stream/index.d.ts +5 -5
  81. package/dist/stream/index.js +2 -1
  82. package/dist/stream/lifecycle-loading-tracker.cjs +1 -1
  83. package/dist/stream/lifecycle-loading-tracker.cjs.map +1 -1
  84. package/dist/stream/lifecycle-loading-tracker.js +1 -1
  85. package/dist/stream/lifecycle-loading-tracker.js.map +1 -1
  86. package/dist/stream/projections/tool-calls.cjs.map +1 -1
  87. package/dist/stream/projections/tool-calls.js.map +1 -1
  88. package/dist/stream/submit-coordinator.cjs +47 -16
  89. package/dist/stream/submit-coordinator.cjs.map +1 -1
  90. package/dist/stream/submit-coordinator.d.cts.map +1 -1
  91. package/dist/stream/submit-coordinator.d.ts.map +1 -1
  92. package/dist/stream/submit-coordinator.js +47 -16
  93. package/dist/stream/submit-coordinator.js.map +1 -1
  94. package/dist/stream/tool-calls.cjs +39 -2
  95. package/dist/stream/tool-calls.cjs.map +1 -1
  96. package/dist/stream/tool-calls.js +38 -3
  97. package/dist/stream/tool-calls.js.map +1 -1
  98. package/dist/stream/types-inference.d.cts +65 -7
  99. package/dist/stream/types-inference.d.cts.map +1 -1
  100. package/dist/stream/types-inference.d.ts +65 -7
  101. package/dist/stream/types-inference.d.ts.map +1 -1
  102. package/dist/stream/types.d.cts +42 -23
  103. package/dist/stream/types.d.cts.map +1 -1
  104. package/dist/stream/types.d.ts +42 -23
  105. package/dist/stream/types.d.ts.map +1 -1
  106. package/dist/types.messages.d.cts +38 -1
  107. package/dist/types.messages.d.cts.map +1 -1
  108. package/dist/types.messages.d.ts +38 -1
  109. package/dist/types.messages.d.ts.map +1 -1
  110. package/package.json +1 -1
@@ -5,22 +5,68 @@ import { ToolsEvent } from "@langchain/protocol";
5
5
  * High-level outcome of a single tool call.
6
6
  */
7
7
  type ToolCallStatus = "running" | "finished" | "error";
8
+ /** Shared metadata for assembled tool-call handles. */
9
+ interface ToolCallBase<TName extends string = string, TInput = unknown> {
10
+ readonly name: TName;
11
+ readonly callId: string;
12
+ /**
13
+ * Pre-v1 alias for {@link callId}. Matches `ToolCallWithResult.id` and
14
+ * `ToolCall.id` on message-level tool calls.
15
+ */
16
+ readonly id: string;
17
+ readonly namespace: string[];
18
+ readonly input: TInput;
19
+ /**
20
+ * Pre-v1 alias for {@link input}. Matches `ToolCallFromTool` `args`.
21
+ */
22
+ readonly args: TInput;
23
+ }
24
+ /**
25
+ * Script-oriented tool handle from the client SDK (`ThreadStream.toolCalls`,
26
+ * subagent/subgraph projections). Completion and errors are surfaced only
27
+ * through {@link output}.
28
+ */
29
+ interface ClientAssembledToolCall<TName extends string = string, TInput = unknown, TOutput = unknown> extends ToolCallBase<TName, TInput> {
30
+ readonly output: Promise<TOutput>;
31
+ }
8
32
  /**
9
- * Assembled view of a single tool call lifecycle (`tool-started` →
10
- * optional `tool-output-delta` `tool-finished` | `tool-error`).
33
+ * Reactive tool handle for framework bindings (`stream.toolCalls`,
34
+ * `useToolCalls`, `injectToolCalls`).
11
35
  *
12
- * Mirrors the in-process `ToolCallStream` interface so that remote
13
- * consumers get the same ergonomics.
36
+ * {@link status}, {@link error}, and {@link output} are plain values that
37
+ * the assembler updates in place as tool events arrive. That lets React,
38
+ * Vue, Svelte, and Angular re-render from a snapshot on each store tick
39
+ * without `await`, effects, or Suspense boundaries around a promise.
40
+ * {@link ClientAssembledToolCall} keeps a promise-based {@link output}
41
+ * instead for script consumers that read tool results sequentially.
42
+ *
43
+ * {@link output} is `null` while the call is running or after it fails;
44
+ * successful completion sets it to the parsed tool return value (objects
45
+ * and strings are unwrapped from ToolMessage wire envelopes when needed).
14
46
  */
15
- interface AssembledToolCall {
16
- readonly name: string;
17
- readonly callId: string;
18
- readonly namespace: string[];
19
- readonly input: unknown;
20
- readonly output: Promise<unknown>;
21
- readonly status: Promise<ToolCallStatus>;
22
- readonly error: Promise<string | undefined>;
47
+ interface AssembledToolCall<TName extends string = string, TInput = unknown, TOutput = unknown> extends ToolCallBase<TName, TInput> {
48
+ readonly output: TOutput | null;
49
+ readonly status: ToolCallStatus;
50
+ readonly error: string | undefined;
23
51
  }
52
+ /**
53
+ * Parse wire-format tool payloads into structured values.
54
+ *
55
+ * Tool events may carry JSON-encoded object strings on the wire; this
56
+ * helper normalises them to plain objects for consumers. Non-JSON strings
57
+ * are returned unchanged.
58
+ */
59
+ declare function parseToolPayload(value: unknown): unknown;
60
+ /**
61
+ * Parse a `tool-finished` output payload into the tool's return value.
62
+ *
63
+ * Wire events often wrap structured tool results in a ToolMessage-shaped
64
+ * object (`{ type: "tool", content: "..." }`). This unwraps that envelope,
65
+ * JSON-decodes string content when possible, and leaves plain strings as-is.
66
+ * Returns `null` when a ToolMessage envelope is present but its content
67
+ * cannot be normalised.
68
+ */
69
+ declare function parseToolOutput(value: unknown): unknown | null;
24
70
  //#endregion
25
- export { AssembledToolCall, ToolCallStatus };
71
+ export { AssembledToolCall, ClientAssembledToolCall, ToolCallStatus, parseToolOutput, parseToolPayload };
26
72
  //# sourceMappingURL=tools.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.cts","names":[],"sources":["../../../../src/client/stream/handles/tools.ts"],"mappings":";;;;;AAUA;KAAY,cAAA;;;;AASZ;;;;UAAiB,iBAAA;EAAA,SACN,IAAA;EAAA,SACA,MAAA;EAAA,SACA,SAAA;EAAA,SACA,KAAA;EAAA,SACA,MAAA,EAAQ,OAAA;EAAA,SACR,MAAA,EAAQ,OAAA,CAAQ,cAAA;EAAA,SAChB,KAAA,EAAO,OAAA;AAAA"}
1
+ {"version":3,"file":"tools.d.cts","names":[],"sources":["../../../../src/client/stream/handles/tools.ts"],"mappings":";;;;;AAUA;KAAY,cAAA;;UAGF,YAAA;EAAA,SACC,IAAA,EAAM,KAAA;EAAA,SACN,MAAA;EAFW;;;;EAAA,SAOX,EAAA;EAAA,SACA,SAAA;EAAA,SACA,KAAA,EAAO,MAAA;EATK;;;EAAA,SAaZ,IAAA,EAAM,MAAA;AAAA;;;;;;UAQA,uBAAA,6EAIP,YAAA,CAAa,KAAA,EAAO,MAAA;EAAA,SACnB,MAAA,EAAQ,OAAA,CAAQ,OAAA;AAAA;AAL3B;;;;;;;;;;;;;;;AAAA,UAuBiB,iBAAA,6EAIP,YAAA,CAAa,KAAA,EAAO,MAAA;EAAA,SACnB,MAAA,EAAQ,OAAA;EAAA,SACR,MAAA,EAAQ,cAAA;EAAA,SACR,KAAA;AAAA;;;;;;;AA0CX;iBAAgB,gBAAA,CAAiB,KAAA;;;;AAsEjC;;;;;;iBAAgB,eAAA,CAAgB,KAAA"}
@@ -5,22 +5,68 @@ import { ToolsEvent } from "@langchain/protocol";
5
5
  * High-level outcome of a single tool call.
6
6
  */
7
7
  type ToolCallStatus = "running" | "finished" | "error";
8
+ /** Shared metadata for assembled tool-call handles. */
9
+ interface ToolCallBase<TName extends string = string, TInput = unknown> {
10
+ readonly name: TName;
11
+ readonly callId: string;
12
+ /**
13
+ * Pre-v1 alias for {@link callId}. Matches `ToolCallWithResult.id` and
14
+ * `ToolCall.id` on message-level tool calls.
15
+ */
16
+ readonly id: string;
17
+ readonly namespace: string[];
18
+ readonly input: TInput;
19
+ /**
20
+ * Pre-v1 alias for {@link input}. Matches `ToolCallFromTool` `args`.
21
+ */
22
+ readonly args: TInput;
23
+ }
24
+ /**
25
+ * Script-oriented tool handle from the client SDK (`ThreadStream.toolCalls`,
26
+ * subagent/subgraph projections). Completion and errors are surfaced only
27
+ * through {@link output}.
28
+ */
29
+ interface ClientAssembledToolCall<TName extends string = string, TInput = unknown, TOutput = unknown> extends ToolCallBase<TName, TInput> {
30
+ readonly output: Promise<TOutput>;
31
+ }
8
32
  /**
9
- * Assembled view of a single tool call lifecycle (`tool-started` →
10
- * optional `tool-output-delta` `tool-finished` | `tool-error`).
33
+ * Reactive tool handle for framework bindings (`stream.toolCalls`,
34
+ * `useToolCalls`, `injectToolCalls`).
11
35
  *
12
- * Mirrors the in-process `ToolCallStream` interface so that remote
13
- * consumers get the same ergonomics.
36
+ * {@link status}, {@link error}, and {@link output} are plain values that
37
+ * the assembler updates in place as tool events arrive. That lets React,
38
+ * Vue, Svelte, and Angular re-render from a snapshot on each store tick
39
+ * without `await`, effects, or Suspense boundaries around a promise.
40
+ * {@link ClientAssembledToolCall} keeps a promise-based {@link output}
41
+ * instead for script consumers that read tool results sequentially.
42
+ *
43
+ * {@link output} is `null` while the call is running or after it fails;
44
+ * successful completion sets it to the parsed tool return value (objects
45
+ * and strings are unwrapped from ToolMessage wire envelopes when needed).
14
46
  */
15
- interface AssembledToolCall {
16
- readonly name: string;
17
- readonly callId: string;
18
- readonly namespace: string[];
19
- readonly input: unknown;
20
- readonly output: Promise<unknown>;
21
- readonly status: Promise<ToolCallStatus>;
22
- readonly error: Promise<string | undefined>;
47
+ interface AssembledToolCall<TName extends string = string, TInput = unknown, TOutput = unknown> extends ToolCallBase<TName, TInput> {
48
+ readonly output: TOutput | null;
49
+ readonly status: ToolCallStatus;
50
+ readonly error: string | undefined;
23
51
  }
52
+ /**
53
+ * Parse wire-format tool payloads into structured values.
54
+ *
55
+ * Tool events may carry JSON-encoded object strings on the wire; this
56
+ * helper normalises them to plain objects for consumers. Non-JSON strings
57
+ * are returned unchanged.
58
+ */
59
+ declare function parseToolPayload(value: unknown): unknown;
60
+ /**
61
+ * Parse a `tool-finished` output payload into the tool's return value.
62
+ *
63
+ * Wire events often wrap structured tool results in a ToolMessage-shaped
64
+ * object (`{ type: "tool", content: "..." }`). This unwraps that envelope,
65
+ * JSON-decodes string content when possible, and leaves plain strings as-is.
66
+ * Returns `null` when a ToolMessage envelope is present but its content
67
+ * cannot be normalised.
68
+ */
69
+ declare function parseToolOutput(value: unknown): unknown | null;
24
70
  //#endregion
25
- export { AssembledToolCall, ToolCallStatus };
71
+ export { AssembledToolCall, ClientAssembledToolCall, ToolCallStatus, parseToolOutput, parseToolPayload };
26
72
  //# sourceMappingURL=tools.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","names":[],"sources":["../../../../src/client/stream/handles/tools.ts"],"mappings":";;;;;AAUA;KAAY,cAAA;;;;AASZ;;;;UAAiB,iBAAA;EAAA,SACN,IAAA;EAAA,SACA,MAAA;EAAA,SACA,SAAA;EAAA,SACA,KAAA;EAAA,SACA,MAAA,EAAQ,OAAA;EAAA,SACR,MAAA,EAAQ,OAAA,CAAQ,cAAA;EAAA,SAChB,KAAA,EAAO,OAAA;AAAA"}
1
+ {"version":3,"file":"tools.d.ts","names":[],"sources":["../../../../src/client/stream/handles/tools.ts"],"mappings":";;;;;AAUA;KAAY,cAAA;;UAGF,YAAA;EAAA,SACC,IAAA,EAAM,KAAA;EAAA,SACN,MAAA;EAFW;;;;EAAA,SAOX,EAAA;EAAA,SACA,SAAA;EAAA,SACA,KAAA,EAAO,MAAA;EATK;;;EAAA,SAaZ,IAAA,EAAM,MAAA;AAAA;;;;;;UAQA,uBAAA,6EAIP,YAAA,CAAa,KAAA,EAAO,MAAA;EAAA,SACnB,MAAA,EAAQ,OAAA,CAAQ,OAAA;AAAA;AAL3B;;;;;;;;;;;;;;;AAAA,UAuBiB,iBAAA,6EAIP,YAAA,CAAa,KAAA,EAAO,MAAA;EAAA,SACnB,MAAA,EAAQ,OAAA;EAAA,SACR,MAAA,EAAQ,cAAA;EAAA,SACR,KAAA;AAAA;;;;;;;AA0CX;iBAAgB,gBAAA,CAAiB,KAAA;;;;AAsEjC;;;;;;iBAAgB,eAAA,CAAgB,KAAA"}
@@ -1,92 +1,161 @@
1
1
  //#region src/client/stream/handles/tools.ts
2
2
  /**
3
- * Incrementally assembles `tools` events into complete
4
- * {@link AssembledToolCall} objects with promise-based output/status/error.
3
+ * Project a runtime handle to the client SDK surface (promise-only
4
+ * {@link output}, no {@link status} / {@link error} fields).
5
+ */
6
+ function toClientAssembledToolCall(handle) {
7
+ return {
8
+ name: handle.name,
9
+ callId: handle.callId,
10
+ id: handle.id,
11
+ namespace: handle.namespace,
12
+ input: handle.input,
13
+ args: handle.args,
14
+ output: handle.outputPromise
15
+ };
16
+ }
17
+ /**
18
+ * Parse wire-format tool payloads into structured values.
19
+ *
20
+ * Tool events may carry JSON-encoded object strings on the wire; this
21
+ * helper normalises them to plain objects for consumers. Non-JSON strings
22
+ * are returned unchanged.
23
+ */
24
+ function parseToolPayload(value) {
25
+ if (typeof value !== "string") return value;
26
+ const trimmed = value.trim();
27
+ if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return value;
28
+ try {
29
+ return JSON.parse(trimmed);
30
+ } catch {
31
+ return value;
32
+ }
33
+ }
34
+ function isToolMessageLike(value) {
35
+ if (!value || typeof value !== "object") return false;
36
+ const record = value;
37
+ if (record.type === "tool") return true;
38
+ return typeof record.tool_call_id === "string" && "content" in record;
39
+ }
40
+ function textFromContentBlocks(content) {
41
+ let out = "";
42
+ for (const block of content) {
43
+ if (!block || typeof block !== "object") continue;
44
+ const record = block;
45
+ if (record.type === "text" && typeof record.text === "string") out += record.text;
46
+ }
47
+ return out;
48
+ }
49
+ /**
50
+ * Normalise tool-result `content` from a wire ToolMessage into the value
51
+ * a tool implementation returned (object, string, etc.).
52
+ */
53
+ function parseToolResultContent(content) {
54
+ if (content == null) return null;
55
+ if (typeof content === "string") {
56
+ if (content.trim().length === 0) return null;
57
+ return parseToolPayload(content);
58
+ }
59
+ if (Array.isArray(content)) {
60
+ const text = textFromContentBlocks(content);
61
+ if (text.length === 0) return null;
62
+ return parseToolPayload(text);
63
+ }
64
+ if (typeof content === "object") return content;
65
+ return null;
66
+ }
67
+ /**
68
+ * Parse a `tool-finished` output payload into the tool's return value.
5
69
  *
6
- * Each `tool-started` event produces an {@link AssembledToolCall} whose
7
- * `output`, `status`, and `error` promises resolve when `tool-finished`
8
- * or `tool-error` arrives for the same `tool_call_id`.
70
+ * Wire events often wrap structured tool results in a ToolMessage-shaped
71
+ * object (`{ type: "tool", content: "..." }`). This unwraps that envelope,
72
+ * JSON-decodes string content when possible, and leaves plain strings as-is.
73
+ * Returns `null` when a ToolMessage envelope is present but its content
74
+ * cannot be normalised.
75
+ */
76
+ function parseToolOutput(value) {
77
+ const parsed = parseToolPayload(value);
78
+ if (isToolMessageLike(parsed)) return parseToolResultContent(parsed.content);
79
+ return parsed ?? null;
80
+ }
81
+ /**
82
+ * Incrementally assembles `tools` events into mutable tool-call handles.
83
+ *
84
+ * Framework consumers store the handle directly; client SDK consumers
85
+ * should map with {@link toClientAssembledToolCall} before yielding.
9
86
  */
10
87
  var ToolCallAssembler = class {
11
88
  active = /* @__PURE__ */ new Map();
12
89
  consume(event) {
13
90
  const data = event.params.data;
14
91
  if (data.event === "tool-started") return this.handleStarted(event, data);
15
- if (data.event === "tool-finished") {
16
- this.handleFinished(data);
17
- return;
18
- }
19
- if (data.event === "tool-error") {
20
- this.handleError(data);
21
- return;
22
- }
92
+ if (data.event === "tool-finished") return this.handleFinished(data);
93
+ if (data.event === "tool-error") return this.handleError(data);
23
94
  }
24
95
  /**
25
96
  * Reject any in-flight tool calls (e.g. on session close).
26
97
  */
27
98
  failAll(reason) {
28
- for (const tc of this.active.values()) {
29
- tc.rejectOutput(reason);
30
- tc.resolveStatus("error");
31
- tc.resolveError(reason.message);
99
+ for (const entry of this.active.values()) {
100
+ entry.rejectOutput(reason);
101
+ entry.handle.status = "error";
102
+ entry.handle.error = reason.message;
32
103
  }
33
104
  this.active.clear();
34
105
  }
35
106
  handleStarted(event, data) {
36
107
  let resolveOutput;
37
108
  let rejectOutput;
38
- let resolveStatus;
39
- let resolveError;
40
- const output = new Promise((resolve, reject) => {
109
+ const outputPromise = new Promise((resolve, reject) => {
41
110
  resolveOutput = resolve;
42
111
  rejectOutput = reject;
43
112
  });
44
- output.catch(() => void 0);
45
- const status = new Promise((resolve) => {
46
- resolveStatus = resolve;
47
- });
48
- const error = new Promise((resolve) => {
49
- resolveError = resolve;
50
- });
51
- const entry = {
52
- name: data.tool_name,
53
- callId: data.tool_call_id,
113
+ outputPromise.catch(() => void 0);
114
+ const input = parseToolPayload(data.input);
115
+ const name = data.tool_name;
116
+ const callId = data.tool_call_id;
117
+ const handle = {
118
+ name,
119
+ callId,
120
+ id: callId,
54
121
  namespace: [...event.params.namespace],
55
- input: data.input,
56
- resolveOutput,
57
- rejectOutput,
58
- resolveStatus,
59
- resolveError
60
- };
61
- this.active.set(data.tool_call_id, entry);
62
- return {
63
- name: entry.name,
64
- callId: entry.callId,
65
- namespace: entry.namespace,
66
- input: entry.input,
67
- output,
68
- status,
69
- error
122
+ input,
123
+ args: input,
124
+ output: null,
125
+ status: "running",
126
+ error: void 0,
127
+ outputPromise
70
128
  };
129
+ this.active.set(callId, {
130
+ handle,
131
+ resolveOutput,
132
+ rejectOutput
133
+ });
134
+ return handle;
71
135
  }
72
136
  handleFinished(data) {
73
137
  const entry = this.active.get(data.tool_call_id);
74
- if (!entry) return;
138
+ if (!entry) return void 0;
75
139
  this.active.delete(data.tool_call_id);
76
- entry.resolveOutput(data.output);
77
- entry.resolveStatus("finished");
78
- entry.resolveError(void 0);
140
+ const value = parseToolOutput(data.output);
141
+ entry.resolveOutput(value);
142
+ entry.handle.output = value;
143
+ entry.handle.status = "finished";
144
+ entry.handle.error = void 0;
145
+ return entry.handle;
79
146
  }
80
147
  handleError(data) {
81
148
  const entry = this.active.get(data.tool_call_id);
82
- if (!entry) return;
149
+ if (!entry) return void 0;
83
150
  this.active.delete(data.tool_call_id);
84
151
  entry.rejectOutput(new Error(data.message));
85
- entry.resolveStatus("error");
86
- entry.resolveError(data.message);
152
+ entry.handle.output = null;
153
+ entry.handle.status = "error";
154
+ entry.handle.error = data.message;
155
+ return entry.handle;
87
156
  }
88
157
  };
89
158
  //#endregion
90
- export { ToolCallAssembler };
159
+ export { ToolCallAssembler, parseToolOutput, parseToolPayload, toClientAssembledToolCall };
91
160
 
92
161
  //# sourceMappingURL=tools.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tools.js","names":[],"sources":["../../../../src/client/stream/handles/tools.ts"],"sourcesContent":["import type {\n ToolsEvent,\n ToolStartedData,\n ToolFinishedData,\n ToolErrorData,\n} from \"@langchain/protocol\";\n\n/**\n * High-level outcome of a single tool call.\n */\nexport type ToolCallStatus = \"running\" | \"finished\" | \"error\";\n\n/**\n * Assembled view of a single tool call lifecycle (`tool-started` →\n * optional `tool-output-delta` → `tool-finished` | `tool-error`).\n *\n * Mirrors the in-process `ToolCallStream` interface so that remote\n * consumers get the same ergonomics.\n */\nexport interface AssembledToolCall {\n readonly name: string;\n readonly callId: string;\n readonly namespace: string[];\n readonly input: unknown;\n readonly output: Promise<unknown>;\n readonly status: Promise<ToolCallStatus>;\n readonly error: Promise<string | undefined>;\n}\n\ntype ActiveToolCall = {\n name: string;\n callId: string;\n namespace: string[];\n input: unknown;\n resolveOutput: (value: unknown) => void;\n rejectOutput: (err: Error) => void;\n resolveStatus: (value: ToolCallStatus) => void;\n resolveError: (value: string | undefined) => void;\n};\n\n/**\n * Incrementally assembles `tools` events into complete\n * {@link AssembledToolCall} objects with promise-based output/status/error.\n *\n * Each `tool-started` event produces an {@link AssembledToolCall} whose\n * `output`, `status`, and `error` promises resolve when `tool-finished`\n * or `tool-error` arrives for the same `tool_call_id`.\n */\nexport class ToolCallAssembler {\n private readonly active = new Map<string, ActiveToolCall>();\n\n consume(event: ToolsEvent): AssembledToolCall | undefined {\n const data = event.params.data;\n\n if (data.event === \"tool-started\") {\n return this.handleStarted(event, data);\n }\n\n if (data.event === \"tool-finished\") {\n this.handleFinished(data);\n return undefined;\n }\n\n if (data.event === \"tool-error\") {\n this.handleError(data);\n return undefined;\n }\n\n // tool-output-delta: no action needed at assembly level\n return undefined;\n }\n\n /**\n * Reject any in-flight tool calls (e.g. on session close).\n */\n failAll(reason: Error): void {\n for (const tc of this.active.values()) {\n tc.rejectOutput(reason);\n tc.resolveStatus(\"error\");\n tc.resolveError(reason.message);\n }\n this.active.clear();\n }\n\n private handleStarted(\n event: ToolsEvent,\n data: ToolStartedData\n ): AssembledToolCall {\n let resolveOutput!: (value: unknown) => void;\n let rejectOutput!: (err: Error) => void;\n let resolveStatus!: (value: ToolCallStatus) => void;\n let resolveError!: (value: string | undefined) => void;\n\n const output = new Promise<unknown>((resolve, reject) => {\n resolveOutput = resolve;\n rejectOutput = reject;\n });\n // Attach a default no-op catch so if no consumer awaits\n // `output` the eventual rejection on `tool-error` / `failAll`\n // doesn't surface as an unhandled Promise rejection.\n output.catch(() => undefined);\n const status = new Promise<ToolCallStatus>((resolve) => {\n resolveStatus = resolve;\n });\n const error = new Promise<string | undefined>((resolve) => {\n resolveError = resolve;\n });\n\n const entry: ActiveToolCall = {\n name: data.tool_name,\n callId: data.tool_call_id,\n namespace: [...event.params.namespace],\n input: data.input,\n resolveOutput,\n rejectOutput,\n resolveStatus,\n resolveError,\n };\n this.active.set(data.tool_call_id, entry);\n\n return {\n name: entry.name,\n callId: entry.callId,\n namespace: entry.namespace,\n input: entry.input,\n output,\n status,\n error,\n };\n }\n\n private handleFinished(data: ToolFinishedData): void {\n const entry = this.active.get(data.tool_call_id);\n if (!entry) return;\n this.active.delete(data.tool_call_id);\n entry.resolveOutput(data.output);\n entry.resolveStatus(\"finished\");\n entry.resolveError(undefined);\n }\n\n private handleError(data: ToolErrorData): void {\n const entry = this.active.get(data.tool_call_id);\n if (!entry) return;\n this.active.delete(data.tool_call_id);\n entry.rejectOutput(new Error(data.message));\n entry.resolveStatus(\"error\");\n entry.resolveError(data.message);\n }\n}\n"],"mappings":";;;;;;;;;AAgDA,IAAa,oBAAb,MAA+B;CAC7B,yBAA0B,IAAI,KAA6B;CAE3D,QAAQ,OAAkD;EACxD,MAAM,OAAO,MAAM,OAAO;AAE1B,MAAI,KAAK,UAAU,eACjB,QAAO,KAAK,cAAc,OAAO,KAAK;AAGxC,MAAI,KAAK,UAAU,iBAAiB;AAClC,QAAK,eAAe,KAAK;AACzB;;AAGF,MAAI,KAAK,UAAU,cAAc;AAC/B,QAAK,YAAY,KAAK;AACtB;;;;;;CAUJ,QAAQ,QAAqB;AAC3B,OAAK,MAAM,MAAM,KAAK,OAAO,QAAQ,EAAE;AACrC,MAAG,aAAa,OAAO;AACvB,MAAG,cAAc,QAAQ;AACzB,MAAG,aAAa,OAAO,QAAQ;;AAEjC,OAAK,OAAO,OAAO;;CAGrB,cACE,OACA,MACmB;EACnB,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EAEJ,MAAM,SAAS,IAAI,SAAkB,SAAS,WAAW;AACvD,mBAAgB;AAChB,kBAAe;IACf;AAIF,SAAO,YAAY,KAAA,EAAU;EAC7B,MAAM,SAAS,IAAI,SAAyB,YAAY;AACtD,mBAAgB;IAChB;EACF,MAAM,QAAQ,IAAI,SAA6B,YAAY;AACzD,kBAAe;IACf;EAEF,MAAM,QAAwB;GAC5B,MAAM,KAAK;GACX,QAAQ,KAAK;GACb,WAAW,CAAC,GAAG,MAAM,OAAO,UAAU;GACtC,OAAO,KAAK;GACZ;GACA;GACA;GACA;GACD;AACD,OAAK,OAAO,IAAI,KAAK,cAAc,MAAM;AAEzC,SAAO;GACL,MAAM,MAAM;GACZ,QAAQ,MAAM;GACd,WAAW,MAAM;GACjB,OAAO,MAAM;GACb;GACA;GACA;GACD;;CAGH,eAAuB,MAA8B;EACnD,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,aAAa;AAChD,MAAI,CAAC,MAAO;AACZ,OAAK,OAAO,OAAO,KAAK,aAAa;AACrC,QAAM,cAAc,KAAK,OAAO;AAChC,QAAM,cAAc,WAAW;AAC/B,QAAM,aAAa,KAAA,EAAU;;CAG/B,YAAoB,MAA2B;EAC7C,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,aAAa;AAChD,MAAI,CAAC,MAAO;AACZ,OAAK,OAAO,OAAO,KAAK,aAAa;AACrC,QAAM,aAAa,IAAI,MAAM,KAAK,QAAQ,CAAC;AAC3C,QAAM,cAAc,QAAQ;AAC5B,QAAM,aAAa,KAAK,QAAQ"}
1
+ {"version":3,"file":"tools.js","names":[],"sources":["../../../../src/client/stream/handles/tools.ts"],"sourcesContent":["import type {\n ToolsEvent,\n ToolStartedData,\n ToolFinishedData,\n ToolErrorData,\n} from \"@langchain/protocol\";\n\n/**\n * High-level outcome of a single tool call.\n */\nexport type ToolCallStatus = \"running\" | \"finished\" | \"error\";\n\n/** Shared metadata for assembled tool-call handles. */\ninterface ToolCallBase<TName extends string = string, TInput = unknown> {\n readonly name: TName;\n readonly callId: string;\n /**\n * Pre-v1 alias for {@link callId}. Matches `ToolCallWithResult.id` and\n * `ToolCall.id` on message-level tool calls.\n */\n readonly id: string;\n readonly namespace: string[];\n readonly input: TInput;\n /**\n * Pre-v1 alias for {@link input}. Matches `ToolCallFromTool` `args`.\n */\n readonly args: TInput;\n}\n\n/**\n * Script-oriented tool handle from the client SDK (`ThreadStream.toolCalls`,\n * subagent/subgraph projections). Completion and errors are surfaced only\n * through {@link output}.\n */\nexport interface ClientAssembledToolCall<\n TName extends string = string,\n TInput = unknown,\n TOutput = unknown,\n> extends ToolCallBase<TName, TInput> {\n readonly output: Promise<TOutput>;\n}\n\n/**\n * Reactive tool handle for framework bindings (`stream.toolCalls`,\n * `useToolCalls`, `injectToolCalls`).\n *\n * {@link status}, {@link error}, and {@link output} are plain values that\n * the assembler updates in place as tool events arrive. That lets React,\n * Vue, Svelte, and Angular re-render from a snapshot on each store tick\n * without `await`, effects, or Suspense boundaries around a promise.\n * {@link ClientAssembledToolCall} keeps a promise-based {@link output}\n * instead for script consumers that read tool results sequentially.\n *\n * {@link output} is `null` while the call is running or after it fails;\n * successful completion sets it to the parsed tool return value (objects\n * and strings are unwrapped from ToolMessage wire envelopes when needed).\n */\nexport interface AssembledToolCall<\n TName extends string = string,\n TInput = unknown,\n TOutput = unknown,\n> extends ToolCallBase<TName, TInput> {\n readonly output: TOutput | null;\n readonly status: ToolCallStatus;\n readonly error: string | undefined;\n}\n\n/** @internal Mutable runtime handle shared by client and framework views. */\ntype MutableToolCallHandle = {\n name: string;\n callId: string;\n id: string;\n namespace: string[];\n input: unknown;\n args: unknown;\n output: unknown | null;\n status: ToolCallStatus;\n error: string | undefined;\n outputPromise: Promise<unknown>;\n};\n\n/**\n * Project a runtime handle to the client SDK surface (promise-only\n * {@link output}, no {@link status} / {@link error} fields).\n */\nexport function toClientAssembledToolCall(\n handle: MutableToolCallHandle\n): ClientAssembledToolCall {\n return {\n name: handle.name,\n callId: handle.callId,\n id: handle.id,\n namespace: handle.namespace,\n input: handle.input,\n args: handle.args,\n output: handle.outputPromise as Promise<unknown>,\n };\n}\n\n/**\n * Parse wire-format tool payloads into structured values.\n *\n * Tool events may carry JSON-encoded object strings on the wire; this\n * helper normalises them to plain objects for consumers. Non-JSON strings\n * are returned unchanged.\n */\nexport function parseToolPayload(value: unknown): unknown {\n if (typeof value !== \"string\") return value;\n const trimmed = value.trim();\n if (!trimmed.startsWith(\"{\") && !trimmed.startsWith(\"[\")) {\n return value;\n }\n\n try {\n return JSON.parse(trimmed) as unknown;\n } catch {\n return value;\n }\n}\n\nfunction isToolMessageLike(\n value: unknown\n): value is Record<string, unknown> & { content: unknown } {\n if (!value || typeof value !== \"object\") return false;\n const record = value as Record<string, unknown>;\n if (record.type === \"tool\") return true;\n return typeof record.tool_call_id === \"string\" && \"content\" in record;\n}\n\nfunction textFromContentBlocks(content: unknown[]): string {\n let out = \"\";\n for (const block of content) {\n if (!block || typeof block !== \"object\") continue;\n const record = block as Record<string, unknown>;\n if (record.type === \"text\" && typeof record.text === \"string\") {\n out += record.text;\n }\n }\n return out;\n}\n\n/**\n * Normalise tool-result `content` from a wire ToolMessage into the value\n * a tool implementation returned (object, string, etc.).\n */\nfunction parseToolResultContent(content: unknown): unknown | null {\n if (content == null) return null;\n\n if (typeof content === \"string\") {\n const trimmed = content.trim();\n if (trimmed.length === 0) return null;\n return parseToolPayload(content);\n }\n\n if (Array.isArray(content)) {\n const text = textFromContentBlocks(content);\n if (text.length === 0) return null;\n return parseToolPayload(text);\n }\n\n if (typeof content === \"object\") {\n return content;\n }\n\n return null;\n}\n\n/**\n * Parse a `tool-finished` output payload into the tool's return value.\n *\n * Wire events often wrap structured tool results in a ToolMessage-shaped\n * object (`{ type: \"tool\", content: \"...\" }`). This unwraps that envelope,\n * JSON-decodes string content when possible, and leaves plain strings as-is.\n * Returns `null` when a ToolMessage envelope is present but its content\n * cannot be normalised.\n */\nexport function parseToolOutput(value: unknown): unknown | null {\n const parsed = parseToolPayload(value);\n if (isToolMessageLike(parsed)) {\n return parseToolResultContent(parsed.content);\n }\n return parsed ?? null;\n}\n\ntype ActiveToolCall = {\n handle: MutableToolCallHandle;\n resolveOutput: (value: unknown) => void;\n rejectOutput: (err: Error) => void;\n};\n\n/**\n * Incrementally assembles `tools` events into mutable tool-call handles.\n *\n * Framework consumers store the handle directly; client SDK consumers\n * should map with {@link toClientAssembledToolCall} before yielding.\n */\nexport class ToolCallAssembler {\n private readonly active = new Map<string, ActiveToolCall>();\n\n consume(event: ToolsEvent): MutableToolCallHandle | undefined {\n const data = event.params.data;\n\n if (data.event === \"tool-started\") {\n return this.handleStarted(event, data);\n }\n\n if (data.event === \"tool-finished\") {\n return this.handleFinished(data);\n }\n\n if (data.event === \"tool-error\") {\n return this.handleError(data);\n }\n\n // tool-output-delta: no action needed at assembly level\n return undefined;\n }\n\n /**\n * Reject any in-flight tool calls (e.g. on session close).\n */\n failAll(reason: Error): void {\n for (const entry of this.active.values()) {\n entry.rejectOutput(reason);\n entry.handle.status = \"error\";\n entry.handle.error = reason.message;\n }\n this.active.clear();\n }\n\n private handleStarted(\n event: ToolsEvent,\n data: ToolStartedData\n ): MutableToolCallHandle {\n let resolveOutput!: (value: unknown) => void;\n let rejectOutput!: (err: Error) => void;\n\n const outputPromise = new Promise<unknown>((resolve, reject) => {\n resolveOutput = resolve;\n rejectOutput = reject;\n });\n // Attach a default no-op catch so if no consumer awaits\n // `output` the eventual rejection on `tool-error` / `failAll`\n // doesn't surface as an unhandled Promise rejection.\n outputPromise.catch(() => undefined);\n\n const input = parseToolPayload(data.input);\n const name = data.tool_name;\n const callId = data.tool_call_id;\n const namespace = [...event.params.namespace];\n\n const handle: MutableToolCallHandle = {\n name,\n callId,\n id: callId,\n namespace,\n input,\n args: input,\n output: null,\n status: \"running\",\n error: undefined,\n outputPromise,\n };\n\n this.active.set(callId, {\n handle,\n resolveOutput,\n rejectOutput,\n });\n\n return handle;\n }\n\n private handleFinished(\n data: ToolFinishedData\n ): MutableToolCallHandle | undefined {\n const entry = this.active.get(data.tool_call_id);\n if (!entry) return undefined;\n this.active.delete(data.tool_call_id);\n const value = parseToolOutput(data.output);\n entry.resolveOutput(value);\n entry.handle.output = value;\n entry.handle.status = \"finished\";\n entry.handle.error = undefined;\n return entry.handle;\n }\n\n private handleError(data: ToolErrorData): MutableToolCallHandle | undefined {\n const entry = this.active.get(data.tool_call_id);\n if (!entry) return undefined;\n this.active.delete(data.tool_call_id);\n entry.rejectOutput(new Error(data.message));\n entry.handle.output = null;\n entry.handle.status = \"error\";\n entry.handle.error = data.message;\n return entry.handle;\n }\n}\n"],"mappings":";;;;;AAqFA,SAAgB,0BACd,QACyB;AACzB,QAAO;EACL,MAAM,OAAO;EACb,QAAQ,OAAO;EACf,IAAI,OAAO;EACX,WAAW,OAAO;EAClB,OAAO,OAAO;EACd,MAAM,OAAO;EACb,QAAQ,OAAO;EAChB;;;;;;;;;AAUH,SAAgB,iBAAiB,OAAyB;AACxD,KAAI,OAAO,UAAU,SAAU,QAAO;CACtC,MAAM,UAAU,MAAM,MAAM;AAC5B,KAAI,CAAC,QAAQ,WAAW,IAAI,IAAI,CAAC,QAAQ,WAAW,IAAI,CACtD,QAAO;AAGT,KAAI;AACF,SAAO,KAAK,MAAM,QAAQ;SACpB;AACN,SAAO;;;AAIX,SAAS,kBACP,OACyD;AACzD,KAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;CAChD,MAAM,SAAS;AACf,KAAI,OAAO,SAAS,OAAQ,QAAO;AACnC,QAAO,OAAO,OAAO,iBAAiB,YAAY,aAAa;;AAGjE,SAAS,sBAAsB,SAA4B;CACzD,IAAI,MAAM;AACV,MAAK,MAAM,SAAS,SAAS;AAC3B,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU;EACzC,MAAM,SAAS;AACf,MAAI,OAAO,SAAS,UAAU,OAAO,OAAO,SAAS,SACnD,QAAO,OAAO;;AAGlB,QAAO;;;;;;AAOT,SAAS,uBAAuB,SAAkC;AAChE,KAAI,WAAW,KAAM,QAAO;AAE5B,KAAI,OAAO,YAAY,UAAU;AAE/B,MADgB,QAAQ,MAAM,CAClB,WAAW,EAAG,QAAO;AACjC,SAAO,iBAAiB,QAAQ;;AAGlC,KAAI,MAAM,QAAQ,QAAQ,EAAE;EAC1B,MAAM,OAAO,sBAAsB,QAAQ;AAC3C,MAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,SAAO,iBAAiB,KAAK;;AAG/B,KAAI,OAAO,YAAY,SACrB,QAAO;AAGT,QAAO;;;;;;;;;;;AAYT,SAAgB,gBAAgB,OAAgC;CAC9D,MAAM,SAAS,iBAAiB,MAAM;AACtC,KAAI,kBAAkB,OAAO,CAC3B,QAAO,uBAAuB,OAAO,QAAQ;AAE/C,QAAO,UAAU;;;;;;;;AAenB,IAAa,oBAAb,MAA+B;CAC7B,yBAA0B,IAAI,KAA6B;CAE3D,QAAQ,OAAsD;EAC5D,MAAM,OAAO,MAAM,OAAO;AAE1B,MAAI,KAAK,UAAU,eACjB,QAAO,KAAK,cAAc,OAAO,KAAK;AAGxC,MAAI,KAAK,UAAU,gBACjB,QAAO,KAAK,eAAe,KAAK;AAGlC,MAAI,KAAK,UAAU,aACjB,QAAO,KAAK,YAAY,KAAK;;;;;CAUjC,QAAQ,QAAqB;AAC3B,OAAK,MAAM,SAAS,KAAK,OAAO,QAAQ,EAAE;AACxC,SAAM,aAAa,OAAO;AAC1B,SAAM,OAAO,SAAS;AACtB,SAAM,OAAO,QAAQ,OAAO;;AAE9B,OAAK,OAAO,OAAO;;CAGrB,cACE,OACA,MACuB;EACvB,IAAI;EACJ,IAAI;EAEJ,MAAM,gBAAgB,IAAI,SAAkB,SAAS,WAAW;AAC9D,mBAAgB;AAChB,kBAAe;IACf;AAIF,gBAAc,YAAY,KAAA,EAAU;EAEpC,MAAM,QAAQ,iBAAiB,KAAK,MAAM;EAC1C,MAAM,OAAO,KAAK;EAClB,MAAM,SAAS,KAAK;EAGpB,MAAM,SAAgC;GACpC;GACA;GACA,IAAI;GACJ,WANgB,CAAC,GAAG,MAAM,OAAO,UAAU;GAO3C;GACA,MAAM;GACN,QAAQ;GACR,QAAQ;GACR,OAAO,KAAA;GACP;GACD;AAED,OAAK,OAAO,IAAI,QAAQ;GACtB;GACA;GACA;GACD,CAAC;AAEF,SAAO;;CAGT,eACE,MACmC;EACnC,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,aAAa;AAChD,MAAI,CAAC,MAAO,QAAO,KAAA;AACnB,OAAK,OAAO,OAAO,KAAK,aAAa;EACrC,MAAM,QAAQ,gBAAgB,KAAK,OAAO;AAC1C,QAAM,cAAc,MAAM;AAC1B,QAAM,OAAO,SAAS;AACtB,QAAM,OAAO,SAAS;AACtB,QAAM,OAAO,QAAQ,KAAA;AACrB,SAAO,MAAM;;CAGf,YAAoB,MAAwD;EAC1E,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,aAAa;AAChD,MAAI,CAAC,MAAO,QAAO,KAAA;AACnB,OAAK,OAAO,OAAO,KAAK,aAAa;AACrC,QAAM,aAAa,IAAI,MAAM,KAAK,QAAQ,CAAC;AAC3C,QAAM,OAAO,SAAS;AACtB,QAAM,OAAO,SAAS;AACtB,QAAM,OAAO,QAAQ,KAAK;AAC1B,SAAO,MAAM"}
@@ -476,10 +476,18 @@ var ThreadStream = class {
476
476
  * Reset interrupt state and resume all paused user subscriptions.
477
477
  * Called before `run.start()` and `input.respond()` so that
478
478
  * iterators on the same handle pick up the next run's events.
479
+ *
480
+ * @param respondedInterruptId - When responding to one of several
481
+ * pending interrupts, only that entry is removed. Clearing the
482
+ * full list here would drop other headless-tool interrupts that
483
+ * are still awaiting client execution.
479
484
  */
480
- #prepareForNextRun() {
485
+ #prepareForNextRun(respondedInterruptId) {
481
486
  this.interrupted = false;
482
- this.interrupts.length = 0;
487
+ if (respondedInterruptId != null) {
488
+ const index = this.interrupts.findIndex((entry) => entry.interruptId === respondedInterruptId);
489
+ if (index >= 0) this.interrupts.splice(index, 1);
490
+ } else this.interrupts.length = 0;
483
491
  if (this.#terminalPauseTimer != null) {
484
492
  clearTimeout(this.#terminalPauseTimer);
485
493
  this.#terminalPauseTimer = void 0;
@@ -532,7 +540,7 @@ var ThreadStream = class {
532
540
  return projection;
533
541
  }
534
542
  /**
535
- * Tool calls with promise-based output/status/error.
543
+ * Tool calls with a promise-based {@link output} for script consumers.
536
544
  * Mirrors the in-process `run.toolCalls`.
537
545
  */
538
546
  get toolCalls() {
@@ -543,7 +551,7 @@ var ThreadStream = class {
543
551
  this.#startProjection(["tools", ...this.#lifecycleChannels()], (event) => {
544
552
  if (event.method !== "tools") return;
545
553
  const tc = assembler.consume(event);
546
- if (tc) buffer.push(tc);
554
+ if (tc) buffer.push(require_tools.toClientAssembledToolCall(tc));
547
555
  }, () => buffer.close());
548
556
  return buffer;
549
557
  }
@@ -829,7 +837,7 @@ var ThreadStream = class {
829
837
  * {@link input.respond}.
830
838
  */
831
839
  async respondInput(params) {
832
- this.#prepareForNextRun();
840
+ this.#prepareForNextRun(params.interrupt_id);
833
841
  this.#startLifecycleWatcher();
834
842
  await this.#send("input.respond", params);
835
843
  }