@langchain/langgraph-sdk 1.9.5 → 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.
- package/dist/client/stream/index.cjs +11 -3
- package/dist/client/stream/index.cjs.map +1 -1
- package/dist/client/stream/index.d.cts.map +1 -1
- package/dist/client/stream/index.d.ts.map +1 -1
- package/dist/client/stream/index.js +11 -3
- package/dist/client/stream/index.js.map +1 -1
- package/dist/headless-tools.cjs +128 -4
- package/dist/headless-tools.cjs.map +1 -1
- package/dist/headless-tools.d.cts +9 -1
- package/dist/headless-tools.d.cts.map +1 -1
- package/dist/headless-tools.d.ts +9 -1
- package/dist/headless-tools.d.ts.map +1 -1
- package/dist/headless-tools.js +126 -5
- package/dist/headless-tools.js.map +1 -1
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/stream/controller.cjs +26 -15
- package/dist/stream/controller.cjs.map +1 -1
- package/dist/stream/controller.d.cts +3 -1
- package/dist/stream/controller.d.cts.map +1 -1
- package/dist/stream/controller.d.ts +3 -1
- package/dist/stream/controller.d.ts.map +1 -1
- package/dist/stream/controller.js +27 -16
- package/dist/stream/controller.js.map +1 -1
- package/dist/stream/discovery/subagents.cjs +13 -0
- package/dist/stream/discovery/subagents.cjs.map +1 -1
- package/dist/stream/discovery/subagents.d.cts +5 -0
- package/dist/stream/discovery/subagents.d.cts.map +1 -1
- package/dist/stream/discovery/subagents.d.ts +5 -0
- package/dist/stream/discovery/subagents.d.ts.map +1 -1
- package/dist/stream/discovery/subagents.js +13 -0
- package/dist/stream/discovery/subagents.js.map +1 -1
- package/dist/stream/discovery/subgraphs.cjs +13 -0
- package/dist/stream/discovery/subgraphs.cjs.map +1 -1
- package/dist/stream/discovery/subgraphs.d.cts +5 -0
- package/dist/stream/discovery/subgraphs.d.cts.map +1 -1
- package/dist/stream/discovery/subgraphs.d.ts +5 -0
- package/dist/stream/discovery/subgraphs.d.ts.map +1 -1
- package/dist/stream/discovery/subgraphs.js +13 -0
- package/dist/stream/discovery/subgraphs.js.map +1 -1
- package/dist/stream/submit-coordinator.cjs +10 -12
- package/dist/stream/submit-coordinator.cjs.map +1 -1
- package/dist/stream/submit-coordinator.d.cts.map +1 -1
- package/dist/stream/submit-coordinator.d.ts.map +1 -1
- package/dist/stream/submit-coordinator.js +10 -12
- package/dist/stream/submit-coordinator.js.map +1 -1
- package/dist/stream/tool-calls.cjs +39 -2
- package/dist/stream/tool-calls.cjs.map +1 -1
- package/dist/stream/tool-calls.js +38 -3
- package/dist/stream/tool-calls.js.map +1 -1
- package/dist/stream/types.d.cts +2 -2
- package/dist/stream/types.d.ts +2 -2
- package/package.json +1 -1
package/dist/headless-tools.cjs
CHANGED
|
@@ -100,6 +100,109 @@ function headlessToolResumeCommand(result) {
|
|
|
100
100
|
return { resume: result.toolCallId ? { [result.toolCallId]: result.value } : result.value };
|
|
101
101
|
}
|
|
102
102
|
/**
|
|
103
|
+
* Merge headless-tool results into one resume command. Use interrupt-id keys
|
|
104
|
+
* whenever the stream provided them so the resume does not need to rediscover
|
|
105
|
+
* a pending interrupt from mutable client state.
|
|
106
|
+
*/
|
|
107
|
+
function headlessToolsBatchResumeCommand(entries) {
|
|
108
|
+
if (entries.length === 0) return { resume: {} };
|
|
109
|
+
if (!entries.every((entry) => entry.interruptId.length > 0) && entries.length === 1) {
|
|
110
|
+
const [entry] = entries;
|
|
111
|
+
return headlessToolResumeCommand({
|
|
112
|
+
toolCallId: entry.toolCallId,
|
|
113
|
+
value: entry.value
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
const resume = {};
|
|
117
|
+
for (const entry of entries) {
|
|
118
|
+
if (entry.interruptId.length === 0) continue;
|
|
119
|
+
resume[entry.interruptId] = entry.toolCallId != null && entry.toolCallId.length > 0 ? { [entry.toolCallId]: entry.value } : entry.value;
|
|
120
|
+
}
|
|
121
|
+
return { resume };
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* True when every top-level resume key is a graph task interrupt id
|
|
125
|
+
* (32-char hex from `values.__interrupt__`).
|
|
126
|
+
*/
|
|
127
|
+
function isInterruptIdKeyedResume(resume) {
|
|
128
|
+
if (resume == null || typeof resume !== "object" || Array.isArray(resume)) return false;
|
|
129
|
+
const keys = Object.keys(resume);
|
|
130
|
+
if (keys.length === 0) return false;
|
|
131
|
+
return keys.every((key) => /^[0-9a-f]{32}$/i.test(key));
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Normalize `command.resume` into the `run.start` input the API turns
|
|
135
|
+
* into `Command({ resume })`. Interrupt-id keyed payloads pass through;
|
|
136
|
+
* tool-call-keyed and generic payloads are wrapped under the matching
|
|
137
|
+
* protocol interrupt id.
|
|
138
|
+
*/
|
|
139
|
+
function buildResumeRunInput(resume, interrupts, resolvedInterruptIds) {
|
|
140
|
+
if (resume == null) return null;
|
|
141
|
+
if (isInterruptIdKeyedResume(resume)) return resume;
|
|
142
|
+
const target = resolveInterruptTargetForHeadlessResume(resume, interrupts, resolvedInterruptIds);
|
|
143
|
+
if (target == null) return null;
|
|
144
|
+
return { [target.interruptId]: resume };
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Reads the tool-call id from a headless-tool resume command shaped as
|
|
148
|
+
* `{ [toolCallId]: result }`.
|
|
149
|
+
*/
|
|
150
|
+
function extractHeadlessToolCallIdFromResumeCommand(resume) {
|
|
151
|
+
if (resume == null || typeof resume !== "object" || Array.isArray(resume)) return;
|
|
152
|
+
const keys = Object.keys(resume);
|
|
153
|
+
if (keys.length !== 1) return void 0;
|
|
154
|
+
return keys[0];
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Pick the protocol interrupt that matches a headless-tool resume payload.
|
|
158
|
+
* Falls back to the newest unresolved interrupt for non-keyed resumes.
|
|
159
|
+
*/
|
|
160
|
+
function resolveInterruptTargetForHeadlessResume(resume, interrupts, resolvedInterruptIds) {
|
|
161
|
+
const toolCallId = extractHeadlessToolCallIdFromResumeCommand(resume);
|
|
162
|
+
if (toolCallId != null) for (let i = interrupts.length - 1; i >= 0; i -= 1) {
|
|
163
|
+
const entry = interrupts[i];
|
|
164
|
+
if (entry == null || resolvedInterruptIds.has(entry.interruptId)) continue;
|
|
165
|
+
if (parseHeadlessToolInterruptPayload(entry.payload)?.toolCall.id === toolCallId) return {
|
|
166
|
+
interruptId: entry.interruptId,
|
|
167
|
+
namespace: [...entry.namespace]
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
for (let i = interrupts.length - 1; i >= 0; i -= 1) {
|
|
171
|
+
const entry = interrupts[i];
|
|
172
|
+
if (entry == null || resolvedInterruptIds.has(entry.interruptId)) continue;
|
|
173
|
+
return {
|
|
174
|
+
interruptId: entry.interruptId,
|
|
175
|
+
namespace: [...entry.namespace]
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
const coalescedHeadlessFlushes = /* @__PURE__ */ new WeakMap();
|
|
181
|
+
/**
|
|
182
|
+
* Coalesce rapid headless-tool flush triggers into one microtask so parallel
|
|
183
|
+
* `input.requested` events observed back-to-back batch into a single resume.
|
|
184
|
+
* Vue/Svelte/Angular watchers run synchronously per event; without this,
|
|
185
|
+
* the first interrupt can be claimed before the second arrives and resume
|
|
186
|
+
* splits into staggered single-tool commands.
|
|
187
|
+
*/
|
|
188
|
+
function scheduleCoalescedHeadlessToolFlush(handledIds, run) {
|
|
189
|
+
let state = coalescedHeadlessFlushes.get(handledIds);
|
|
190
|
+
if (state == null) {
|
|
191
|
+
state = {
|
|
192
|
+
scheduled: false,
|
|
193
|
+
run: () => {}
|
|
194
|
+
};
|
|
195
|
+
coalescedHeadlessFlushes.set(handledIds, state);
|
|
196
|
+
}
|
|
197
|
+
state.run = run;
|
|
198
|
+
if (state.scheduled) return;
|
|
199
|
+
state.scheduled = true;
|
|
200
|
+
Promise.resolve().then(() => {
|
|
201
|
+
state.scheduled = false;
|
|
202
|
+
state.run();
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
103
206
|
* Execute and resume all newly seen headless-tool interrupts from a values
|
|
104
207
|
* payload. Callers own `handledIds` and should clear it when the thread changes.
|
|
105
208
|
*/
|
|
@@ -108,6 +211,8 @@ function flushPendingHeadlessToolInterrupts(values, tools, handledIds, options)
|
|
|
108
211
|
const interrupts = values.__interrupt__;
|
|
109
212
|
if (!Array.isArray(interrupts) || interrupts.length === 0) return;
|
|
110
213
|
const defer = options.defer ?? ((run) => run());
|
|
214
|
+
const pending = [];
|
|
215
|
+
const seenToolCallIds = /* @__PURE__ */ new Set();
|
|
111
216
|
for (const interrupt of interrupts) {
|
|
112
217
|
const headlessInterrupt = parseHeadlessToolInterruptPayload(interrupt.value);
|
|
113
218
|
if (!headlessInterrupt) continue;
|
|
@@ -115,16 +220,33 @@ function flushPendingHeadlessToolInterrupts(values, tools, handledIds, options)
|
|
|
115
220
|
const toolCallId = headlessInterrupt.toolCall.id ?? "";
|
|
116
221
|
if (handledIds.has(interruptId)) continue;
|
|
117
222
|
if (toolCallId && handledIds.has(toolCallId)) continue;
|
|
223
|
+
if (toolCallId && seenToolCallIds.has(toolCallId)) continue;
|
|
224
|
+
if (toolCallId) seenToolCallIds.add(toolCallId);
|
|
118
225
|
handledIds.add(interruptId);
|
|
119
226
|
if (toolCallId) handledIds.add(toolCallId);
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
227
|
+
pending.push({
|
|
228
|
+
interruptId,
|
|
229
|
+
headlessInterrupt,
|
|
230
|
+
toolCallId
|
|
124
231
|
});
|
|
125
232
|
}
|
|
233
|
+
if (pending.length === 0) return;
|
|
234
|
+
defer(() => {
|
|
235
|
+
(async () => {
|
|
236
|
+
const results = await Promise.all(pending.map(async ({ interruptId, headlessInterrupt, toolCallId }) => {
|
|
237
|
+
const result = await handleHeadlessToolInterrupt(headlessInterrupt, tools, options.onTool);
|
|
238
|
+
return {
|
|
239
|
+
interruptId,
|
|
240
|
+
toolCallId: result.toolCallId ?? toolCallId,
|
|
241
|
+
value: result.value
|
|
242
|
+
};
|
|
243
|
+
}));
|
|
244
|
+
await Promise.resolve(options.resumeSubmit(headlessToolsBatchResumeCommand(results)));
|
|
245
|
+
})();
|
|
246
|
+
});
|
|
126
247
|
}
|
|
127
248
|
//#endregion
|
|
249
|
+
exports.buildResumeRunInput = buildResumeRunInput;
|
|
128
250
|
exports.executeHeadlessTool = executeHeadlessTool;
|
|
129
251
|
exports.filterOutHeadlessToolInterrupts = filterOutHeadlessToolInterrupts;
|
|
130
252
|
exports.findHeadlessTool = findHeadlessTool;
|
|
@@ -133,5 +255,7 @@ exports.handleHeadlessToolInterrupt = handleHeadlessToolInterrupt;
|
|
|
133
255
|
exports.headlessToolResumeCommand = headlessToolResumeCommand;
|
|
134
256
|
exports.isHeadlessToolInterrupt = isHeadlessToolInterrupt;
|
|
135
257
|
exports.parseHeadlessToolInterruptPayload = parseHeadlessToolInterruptPayload;
|
|
258
|
+
exports.resolveInterruptTargetForHeadlessResume = resolveInterruptTargetForHeadlessResume;
|
|
259
|
+
exports.scheduleCoalescedHeadlessToolFlush = scheduleCoalescedHeadlessToolFlush;
|
|
136
260
|
|
|
137
261
|
//# sourceMappingURL=headless-tools.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headless-tools.cjs","names":[],"sources":["../src/headless-tools.ts"],"sourcesContent":["import type { Interrupt } from \"./schema.js\";\n\n/**\n * Represents a headless tool interrupt payload emitted by LangChain's\n * schema-only `tool({ ... })` overload.\n *\n * Servers may serialize the nested tool call as `toolCall` (JS) or\n * `tool_call` (Python). Use {@link parseHeadlessToolInterruptPayload} to\n * normalize either shape before reading fields.\n */\nexport interface HeadlessToolInterrupt {\n type: \"tool\";\n toolCall: {\n id: string | undefined;\n name: string;\n args: unknown;\n };\n}\n\n/**\n * Parses a headless-tool interrupt `value` from the graph. Accepts both\n * `toolCall` (LangChain JS) and `tool_call` (Python / JSON snake_case).\n */\nexport function parseHeadlessToolInterruptPayload(\n value: unknown\n): HeadlessToolInterrupt | null {\n if (typeof value !== \"object\" || value == null) {\n return null;\n }\n const v = value as Record<string, unknown>;\n if (v.type !== \"tool\") {\n return null;\n }\n\n const rawTc = v.toolCall ?? v.tool_call;\n if (typeof rawTc !== \"object\" || rawTc == null) {\n return null;\n }\n const tc = rawTc as Record<string, unknown>;\n if (typeof tc.name !== \"string\") {\n return null;\n }\n\n const id = typeof tc.id === \"string\" ? tc.id : undefined;\n\n return {\n type: \"tool\",\n toolCall: {\n id,\n name: tc.name,\n args: tc.args,\n },\n };\n}\n\n/**\n * Client-side implementation returned by `headlessTool.implement(...)`.\n */\nexport interface HeadlessToolImplementation<Args = unknown, Output = unknown> {\n tool: {\n name: string;\n };\n execute: (args: Args) => Promise<Output>;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnyHeadlessToolImplementation = HeadlessToolImplementation<\n any,\n any\n>;\n\nexport interface ToolEvent {\n phase: \"start\" | \"success\" | \"error\";\n name: string;\n args: unknown;\n result?: unknown;\n error?: Error;\n duration?: number;\n}\n\nexport type OnToolCallback = (event: ToolEvent) => void;\n\n/**\n * Strip headless-tool interrupts from a user-facing interrupt list.\n */\nexport function filterOutHeadlessToolInterrupts<T extends { value?: unknown }>(\n interrupts: readonly T[]\n): T[] {\n return interrupts.filter(\n (interrupt) =>\n interrupt.value == null || !isHeadlessToolInterrupt(interrupt.value)\n );\n}\n\nexport function isHeadlessToolInterrupt(\n interrupt: unknown\n): interrupt is HeadlessToolInterrupt {\n return parseHeadlessToolInterruptPayload(interrupt) != null;\n}\n\nexport function findHeadlessTool<Args = unknown, Output = unknown>(\n tools: HeadlessToolImplementation[],\n name: string\n): HeadlessToolImplementation<Args, Output> | undefined {\n return tools.find((tool) => tool.tool.name === name) as\n | HeadlessToolImplementation<Args, Output>\n | undefined;\n}\n\nexport async function executeHeadlessTool<Args = unknown, Output = unknown>(\n implementation: HeadlessToolImplementation<Args, Output>,\n args: Args,\n onTool?: OnToolCallback\n): Promise<\n { success: true; result: Output } | { success: false; error: Error }\n> {\n const startTime = Date.now();\n\n onTool?.({\n phase: \"start\",\n name: implementation.tool.name,\n args,\n });\n\n try {\n const result = await implementation.execute(args);\n const duration = Date.now() - startTime;\n\n onTool?.({\n phase: \"success\",\n name: implementation.tool.name,\n args,\n result,\n duration,\n });\n\n return { success: true, result };\n } catch (err) {\n // oxlint-disable-next-line no-instanceof/no-instanceof\n const error = err instanceof Error ? err : new Error(String(err));\n const duration = Date.now() - startTime;\n\n onTool?.({\n phase: \"error\",\n name: implementation.tool.name,\n args,\n error,\n duration,\n });\n\n return { success: false, error };\n }\n}\n\nexport async function handleHeadlessToolInterrupt(\n interrupt: HeadlessToolInterrupt,\n tools: HeadlessToolImplementation[],\n onTool?: OnToolCallback\n): Promise<{ toolCallId: string | undefined; value: unknown }> {\n const { toolCall } = interrupt;\n const implementation = findHeadlessTool(tools, toolCall.name);\n\n if (!implementation) {\n const error = new Error(\n `Headless tool \"${toolCall.name}\" is not registered. ` +\n `Available tools: ${tools.map((tool) => tool.tool.name).join(\", \") || \"none\"}`\n );\n\n onTool?.({\n phase: \"error\",\n name: toolCall.name,\n args: toolCall.args,\n error,\n duration: 0,\n });\n\n return {\n toolCallId: toolCall.id,\n value: { error: error.message },\n };\n }\n\n const result = await executeHeadlessTool(\n implementation,\n toolCall.args as never,\n onTool\n );\n\n if (result.success) {\n return {\n toolCallId: toolCall.id,\n value: result.result,\n };\n }\n\n return {\n toolCallId: toolCall.id,\n value: { error: result.error.message },\n };\n}\n\nexport function headlessToolResumeCommand(result: {\n toolCallId: string | undefined;\n value: unknown;\n}): { resume: unknown } {\n return {\n resume: result.toolCallId\n ? { [result.toolCallId]: result.value }\n : result.value,\n };\n}\n\nexport interface FlushPendingHeadlessToolInterruptsOptions {\n onTool?: OnToolCallback;\n resumeSubmit: (command: { resume: unknown }) => void | Promise<void>;\n defer?: (run: () => void) => void;\n}\n\n/**\n * Execute and resume all newly seen headless-tool interrupts from a values\n * payload. Callers own `handledIds` and should clear it when the thread changes.\n */\nexport function flushPendingHeadlessToolInterrupts(\n values: Record<string, unknown> | null | undefined,\n tools: HeadlessToolImplementation[] | undefined,\n handledIds: Set<string>,\n options: FlushPendingHeadlessToolInterruptsOptions\n): void {\n if (!tools?.length || !values) return;\n\n const interrupts = values.__interrupt__;\n if (!Array.isArray(interrupts) || interrupts.length === 0) return;\n\n const defer = options.defer ?? ((run) => run());\n\n for (const interrupt of interrupts as Interrupt[]) {\n const headlessInterrupt = parseHeadlessToolInterruptPayload(\n interrupt.value\n );\n if (!headlessInterrupt) continue;\n\n const interruptId = interrupt.id ?? headlessInterrupt.toolCall.id ?? \"\";\n const toolCallId = headlessInterrupt.toolCall.id ?? \"\";\n if (handledIds.has(interruptId)) continue;\n // v2 protocol runs mirror the same headless-tool interrupt in both\n // `values.__interrupt__` and `rootStore.interrupts` with different\n // ids (graph/task id vs protocol interrupt_id). The headless-tool\n // effect can also re-run after the first resume clears\n // `rootStore.interrupts` while `values.__interrupt__` is still\n // present — persist tool call ids in the caller-owned set so we\n // only execute + resume once per pending tool call.\n if (toolCallId && handledIds.has(toolCallId)) continue;\n handledIds.add(interruptId);\n if (toolCallId) handledIds.add(toolCallId);\n\n defer(() => {\n void handleHeadlessToolInterrupt(\n headlessInterrupt,\n tools,\n options.onTool\n ).then((result) => {\n void options.resumeSubmit(headlessToolResumeCommand(result));\n });\n });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"headless-tools.cjs","names":[],"sources":["../src/headless-tools.ts"],"sourcesContent":["import type { Interrupt } from \"./schema.js\";\n\n/**\n * Represents a headless tool interrupt payload emitted by LangChain's\n * schema-only `tool({ ... })` overload.\n *\n * Servers may serialize the nested tool call as `toolCall` (JS) or\n * `tool_call` (Python). Use {@link parseHeadlessToolInterruptPayload} to\n * normalize either shape before reading fields.\n */\nexport interface HeadlessToolInterrupt {\n type: \"tool\";\n toolCall: {\n id: string | undefined;\n name: string;\n args: unknown;\n };\n}\n\n/**\n * Parses a headless-tool interrupt `value` from the graph. Accepts both\n * `toolCall` (LangChain JS) and `tool_call` (Python / JSON snake_case).\n */\nexport function parseHeadlessToolInterruptPayload(\n value: unknown\n): HeadlessToolInterrupt | null {\n if (typeof value !== \"object\" || value == null) {\n return null;\n }\n const v = value as Record<string, unknown>;\n if (v.type !== \"tool\") {\n return null;\n }\n\n const rawTc = v.toolCall ?? v.tool_call;\n if (typeof rawTc !== \"object\" || rawTc == null) {\n return null;\n }\n const tc = rawTc as Record<string, unknown>;\n if (typeof tc.name !== \"string\") {\n return null;\n }\n\n const id = typeof tc.id === \"string\" ? tc.id : undefined;\n\n return {\n type: \"tool\",\n toolCall: {\n id,\n name: tc.name,\n args: tc.args,\n },\n };\n}\n\n/**\n * Client-side implementation returned by `headlessTool.implement(...)`.\n */\nexport interface HeadlessToolImplementation<Args = unknown, Output = unknown> {\n tool: {\n name: string;\n };\n execute: (args: Args) => Promise<Output>;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnyHeadlessToolImplementation = HeadlessToolImplementation<\n any,\n any\n>;\n\nexport interface ToolEvent {\n phase: \"start\" | \"success\" | \"error\";\n name: string;\n args: unknown;\n result?: unknown;\n error?: Error;\n duration?: number;\n}\n\nexport type OnToolCallback = (event: ToolEvent) => void;\n\n/**\n * Strip headless-tool interrupts from a user-facing interrupt list.\n */\nexport function filterOutHeadlessToolInterrupts<T extends { value?: unknown }>(\n interrupts: readonly T[]\n): T[] {\n return interrupts.filter(\n (interrupt) =>\n interrupt.value == null || !isHeadlessToolInterrupt(interrupt.value)\n );\n}\n\nexport function isHeadlessToolInterrupt(\n interrupt: unknown\n): interrupt is HeadlessToolInterrupt {\n return parseHeadlessToolInterruptPayload(interrupt) != null;\n}\n\nexport function findHeadlessTool<Args = unknown, Output = unknown>(\n tools: HeadlessToolImplementation[],\n name: string\n): HeadlessToolImplementation<Args, Output> | undefined {\n return tools.find((tool) => tool.tool.name === name) as\n | HeadlessToolImplementation<Args, Output>\n | undefined;\n}\n\nexport async function executeHeadlessTool<Args = unknown, Output = unknown>(\n implementation: HeadlessToolImplementation<Args, Output>,\n args: Args,\n onTool?: OnToolCallback\n): Promise<\n { success: true; result: Output } | { success: false; error: Error }\n> {\n const startTime = Date.now();\n\n onTool?.({\n phase: \"start\",\n name: implementation.tool.name,\n args,\n });\n\n try {\n const result = await implementation.execute(args);\n const duration = Date.now() - startTime;\n\n onTool?.({\n phase: \"success\",\n name: implementation.tool.name,\n args,\n result,\n duration,\n });\n\n return { success: true, result };\n } catch (err) {\n // oxlint-disable-next-line no-instanceof/no-instanceof\n const error = err instanceof Error ? err : new Error(String(err));\n const duration = Date.now() - startTime;\n\n onTool?.({\n phase: \"error\",\n name: implementation.tool.name,\n args,\n error,\n duration,\n });\n\n return { success: false, error };\n }\n}\n\nexport async function handleHeadlessToolInterrupt(\n interrupt: HeadlessToolInterrupt,\n tools: HeadlessToolImplementation[],\n onTool?: OnToolCallback\n): Promise<{ toolCallId: string | undefined; value: unknown }> {\n const { toolCall } = interrupt;\n const implementation = findHeadlessTool(tools, toolCall.name);\n\n if (!implementation) {\n const error = new Error(\n `Headless tool \"${toolCall.name}\" is not registered. ` +\n `Available tools: ${tools.map((tool) => tool.tool.name).join(\", \") || \"none\"}`\n );\n\n onTool?.({\n phase: \"error\",\n name: toolCall.name,\n args: toolCall.args,\n error,\n duration: 0,\n });\n\n return {\n toolCallId: toolCall.id,\n value: { error: error.message },\n };\n }\n\n const result = await executeHeadlessTool(\n implementation,\n toolCall.args as never,\n onTool\n );\n\n if (result.success) {\n return {\n toolCallId: toolCall.id,\n value: result.result,\n };\n }\n\n return {\n toolCallId: toolCall.id,\n value: { error: result.error.message },\n };\n}\n\nexport function headlessToolResumeCommand(result: {\n toolCallId: string | undefined;\n value: unknown;\n}): { resume: unknown } {\n return {\n resume: result.toolCallId\n ? { [result.toolCallId]: result.value }\n : result.value,\n };\n}\n\n/**\n * Merge headless-tool results into one resume command. Use interrupt-id keys\n * whenever the stream provided them so the resume does not need to rediscover\n * a pending interrupt from mutable client state.\n */\nexport function headlessToolsBatchResumeCommand(\n entries: ReadonlyArray<{\n interruptId: string;\n toolCallId: string | undefined;\n value: unknown;\n }>\n): { resume: unknown } {\n if (entries.length === 0) {\n return { resume: {} };\n }\n\n const hasInterruptIds = entries.every(\n (entry) => entry.interruptId.length > 0\n );\n if (!hasInterruptIds && entries.length === 1) {\n const [entry] = entries;\n return headlessToolResumeCommand({\n toolCallId: entry.toolCallId,\n value: entry.value,\n });\n }\n\n const resume: Record<string, unknown> = {};\n for (const entry of entries) {\n if (entry.interruptId.length === 0) continue;\n resume[entry.interruptId] =\n entry.toolCallId != null && entry.toolCallId.length > 0\n ? { [entry.toolCallId]: entry.value }\n : entry.value;\n }\n return { resume };\n}\n\n/**\n * True when every top-level resume key is a graph task interrupt id\n * (32-char hex from `values.__interrupt__`).\n */\nexport function isInterruptIdKeyedResume(resume: unknown): boolean {\n if (resume == null || typeof resume !== \"object\" || Array.isArray(resume)) {\n return false;\n }\n const keys = Object.keys(resume as Record<string, unknown>);\n if (keys.length === 0) return false;\n return keys.every((key) => /^[0-9a-f]{32}$/i.test(key));\n}\n\n/**\n * Normalize `command.resume` into the `run.start` input the API turns\n * into `Command({ resume })`. Interrupt-id keyed payloads pass through;\n * tool-call-keyed and generic payloads are wrapped under the matching\n * protocol interrupt id.\n */\nexport function buildResumeRunInput(\n resume: unknown,\n interrupts: readonly ProtocolInterruptEntry[],\n resolvedInterruptIds: ReadonlySet<string>\n): Record<string, unknown> | null {\n if (resume == null) return null;\n if (isInterruptIdKeyedResume(resume)) {\n return resume as Record<string, unknown>;\n }\n\n const target = resolveInterruptTargetForHeadlessResume(\n resume,\n interrupts,\n resolvedInterruptIds\n );\n if (target == null) return null;\n\n return { [target.interruptId]: resume };\n}\n\n/**\n * Reads the tool-call id from a headless-tool resume command shaped as\n * `{ [toolCallId]: result }`.\n */\nexport function extractHeadlessToolCallIdFromResumeCommand(\n resume: unknown\n): string | undefined {\n if (resume == null || typeof resume !== \"object\" || Array.isArray(resume)) {\n return undefined;\n }\n const keys = Object.keys(resume as Record<string, unknown>);\n if (keys.length !== 1) return undefined;\n return keys[0];\n}\n\nexport interface ProtocolInterruptEntry {\n interruptId: string;\n namespace: string[];\n payload: unknown;\n}\n\n/**\n * Pick the protocol interrupt that matches a headless-tool resume payload.\n * Falls back to the newest unresolved interrupt for non-keyed resumes.\n */\nexport function resolveInterruptTargetForHeadlessResume(\n resume: unknown,\n interrupts: readonly ProtocolInterruptEntry[],\n resolvedInterruptIds: ReadonlySet<string>\n): { interruptId: string; namespace: string[] } | null {\n const toolCallId = extractHeadlessToolCallIdFromResumeCommand(resume);\n if (toolCallId != null) {\n for (let i = interrupts.length - 1; i >= 0; i -= 1) {\n const entry = interrupts[i];\n if (entry == null || resolvedInterruptIds.has(entry.interruptId)) {\n continue;\n }\n const headless = parseHeadlessToolInterruptPayload(entry.payload);\n if (headless?.toolCall.id === toolCallId) {\n return {\n interruptId: entry.interruptId,\n namespace: [...entry.namespace],\n };\n }\n }\n }\n\n for (let i = interrupts.length - 1; i >= 0; i -= 1) {\n const entry = interrupts[i];\n if (entry == null || resolvedInterruptIds.has(entry.interruptId)) {\n continue;\n }\n return {\n interruptId: entry.interruptId,\n namespace: [...entry.namespace],\n };\n }\n return null;\n}\n\nexport interface FlushPendingHeadlessToolInterruptsOptions {\n onTool?: OnToolCallback;\n resumeSubmit: (command: { resume: unknown }) => void | Promise<void>;\n defer?: (run: () => void) => void;\n}\n\nconst coalescedHeadlessFlushes = new WeakMap<\n Set<string>,\n { scheduled: boolean; run: () => void }\n>();\n\n/**\n * Coalesce rapid headless-tool flush triggers into one microtask so parallel\n * `input.requested` events observed back-to-back batch into a single resume.\n * Vue/Svelte/Angular watchers run synchronously per event; without this,\n * the first interrupt can be claimed before the second arrives and resume\n * splits into staggered single-tool commands.\n */\nexport function scheduleCoalescedHeadlessToolFlush(\n handledIds: Set<string>,\n run: () => void\n): void {\n let state = coalescedHeadlessFlushes.get(handledIds);\n if (state == null) {\n state = { scheduled: false, run: () => {} };\n coalescedHeadlessFlushes.set(handledIds, state);\n }\n state.run = run;\n if (state.scheduled) return;\n state.scheduled = true;\n void Promise.resolve().then(() => {\n state!.scheduled = false;\n state!.run();\n });\n}\n\n/**\n * Execute and resume all newly seen headless-tool interrupts from a values\n * payload. Callers own `handledIds` and should clear it when the thread changes.\n */\nexport function flushPendingHeadlessToolInterrupts(\n values: Record<string, unknown> | null | undefined,\n tools: HeadlessToolImplementation[] | undefined,\n handledIds: Set<string>,\n options: FlushPendingHeadlessToolInterruptsOptions\n): void {\n if (!tools?.length || !values) return;\n\n const interrupts = values.__interrupt__;\n if (!Array.isArray(interrupts) || interrupts.length === 0) return;\n\n const defer = options.defer ?? ((run) => run());\n const pending: Array<{\n interruptId: string;\n headlessInterrupt: HeadlessToolInterrupt;\n toolCallId: string;\n }> = [];\n const seenToolCallIds = new Set<string>();\n\n for (const interrupt of interrupts as Interrupt[]) {\n const headlessInterrupt = parseHeadlessToolInterruptPayload(\n interrupt.value\n );\n if (!headlessInterrupt) continue;\n\n const interruptId = interrupt.id ?? headlessInterrupt.toolCall.id ?? \"\";\n const toolCallId = headlessInterrupt.toolCall.id ?? \"\";\n if (handledIds.has(interruptId)) continue;\n // v2 protocol runs mirror the same headless-tool interrupt in both\n // `values.__interrupt__` and `rootStore.interrupts` with different\n // ids (graph/task id vs protocol interrupt_id). The headless-tool\n // effect can also re-run after the first resume clears\n // `rootStore.interrupts` while `values.__interrupt__` is still\n // present — persist tool call ids in the caller-owned set so we\n // only execute + resume once per pending tool call.\n if (toolCallId && handledIds.has(toolCallId)) continue;\n if (toolCallId && seenToolCallIds.has(toolCallId)) continue;\n if (toolCallId) seenToolCallIds.add(toolCallId);\n\n // Claim before defer so a second flush in the same tick cannot\n // schedule a duplicate execute/resume for the same interrupt.\n handledIds.add(interruptId);\n if (toolCallId) handledIds.add(toolCallId);\n\n pending.push({ interruptId, headlessInterrupt, toolCallId });\n }\n\n if (pending.length === 0) return;\n\n defer(() => {\n void (async () => {\n const results = await Promise.all(\n pending.map(async ({ interruptId, headlessInterrupt, toolCallId }) => {\n const result = await handleHeadlessToolInterrupt(\n headlessInterrupt,\n tools,\n options.onTool\n );\n return {\n interruptId,\n toolCallId: result.toolCallId ?? toolCallId,\n value: result.value,\n };\n })\n );\n await Promise.resolve(\n options.resumeSubmit(headlessToolsBatchResumeCommand(results))\n );\n })();\n });\n}\n"],"mappings":"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"}
|
|
@@ -71,11 +71,19 @@ interface FlushPendingHeadlessToolInterruptsOptions {
|
|
|
71
71
|
}) => void | Promise<void>;
|
|
72
72
|
defer?: (run: () => void) => void;
|
|
73
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Coalesce rapid headless-tool flush triggers into one microtask so parallel
|
|
76
|
+
* `input.requested` events observed back-to-back batch into a single resume.
|
|
77
|
+
* Vue/Svelte/Angular watchers run synchronously per event; without this,
|
|
78
|
+
* the first interrupt can be claimed before the second arrives and resume
|
|
79
|
+
* splits into staggered single-tool commands.
|
|
80
|
+
*/
|
|
81
|
+
declare function scheduleCoalescedHeadlessToolFlush(handledIds: Set<string>, run: () => void): void;
|
|
74
82
|
/**
|
|
75
83
|
* Execute and resume all newly seen headless-tool interrupts from a values
|
|
76
84
|
* payload. Callers own `handledIds` and should clear it when the thread changes.
|
|
77
85
|
*/
|
|
78
86
|
declare function flushPendingHeadlessToolInterrupts(values: Record<string, unknown> | null | undefined, tools: HeadlessToolImplementation[] | undefined, handledIds: Set<string>, options: FlushPendingHeadlessToolInterruptsOptions): void;
|
|
79
87
|
//#endregion
|
|
80
|
-
export { AnyHeadlessToolImplementation, FlushPendingHeadlessToolInterruptsOptions, HeadlessToolImplementation, HeadlessToolInterrupt, OnToolCallback, ToolEvent, executeHeadlessTool, filterOutHeadlessToolInterrupts, findHeadlessTool, flushPendingHeadlessToolInterrupts, handleHeadlessToolInterrupt, headlessToolResumeCommand, isHeadlessToolInterrupt, parseHeadlessToolInterruptPayload };
|
|
88
|
+
export { AnyHeadlessToolImplementation, FlushPendingHeadlessToolInterruptsOptions, HeadlessToolImplementation, HeadlessToolInterrupt, OnToolCallback, ToolEvent, executeHeadlessTool, filterOutHeadlessToolInterrupts, findHeadlessTool, flushPendingHeadlessToolInterrupts, handleHeadlessToolInterrupt, headlessToolResumeCommand, isHeadlessToolInterrupt, parseHeadlessToolInterruptPayload, scheduleCoalescedHeadlessToolFlush };
|
|
81
89
|
//# sourceMappingURL=headless-tools.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headless-tools.d.cts","names":[],"sources":["../src/headless-tools.ts"],"mappings":";;AAUA;;;;;;;UAAiB,qBAAA;EACf,IAAA;EACA,QAAA;IACE,EAAA;IACA,IAAA;IACA,IAAA;EAAA;AAAA;;AA2CJ;;;iBAnCgB,iCAAA,CACd,KAAA,YACC,qBAAA;;;;UAiCc,0BAAA;EACf,IAAA;IACE,IAAA;EAAA;EAEF,OAAA,GAAU,IAAA,EAAM,IAAA,KAAS,OAAA,CAAQ,MAAA;AAAA;AAAA,KAIvB,6BAAA,GAAgC,0BAAA;AAAA,UAK3B,SAAA;EACf,KAAA;EACA,IAAA;EACA,IAAA;EACA,MAAA;EACA,KAAA,GAAQ,KAAA;EACR,QAAA;AAAA;AAAA,KAGU,cAAA,IAAkB,KAAA,EAAO,SAAA;;AATrC;;iBAcgB,+BAAA;EAA4C,KAAA;AAAA,EAAA,CAC1D,UAAA,WAAqB,CAAA,KACpB,CAAA;AAAA,iBAOa,uBAAA,CACd,SAAA,YACC,SAAA,IAAa,qBAAA;AAAA,iBAIA,gBAAA,kCAAA,CACd,KAAA,EAAO,0BAAA,IACP,IAAA,WACC,0BAAA,CAA2B,IAAA,EAAM,MAAA;AAAA,iBAMd,mBAAA,kCAAA,CACpB,cAAA,EAAgB,0BAAA,CAA2B,IAAA,EAAM,MAAA,GACjD,IAAA,EAAM,IAAA,EACN,MAAA,GAAS,cAAA,GACR,OAAA;EACC,OAAA;EAAe,MAAA,EAAQ,MAAA;AAAA;EAAa,OAAA;EAAgB,KAAA,EAAO,KAAA;AAAA;AAAA,iBAwCzC,2BAAA,CACpB,SAAA,EAAW,qBAAA,EACX,KAAA,EAAO,0BAAA,IACP,MAAA,GAAS,cAAA,GACR,OAAA;EAAU,UAAA;EAAgC,KAAA;AAAA;AAAA,iBA2C7B,yBAAA,CAA0B,MAAA;EACxC,UAAA;EACA,KAAA;AAAA;EACI,MAAA;AAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"headless-tools.d.cts","names":[],"sources":["../src/headless-tools.ts"],"mappings":";;AAUA;;;;;;;UAAiB,qBAAA;EACf,IAAA;EACA,QAAA;IACE,EAAA;IACA,IAAA;IACA,IAAA;EAAA;AAAA;;AA2CJ;;;iBAnCgB,iCAAA,CACd,KAAA,YACC,qBAAA;;;;UAiCc,0BAAA;EACf,IAAA;IACE,IAAA;EAAA;EAEF,OAAA,GAAU,IAAA,EAAM,IAAA,KAAS,OAAA,CAAQ,MAAA;AAAA;AAAA,KAIvB,6BAAA,GAAgC,0BAAA;AAAA,UAK3B,SAAA;EACf,KAAA;EACA,IAAA;EACA,IAAA;EACA,MAAA;EACA,KAAA,GAAQ,KAAA;EACR,QAAA;AAAA;AAAA,KAGU,cAAA,IAAkB,KAAA,EAAO,SAAA;;AATrC;;iBAcgB,+BAAA;EAA4C,KAAA;AAAA,EAAA,CAC1D,UAAA,WAAqB,CAAA,KACpB,CAAA;AAAA,iBAOa,uBAAA,CACd,SAAA,YACC,SAAA,IAAa,qBAAA;AAAA,iBAIA,gBAAA,kCAAA,CACd,KAAA,EAAO,0BAAA,IACP,IAAA,WACC,0BAAA,CAA2B,IAAA,EAAM,MAAA;AAAA,iBAMd,mBAAA,kCAAA,CACpB,cAAA,EAAgB,0BAAA,CAA2B,IAAA,EAAM,MAAA,GACjD,IAAA,EAAM,IAAA,EACN,MAAA,GAAS,cAAA,GACR,OAAA;EACC,OAAA;EAAe,MAAA,EAAQ,MAAA;AAAA;EAAa,OAAA;EAAgB,KAAA,EAAO,KAAA;AAAA;AAAA,iBAwCzC,2BAAA,CACpB,SAAA,EAAW,qBAAA,EACX,KAAA,EAAO,0BAAA,IACP,MAAA,GAAS,cAAA,GACR,OAAA;EAAU,UAAA;EAAgC,KAAA;AAAA;AAAA,iBA2C7B,yBAAA,CAA0B,MAAA;EACxC,UAAA;EACA,KAAA;AAAA;EACI,MAAA;AAAA;AAAA,UAiJW,yCAAA;EACf,MAAA,GAAS,cAAA;EACT,YAAA,GAAe,OAAA;IAAW,MAAA;EAAA,aAA6B,OAAA;EACvD,KAAA,IAAS,GAAA;AAAA;;;;;;;;iBAeK,kCAAA,CACd,UAAA,EAAY,GAAA,UACZ,GAAA;;;;;iBAoBc,kCAAA,CACd,MAAA,EAAQ,MAAA,sCACR,KAAA,EAAO,0BAAA,gBACP,UAAA,EAAY,GAAA,UACZ,OAAA,EAAS,yCAAA"}
|
package/dist/headless-tools.d.ts
CHANGED
|
@@ -71,11 +71,19 @@ interface FlushPendingHeadlessToolInterruptsOptions {
|
|
|
71
71
|
}) => void | Promise<void>;
|
|
72
72
|
defer?: (run: () => void) => void;
|
|
73
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Coalesce rapid headless-tool flush triggers into one microtask so parallel
|
|
76
|
+
* `input.requested` events observed back-to-back batch into a single resume.
|
|
77
|
+
* Vue/Svelte/Angular watchers run synchronously per event; without this,
|
|
78
|
+
* the first interrupt can be claimed before the second arrives and resume
|
|
79
|
+
* splits into staggered single-tool commands.
|
|
80
|
+
*/
|
|
81
|
+
declare function scheduleCoalescedHeadlessToolFlush(handledIds: Set<string>, run: () => void): void;
|
|
74
82
|
/**
|
|
75
83
|
* Execute and resume all newly seen headless-tool interrupts from a values
|
|
76
84
|
* payload. Callers own `handledIds` and should clear it when the thread changes.
|
|
77
85
|
*/
|
|
78
86
|
declare function flushPendingHeadlessToolInterrupts(values: Record<string, unknown> | null | undefined, tools: HeadlessToolImplementation[] | undefined, handledIds: Set<string>, options: FlushPendingHeadlessToolInterruptsOptions): void;
|
|
79
87
|
//#endregion
|
|
80
|
-
export { AnyHeadlessToolImplementation, FlushPendingHeadlessToolInterruptsOptions, HeadlessToolImplementation, HeadlessToolInterrupt, OnToolCallback, ToolEvent, executeHeadlessTool, filterOutHeadlessToolInterrupts, findHeadlessTool, flushPendingHeadlessToolInterrupts, handleHeadlessToolInterrupt, headlessToolResumeCommand, isHeadlessToolInterrupt, parseHeadlessToolInterruptPayload };
|
|
88
|
+
export { AnyHeadlessToolImplementation, FlushPendingHeadlessToolInterruptsOptions, HeadlessToolImplementation, HeadlessToolInterrupt, OnToolCallback, ToolEvent, executeHeadlessTool, filterOutHeadlessToolInterrupts, findHeadlessTool, flushPendingHeadlessToolInterrupts, handleHeadlessToolInterrupt, headlessToolResumeCommand, isHeadlessToolInterrupt, parseHeadlessToolInterruptPayload, scheduleCoalescedHeadlessToolFlush };
|
|
81
89
|
//# sourceMappingURL=headless-tools.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headless-tools.d.ts","names":[],"sources":["../src/headless-tools.ts"],"mappings":";;AAUA;;;;;;;UAAiB,qBAAA;EACf,IAAA;EACA,QAAA;IACE,EAAA;IACA,IAAA;IACA,IAAA;EAAA;AAAA;;AA2CJ;;;iBAnCgB,iCAAA,CACd,KAAA,YACC,qBAAA;;;;UAiCc,0BAAA;EACf,IAAA;IACE,IAAA;EAAA;EAEF,OAAA,GAAU,IAAA,EAAM,IAAA,KAAS,OAAA,CAAQ,MAAA;AAAA;AAAA,KAIvB,6BAAA,GAAgC,0BAAA;AAAA,UAK3B,SAAA;EACf,KAAA;EACA,IAAA;EACA,IAAA;EACA,MAAA;EACA,KAAA,GAAQ,KAAA;EACR,QAAA;AAAA;AAAA,KAGU,cAAA,IAAkB,KAAA,EAAO,SAAA;;AATrC;;iBAcgB,+BAAA;EAA4C,KAAA;AAAA,EAAA,CAC1D,UAAA,WAAqB,CAAA,KACpB,CAAA;AAAA,iBAOa,uBAAA,CACd,SAAA,YACC,SAAA,IAAa,qBAAA;AAAA,iBAIA,gBAAA,kCAAA,CACd,KAAA,EAAO,0BAAA,IACP,IAAA,WACC,0BAAA,CAA2B,IAAA,EAAM,MAAA;AAAA,iBAMd,mBAAA,kCAAA,CACpB,cAAA,EAAgB,0BAAA,CAA2B,IAAA,EAAM,MAAA,GACjD,IAAA,EAAM,IAAA,EACN,MAAA,GAAS,cAAA,GACR,OAAA;EACC,OAAA;EAAe,MAAA,EAAQ,MAAA;AAAA;EAAa,OAAA;EAAgB,KAAA,EAAO,KAAA;AAAA;AAAA,iBAwCzC,2BAAA,CACpB,SAAA,EAAW,qBAAA,EACX,KAAA,EAAO,0BAAA,IACP,MAAA,GAAS,cAAA,GACR,OAAA;EAAU,UAAA;EAAgC,KAAA;AAAA;AAAA,iBA2C7B,yBAAA,CAA0B,MAAA;EACxC,UAAA;EACA,KAAA;AAAA;EACI,MAAA;AAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"headless-tools.d.ts","names":[],"sources":["../src/headless-tools.ts"],"mappings":";;AAUA;;;;;;;UAAiB,qBAAA;EACf,IAAA;EACA,QAAA;IACE,EAAA;IACA,IAAA;IACA,IAAA;EAAA;AAAA;;AA2CJ;;;iBAnCgB,iCAAA,CACd,KAAA,YACC,qBAAA;;;;UAiCc,0BAAA;EACf,IAAA;IACE,IAAA;EAAA;EAEF,OAAA,GAAU,IAAA,EAAM,IAAA,KAAS,OAAA,CAAQ,MAAA;AAAA;AAAA,KAIvB,6BAAA,GAAgC,0BAAA;AAAA,UAK3B,SAAA;EACf,KAAA;EACA,IAAA;EACA,IAAA;EACA,MAAA;EACA,KAAA,GAAQ,KAAA;EACR,QAAA;AAAA;AAAA,KAGU,cAAA,IAAkB,KAAA,EAAO,SAAA;;AATrC;;iBAcgB,+BAAA;EAA4C,KAAA;AAAA,EAAA,CAC1D,UAAA,WAAqB,CAAA,KACpB,CAAA;AAAA,iBAOa,uBAAA,CACd,SAAA,YACC,SAAA,IAAa,qBAAA;AAAA,iBAIA,gBAAA,kCAAA,CACd,KAAA,EAAO,0BAAA,IACP,IAAA,WACC,0BAAA,CAA2B,IAAA,EAAM,MAAA;AAAA,iBAMd,mBAAA,kCAAA,CACpB,cAAA,EAAgB,0BAAA,CAA2B,IAAA,EAAM,MAAA,GACjD,IAAA,EAAM,IAAA,EACN,MAAA,GAAS,cAAA,GACR,OAAA;EACC,OAAA;EAAe,MAAA,EAAQ,MAAA;AAAA;EAAa,OAAA;EAAgB,KAAA,EAAO,KAAA;AAAA;AAAA,iBAwCzC,2BAAA,CACpB,SAAA,EAAW,qBAAA,EACX,KAAA,EAAO,0BAAA,IACP,MAAA,GAAS,cAAA,GACR,OAAA;EAAU,UAAA;EAAgC,KAAA;AAAA;AAAA,iBA2C7B,yBAAA,CAA0B,MAAA;EACxC,UAAA;EACA,KAAA;AAAA;EACI,MAAA;AAAA;AAAA,UAiJW,yCAAA;EACf,MAAA,GAAS,cAAA;EACT,YAAA,GAAe,OAAA;IAAW,MAAA;EAAA,aAA6B,OAAA;EACvD,KAAA,IAAS,GAAA;AAAA;;;;;;;;iBAeK,kCAAA,CACd,UAAA,EAAY,GAAA,UACZ,GAAA;;;;;iBAoBc,kCAAA,CACd,MAAA,EAAQ,MAAA,sCACR,KAAA,EAAO,0BAAA,gBACP,UAAA,EAAY,GAAA,UACZ,OAAA,EAAS,yCAAA"}
|
package/dist/headless-tools.js
CHANGED
|
@@ -100,6 +100,109 @@ function headlessToolResumeCommand(result) {
|
|
|
100
100
|
return { resume: result.toolCallId ? { [result.toolCallId]: result.value } : result.value };
|
|
101
101
|
}
|
|
102
102
|
/**
|
|
103
|
+
* Merge headless-tool results into one resume command. Use interrupt-id keys
|
|
104
|
+
* whenever the stream provided them so the resume does not need to rediscover
|
|
105
|
+
* a pending interrupt from mutable client state.
|
|
106
|
+
*/
|
|
107
|
+
function headlessToolsBatchResumeCommand(entries) {
|
|
108
|
+
if (entries.length === 0) return { resume: {} };
|
|
109
|
+
if (!entries.every((entry) => entry.interruptId.length > 0) && entries.length === 1) {
|
|
110
|
+
const [entry] = entries;
|
|
111
|
+
return headlessToolResumeCommand({
|
|
112
|
+
toolCallId: entry.toolCallId,
|
|
113
|
+
value: entry.value
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
const resume = {};
|
|
117
|
+
for (const entry of entries) {
|
|
118
|
+
if (entry.interruptId.length === 0) continue;
|
|
119
|
+
resume[entry.interruptId] = entry.toolCallId != null && entry.toolCallId.length > 0 ? { [entry.toolCallId]: entry.value } : entry.value;
|
|
120
|
+
}
|
|
121
|
+
return { resume };
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* True when every top-level resume key is a graph task interrupt id
|
|
125
|
+
* (32-char hex from `values.__interrupt__`).
|
|
126
|
+
*/
|
|
127
|
+
function isInterruptIdKeyedResume(resume) {
|
|
128
|
+
if (resume == null || typeof resume !== "object" || Array.isArray(resume)) return false;
|
|
129
|
+
const keys = Object.keys(resume);
|
|
130
|
+
if (keys.length === 0) return false;
|
|
131
|
+
return keys.every((key) => /^[0-9a-f]{32}$/i.test(key));
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Normalize `command.resume` into the `run.start` input the API turns
|
|
135
|
+
* into `Command({ resume })`. Interrupt-id keyed payloads pass through;
|
|
136
|
+
* tool-call-keyed and generic payloads are wrapped under the matching
|
|
137
|
+
* protocol interrupt id.
|
|
138
|
+
*/
|
|
139
|
+
function buildResumeRunInput(resume, interrupts, resolvedInterruptIds) {
|
|
140
|
+
if (resume == null) return null;
|
|
141
|
+
if (isInterruptIdKeyedResume(resume)) return resume;
|
|
142
|
+
const target = resolveInterruptTargetForHeadlessResume(resume, interrupts, resolvedInterruptIds);
|
|
143
|
+
if (target == null) return null;
|
|
144
|
+
return { [target.interruptId]: resume };
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Reads the tool-call id from a headless-tool resume command shaped as
|
|
148
|
+
* `{ [toolCallId]: result }`.
|
|
149
|
+
*/
|
|
150
|
+
function extractHeadlessToolCallIdFromResumeCommand(resume) {
|
|
151
|
+
if (resume == null || typeof resume !== "object" || Array.isArray(resume)) return;
|
|
152
|
+
const keys = Object.keys(resume);
|
|
153
|
+
if (keys.length !== 1) return void 0;
|
|
154
|
+
return keys[0];
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Pick the protocol interrupt that matches a headless-tool resume payload.
|
|
158
|
+
* Falls back to the newest unresolved interrupt for non-keyed resumes.
|
|
159
|
+
*/
|
|
160
|
+
function resolveInterruptTargetForHeadlessResume(resume, interrupts, resolvedInterruptIds) {
|
|
161
|
+
const toolCallId = extractHeadlessToolCallIdFromResumeCommand(resume);
|
|
162
|
+
if (toolCallId != null) for (let i = interrupts.length - 1; i >= 0; i -= 1) {
|
|
163
|
+
const entry = interrupts[i];
|
|
164
|
+
if (entry == null || resolvedInterruptIds.has(entry.interruptId)) continue;
|
|
165
|
+
if (parseHeadlessToolInterruptPayload(entry.payload)?.toolCall.id === toolCallId) return {
|
|
166
|
+
interruptId: entry.interruptId,
|
|
167
|
+
namespace: [...entry.namespace]
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
for (let i = interrupts.length - 1; i >= 0; i -= 1) {
|
|
171
|
+
const entry = interrupts[i];
|
|
172
|
+
if (entry == null || resolvedInterruptIds.has(entry.interruptId)) continue;
|
|
173
|
+
return {
|
|
174
|
+
interruptId: entry.interruptId,
|
|
175
|
+
namespace: [...entry.namespace]
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
const coalescedHeadlessFlushes = /* @__PURE__ */ new WeakMap();
|
|
181
|
+
/**
|
|
182
|
+
* Coalesce rapid headless-tool flush triggers into one microtask so parallel
|
|
183
|
+
* `input.requested` events observed back-to-back batch into a single resume.
|
|
184
|
+
* Vue/Svelte/Angular watchers run synchronously per event; without this,
|
|
185
|
+
* the first interrupt can be claimed before the second arrives and resume
|
|
186
|
+
* splits into staggered single-tool commands.
|
|
187
|
+
*/
|
|
188
|
+
function scheduleCoalescedHeadlessToolFlush(handledIds, run) {
|
|
189
|
+
let state = coalescedHeadlessFlushes.get(handledIds);
|
|
190
|
+
if (state == null) {
|
|
191
|
+
state = {
|
|
192
|
+
scheduled: false,
|
|
193
|
+
run: () => {}
|
|
194
|
+
};
|
|
195
|
+
coalescedHeadlessFlushes.set(handledIds, state);
|
|
196
|
+
}
|
|
197
|
+
state.run = run;
|
|
198
|
+
if (state.scheduled) return;
|
|
199
|
+
state.scheduled = true;
|
|
200
|
+
Promise.resolve().then(() => {
|
|
201
|
+
state.scheduled = false;
|
|
202
|
+
state.run();
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
103
206
|
* Execute and resume all newly seen headless-tool interrupts from a values
|
|
104
207
|
* payload. Callers own `handledIds` and should clear it when the thread changes.
|
|
105
208
|
*/
|
|
@@ -108,6 +211,8 @@ function flushPendingHeadlessToolInterrupts(values, tools, handledIds, options)
|
|
|
108
211
|
const interrupts = values.__interrupt__;
|
|
109
212
|
if (!Array.isArray(interrupts) || interrupts.length === 0) return;
|
|
110
213
|
const defer = options.defer ?? ((run) => run());
|
|
214
|
+
const pending = [];
|
|
215
|
+
const seenToolCallIds = /* @__PURE__ */ new Set();
|
|
111
216
|
for (const interrupt of interrupts) {
|
|
112
217
|
const headlessInterrupt = parseHeadlessToolInterruptPayload(interrupt.value);
|
|
113
218
|
if (!headlessInterrupt) continue;
|
|
@@ -115,16 +220,32 @@ function flushPendingHeadlessToolInterrupts(values, tools, handledIds, options)
|
|
|
115
220
|
const toolCallId = headlessInterrupt.toolCall.id ?? "";
|
|
116
221
|
if (handledIds.has(interruptId)) continue;
|
|
117
222
|
if (toolCallId && handledIds.has(toolCallId)) continue;
|
|
223
|
+
if (toolCallId && seenToolCallIds.has(toolCallId)) continue;
|
|
224
|
+
if (toolCallId) seenToolCallIds.add(toolCallId);
|
|
118
225
|
handledIds.add(interruptId);
|
|
119
226
|
if (toolCallId) handledIds.add(toolCallId);
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
227
|
+
pending.push({
|
|
228
|
+
interruptId,
|
|
229
|
+
headlessInterrupt,
|
|
230
|
+
toolCallId
|
|
124
231
|
});
|
|
125
232
|
}
|
|
233
|
+
if (pending.length === 0) return;
|
|
234
|
+
defer(() => {
|
|
235
|
+
(async () => {
|
|
236
|
+
const results = await Promise.all(pending.map(async ({ interruptId, headlessInterrupt, toolCallId }) => {
|
|
237
|
+
const result = await handleHeadlessToolInterrupt(headlessInterrupt, tools, options.onTool);
|
|
238
|
+
return {
|
|
239
|
+
interruptId,
|
|
240
|
+
toolCallId: result.toolCallId ?? toolCallId,
|
|
241
|
+
value: result.value
|
|
242
|
+
};
|
|
243
|
+
}));
|
|
244
|
+
await Promise.resolve(options.resumeSubmit(headlessToolsBatchResumeCommand(results)));
|
|
245
|
+
})();
|
|
246
|
+
});
|
|
126
247
|
}
|
|
127
248
|
//#endregion
|
|
128
|
-
export { executeHeadlessTool, filterOutHeadlessToolInterrupts, findHeadlessTool, flushPendingHeadlessToolInterrupts, handleHeadlessToolInterrupt, headlessToolResumeCommand, isHeadlessToolInterrupt, parseHeadlessToolInterruptPayload };
|
|
249
|
+
export { buildResumeRunInput, executeHeadlessTool, filterOutHeadlessToolInterrupts, findHeadlessTool, flushPendingHeadlessToolInterrupts, handleHeadlessToolInterrupt, headlessToolResumeCommand, isHeadlessToolInterrupt, parseHeadlessToolInterruptPayload, resolveInterruptTargetForHeadlessResume, scheduleCoalescedHeadlessToolFlush };
|
|
129
250
|
|
|
130
251
|
//# sourceMappingURL=headless-tools.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headless-tools.js","names":[],"sources":["../src/headless-tools.ts"],"sourcesContent":["import type { Interrupt } from \"./schema.js\";\n\n/**\n * Represents a headless tool interrupt payload emitted by LangChain's\n * schema-only `tool({ ... })` overload.\n *\n * Servers may serialize the nested tool call as `toolCall` (JS) or\n * `tool_call` (Python). Use {@link parseHeadlessToolInterruptPayload} to\n * normalize either shape before reading fields.\n */\nexport interface HeadlessToolInterrupt {\n type: \"tool\";\n toolCall: {\n id: string | undefined;\n name: string;\n args: unknown;\n };\n}\n\n/**\n * Parses a headless-tool interrupt `value` from the graph. Accepts both\n * `toolCall` (LangChain JS) and `tool_call` (Python / JSON snake_case).\n */\nexport function parseHeadlessToolInterruptPayload(\n value: unknown\n): HeadlessToolInterrupt | null {\n if (typeof value !== \"object\" || value == null) {\n return null;\n }\n const v = value as Record<string, unknown>;\n if (v.type !== \"tool\") {\n return null;\n }\n\n const rawTc = v.toolCall ?? v.tool_call;\n if (typeof rawTc !== \"object\" || rawTc == null) {\n return null;\n }\n const tc = rawTc as Record<string, unknown>;\n if (typeof tc.name !== \"string\") {\n return null;\n }\n\n const id = typeof tc.id === \"string\" ? tc.id : undefined;\n\n return {\n type: \"tool\",\n toolCall: {\n id,\n name: tc.name,\n args: tc.args,\n },\n };\n}\n\n/**\n * Client-side implementation returned by `headlessTool.implement(...)`.\n */\nexport interface HeadlessToolImplementation<Args = unknown, Output = unknown> {\n tool: {\n name: string;\n };\n execute: (args: Args) => Promise<Output>;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnyHeadlessToolImplementation = HeadlessToolImplementation<\n any,\n any\n>;\n\nexport interface ToolEvent {\n phase: \"start\" | \"success\" | \"error\";\n name: string;\n args: unknown;\n result?: unknown;\n error?: Error;\n duration?: number;\n}\n\nexport type OnToolCallback = (event: ToolEvent) => void;\n\n/**\n * Strip headless-tool interrupts from a user-facing interrupt list.\n */\nexport function filterOutHeadlessToolInterrupts<T extends { value?: unknown }>(\n interrupts: readonly T[]\n): T[] {\n return interrupts.filter(\n (interrupt) =>\n interrupt.value == null || !isHeadlessToolInterrupt(interrupt.value)\n );\n}\n\nexport function isHeadlessToolInterrupt(\n interrupt: unknown\n): interrupt is HeadlessToolInterrupt {\n return parseHeadlessToolInterruptPayload(interrupt) != null;\n}\n\nexport function findHeadlessTool<Args = unknown, Output = unknown>(\n tools: HeadlessToolImplementation[],\n name: string\n): HeadlessToolImplementation<Args, Output> | undefined {\n return tools.find((tool) => tool.tool.name === name) as\n | HeadlessToolImplementation<Args, Output>\n | undefined;\n}\n\nexport async function executeHeadlessTool<Args = unknown, Output = unknown>(\n implementation: HeadlessToolImplementation<Args, Output>,\n args: Args,\n onTool?: OnToolCallback\n): Promise<\n { success: true; result: Output } | { success: false; error: Error }\n> {\n const startTime = Date.now();\n\n onTool?.({\n phase: \"start\",\n name: implementation.tool.name,\n args,\n });\n\n try {\n const result = await implementation.execute(args);\n const duration = Date.now() - startTime;\n\n onTool?.({\n phase: \"success\",\n name: implementation.tool.name,\n args,\n result,\n duration,\n });\n\n return { success: true, result };\n } catch (err) {\n // oxlint-disable-next-line no-instanceof/no-instanceof\n const error = err instanceof Error ? err : new Error(String(err));\n const duration = Date.now() - startTime;\n\n onTool?.({\n phase: \"error\",\n name: implementation.tool.name,\n args,\n error,\n duration,\n });\n\n return { success: false, error };\n }\n}\n\nexport async function handleHeadlessToolInterrupt(\n interrupt: HeadlessToolInterrupt,\n tools: HeadlessToolImplementation[],\n onTool?: OnToolCallback\n): Promise<{ toolCallId: string | undefined; value: unknown }> {\n const { toolCall } = interrupt;\n const implementation = findHeadlessTool(tools, toolCall.name);\n\n if (!implementation) {\n const error = new Error(\n `Headless tool \"${toolCall.name}\" is not registered. ` +\n `Available tools: ${tools.map((tool) => tool.tool.name).join(\", \") || \"none\"}`\n );\n\n onTool?.({\n phase: \"error\",\n name: toolCall.name,\n args: toolCall.args,\n error,\n duration: 0,\n });\n\n return {\n toolCallId: toolCall.id,\n value: { error: error.message },\n };\n }\n\n const result = await executeHeadlessTool(\n implementation,\n toolCall.args as never,\n onTool\n );\n\n if (result.success) {\n return {\n toolCallId: toolCall.id,\n value: result.result,\n };\n }\n\n return {\n toolCallId: toolCall.id,\n value: { error: result.error.message },\n };\n}\n\nexport function headlessToolResumeCommand(result: {\n toolCallId: string | undefined;\n value: unknown;\n}): { resume: unknown } {\n return {\n resume: result.toolCallId\n ? { [result.toolCallId]: result.value }\n : result.value,\n };\n}\n\nexport interface FlushPendingHeadlessToolInterruptsOptions {\n onTool?: OnToolCallback;\n resumeSubmit: (command: { resume: unknown }) => void | Promise<void>;\n defer?: (run: () => void) => void;\n}\n\n/**\n * Execute and resume all newly seen headless-tool interrupts from a values\n * payload. Callers own `handledIds` and should clear it when the thread changes.\n */\nexport function flushPendingHeadlessToolInterrupts(\n values: Record<string, unknown> | null | undefined,\n tools: HeadlessToolImplementation[] | undefined,\n handledIds: Set<string>,\n options: FlushPendingHeadlessToolInterruptsOptions\n): void {\n if (!tools?.length || !values) return;\n\n const interrupts = values.__interrupt__;\n if (!Array.isArray(interrupts) || interrupts.length === 0) return;\n\n const defer = options.defer ?? ((run) => run());\n\n for (const interrupt of interrupts as Interrupt[]) {\n const headlessInterrupt = parseHeadlessToolInterruptPayload(\n interrupt.value\n );\n if (!headlessInterrupt) continue;\n\n const interruptId = interrupt.id ?? headlessInterrupt.toolCall.id ?? \"\";\n const toolCallId = headlessInterrupt.toolCall.id ?? \"\";\n if (handledIds.has(interruptId)) continue;\n // v2 protocol runs mirror the same headless-tool interrupt in both\n // `values.__interrupt__` and `rootStore.interrupts` with different\n // ids (graph/task id vs protocol interrupt_id). The headless-tool\n // effect can also re-run after the first resume clears\n // `rootStore.interrupts` while `values.__interrupt__` is still\n // present — persist tool call ids in the caller-owned set so we\n // only execute + resume once per pending tool call.\n if (toolCallId && handledIds.has(toolCallId)) continue;\n handledIds.add(interruptId);\n if (toolCallId) handledIds.add(toolCallId);\n\n defer(() => {\n void handleHeadlessToolInterrupt(\n headlessInterrupt,\n tools,\n options.onTool\n ).then((result) => {\n void options.resumeSubmit(headlessToolResumeCommand(result));\n });\n });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"headless-tools.js","names":[],"sources":["../src/headless-tools.ts"],"sourcesContent":["import type { Interrupt } from \"./schema.js\";\n\n/**\n * Represents a headless tool interrupt payload emitted by LangChain's\n * schema-only `tool({ ... })` overload.\n *\n * Servers may serialize the nested tool call as `toolCall` (JS) or\n * `tool_call` (Python). Use {@link parseHeadlessToolInterruptPayload} to\n * normalize either shape before reading fields.\n */\nexport interface HeadlessToolInterrupt {\n type: \"tool\";\n toolCall: {\n id: string | undefined;\n name: string;\n args: unknown;\n };\n}\n\n/**\n * Parses a headless-tool interrupt `value` from the graph. Accepts both\n * `toolCall` (LangChain JS) and `tool_call` (Python / JSON snake_case).\n */\nexport function parseHeadlessToolInterruptPayload(\n value: unknown\n): HeadlessToolInterrupt | null {\n if (typeof value !== \"object\" || value == null) {\n return null;\n }\n const v = value as Record<string, unknown>;\n if (v.type !== \"tool\") {\n return null;\n }\n\n const rawTc = v.toolCall ?? v.tool_call;\n if (typeof rawTc !== \"object\" || rawTc == null) {\n return null;\n }\n const tc = rawTc as Record<string, unknown>;\n if (typeof tc.name !== \"string\") {\n return null;\n }\n\n const id = typeof tc.id === \"string\" ? tc.id : undefined;\n\n return {\n type: \"tool\",\n toolCall: {\n id,\n name: tc.name,\n args: tc.args,\n },\n };\n}\n\n/**\n * Client-side implementation returned by `headlessTool.implement(...)`.\n */\nexport interface HeadlessToolImplementation<Args = unknown, Output = unknown> {\n tool: {\n name: string;\n };\n execute: (args: Args) => Promise<Output>;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnyHeadlessToolImplementation = HeadlessToolImplementation<\n any,\n any\n>;\n\nexport interface ToolEvent {\n phase: \"start\" | \"success\" | \"error\";\n name: string;\n args: unknown;\n result?: unknown;\n error?: Error;\n duration?: number;\n}\n\nexport type OnToolCallback = (event: ToolEvent) => void;\n\n/**\n * Strip headless-tool interrupts from a user-facing interrupt list.\n */\nexport function filterOutHeadlessToolInterrupts<T extends { value?: unknown }>(\n interrupts: readonly T[]\n): T[] {\n return interrupts.filter(\n (interrupt) =>\n interrupt.value == null || !isHeadlessToolInterrupt(interrupt.value)\n );\n}\n\nexport function isHeadlessToolInterrupt(\n interrupt: unknown\n): interrupt is HeadlessToolInterrupt {\n return parseHeadlessToolInterruptPayload(interrupt) != null;\n}\n\nexport function findHeadlessTool<Args = unknown, Output = unknown>(\n tools: HeadlessToolImplementation[],\n name: string\n): HeadlessToolImplementation<Args, Output> | undefined {\n return tools.find((tool) => tool.tool.name === name) as\n | HeadlessToolImplementation<Args, Output>\n | undefined;\n}\n\nexport async function executeHeadlessTool<Args = unknown, Output = unknown>(\n implementation: HeadlessToolImplementation<Args, Output>,\n args: Args,\n onTool?: OnToolCallback\n): Promise<\n { success: true; result: Output } | { success: false; error: Error }\n> {\n const startTime = Date.now();\n\n onTool?.({\n phase: \"start\",\n name: implementation.tool.name,\n args,\n });\n\n try {\n const result = await implementation.execute(args);\n const duration = Date.now() - startTime;\n\n onTool?.({\n phase: \"success\",\n name: implementation.tool.name,\n args,\n result,\n duration,\n });\n\n return { success: true, result };\n } catch (err) {\n // oxlint-disable-next-line no-instanceof/no-instanceof\n const error = err instanceof Error ? err : new Error(String(err));\n const duration = Date.now() - startTime;\n\n onTool?.({\n phase: \"error\",\n name: implementation.tool.name,\n args,\n error,\n duration,\n });\n\n return { success: false, error };\n }\n}\n\nexport async function handleHeadlessToolInterrupt(\n interrupt: HeadlessToolInterrupt,\n tools: HeadlessToolImplementation[],\n onTool?: OnToolCallback\n): Promise<{ toolCallId: string | undefined; value: unknown }> {\n const { toolCall } = interrupt;\n const implementation = findHeadlessTool(tools, toolCall.name);\n\n if (!implementation) {\n const error = new Error(\n `Headless tool \"${toolCall.name}\" is not registered. ` +\n `Available tools: ${tools.map((tool) => tool.tool.name).join(\", \") || \"none\"}`\n );\n\n onTool?.({\n phase: \"error\",\n name: toolCall.name,\n args: toolCall.args,\n error,\n duration: 0,\n });\n\n return {\n toolCallId: toolCall.id,\n value: { error: error.message },\n };\n }\n\n const result = await executeHeadlessTool(\n implementation,\n toolCall.args as never,\n onTool\n );\n\n if (result.success) {\n return {\n toolCallId: toolCall.id,\n value: result.result,\n };\n }\n\n return {\n toolCallId: toolCall.id,\n value: { error: result.error.message },\n };\n}\n\nexport function headlessToolResumeCommand(result: {\n toolCallId: string | undefined;\n value: unknown;\n}): { resume: unknown } {\n return {\n resume: result.toolCallId\n ? { [result.toolCallId]: result.value }\n : result.value,\n };\n}\n\n/**\n * Merge headless-tool results into one resume command. Use interrupt-id keys\n * whenever the stream provided them so the resume does not need to rediscover\n * a pending interrupt from mutable client state.\n */\nexport function headlessToolsBatchResumeCommand(\n entries: ReadonlyArray<{\n interruptId: string;\n toolCallId: string | undefined;\n value: unknown;\n }>\n): { resume: unknown } {\n if (entries.length === 0) {\n return { resume: {} };\n }\n\n const hasInterruptIds = entries.every(\n (entry) => entry.interruptId.length > 0\n );\n if (!hasInterruptIds && entries.length === 1) {\n const [entry] = entries;\n return headlessToolResumeCommand({\n toolCallId: entry.toolCallId,\n value: entry.value,\n });\n }\n\n const resume: Record<string, unknown> = {};\n for (const entry of entries) {\n if (entry.interruptId.length === 0) continue;\n resume[entry.interruptId] =\n entry.toolCallId != null && entry.toolCallId.length > 0\n ? { [entry.toolCallId]: entry.value }\n : entry.value;\n }\n return { resume };\n}\n\n/**\n * True when every top-level resume key is a graph task interrupt id\n * (32-char hex from `values.__interrupt__`).\n */\nexport function isInterruptIdKeyedResume(resume: unknown): boolean {\n if (resume == null || typeof resume !== \"object\" || Array.isArray(resume)) {\n return false;\n }\n const keys = Object.keys(resume as Record<string, unknown>);\n if (keys.length === 0) return false;\n return keys.every((key) => /^[0-9a-f]{32}$/i.test(key));\n}\n\n/**\n * Normalize `command.resume` into the `run.start` input the API turns\n * into `Command({ resume })`. Interrupt-id keyed payloads pass through;\n * tool-call-keyed and generic payloads are wrapped under the matching\n * protocol interrupt id.\n */\nexport function buildResumeRunInput(\n resume: unknown,\n interrupts: readonly ProtocolInterruptEntry[],\n resolvedInterruptIds: ReadonlySet<string>\n): Record<string, unknown> | null {\n if (resume == null) return null;\n if (isInterruptIdKeyedResume(resume)) {\n return resume as Record<string, unknown>;\n }\n\n const target = resolveInterruptTargetForHeadlessResume(\n resume,\n interrupts,\n resolvedInterruptIds\n );\n if (target == null) return null;\n\n return { [target.interruptId]: resume };\n}\n\n/**\n * Reads the tool-call id from a headless-tool resume command shaped as\n * `{ [toolCallId]: result }`.\n */\nexport function extractHeadlessToolCallIdFromResumeCommand(\n resume: unknown\n): string | undefined {\n if (resume == null || typeof resume !== \"object\" || Array.isArray(resume)) {\n return undefined;\n }\n const keys = Object.keys(resume as Record<string, unknown>);\n if (keys.length !== 1) return undefined;\n return keys[0];\n}\n\nexport interface ProtocolInterruptEntry {\n interruptId: string;\n namespace: string[];\n payload: unknown;\n}\n\n/**\n * Pick the protocol interrupt that matches a headless-tool resume payload.\n * Falls back to the newest unresolved interrupt for non-keyed resumes.\n */\nexport function resolveInterruptTargetForHeadlessResume(\n resume: unknown,\n interrupts: readonly ProtocolInterruptEntry[],\n resolvedInterruptIds: ReadonlySet<string>\n): { interruptId: string; namespace: string[] } | null {\n const toolCallId = extractHeadlessToolCallIdFromResumeCommand(resume);\n if (toolCallId != null) {\n for (let i = interrupts.length - 1; i >= 0; i -= 1) {\n const entry = interrupts[i];\n if (entry == null || resolvedInterruptIds.has(entry.interruptId)) {\n continue;\n }\n const headless = parseHeadlessToolInterruptPayload(entry.payload);\n if (headless?.toolCall.id === toolCallId) {\n return {\n interruptId: entry.interruptId,\n namespace: [...entry.namespace],\n };\n }\n }\n }\n\n for (let i = interrupts.length - 1; i >= 0; i -= 1) {\n const entry = interrupts[i];\n if (entry == null || resolvedInterruptIds.has(entry.interruptId)) {\n continue;\n }\n return {\n interruptId: entry.interruptId,\n namespace: [...entry.namespace],\n };\n }\n return null;\n}\n\nexport interface FlushPendingHeadlessToolInterruptsOptions {\n onTool?: OnToolCallback;\n resumeSubmit: (command: { resume: unknown }) => void | Promise<void>;\n defer?: (run: () => void) => void;\n}\n\nconst coalescedHeadlessFlushes = new WeakMap<\n Set<string>,\n { scheduled: boolean; run: () => void }\n>();\n\n/**\n * Coalesce rapid headless-tool flush triggers into one microtask so parallel\n * `input.requested` events observed back-to-back batch into a single resume.\n * Vue/Svelte/Angular watchers run synchronously per event; without this,\n * the first interrupt can be claimed before the second arrives and resume\n * splits into staggered single-tool commands.\n */\nexport function scheduleCoalescedHeadlessToolFlush(\n handledIds: Set<string>,\n run: () => void\n): void {\n let state = coalescedHeadlessFlushes.get(handledIds);\n if (state == null) {\n state = { scheduled: false, run: () => {} };\n coalescedHeadlessFlushes.set(handledIds, state);\n }\n state.run = run;\n if (state.scheduled) return;\n state.scheduled = true;\n void Promise.resolve().then(() => {\n state!.scheduled = false;\n state!.run();\n });\n}\n\n/**\n * Execute and resume all newly seen headless-tool interrupts from a values\n * payload. Callers own `handledIds` and should clear it when the thread changes.\n */\nexport function flushPendingHeadlessToolInterrupts(\n values: Record<string, unknown> | null | undefined,\n tools: HeadlessToolImplementation[] | undefined,\n handledIds: Set<string>,\n options: FlushPendingHeadlessToolInterruptsOptions\n): void {\n if (!tools?.length || !values) return;\n\n const interrupts = values.__interrupt__;\n if (!Array.isArray(interrupts) || interrupts.length === 0) return;\n\n const defer = options.defer ?? ((run) => run());\n const pending: Array<{\n interruptId: string;\n headlessInterrupt: HeadlessToolInterrupt;\n toolCallId: string;\n }> = [];\n const seenToolCallIds = new Set<string>();\n\n for (const interrupt of interrupts as Interrupt[]) {\n const headlessInterrupt = parseHeadlessToolInterruptPayload(\n interrupt.value\n );\n if (!headlessInterrupt) continue;\n\n const interruptId = interrupt.id ?? headlessInterrupt.toolCall.id ?? \"\";\n const toolCallId = headlessInterrupt.toolCall.id ?? \"\";\n if (handledIds.has(interruptId)) continue;\n // v2 protocol runs mirror the same headless-tool interrupt in both\n // `values.__interrupt__` and `rootStore.interrupts` with different\n // ids (graph/task id vs protocol interrupt_id). The headless-tool\n // effect can also re-run after the first resume clears\n // `rootStore.interrupts` while `values.__interrupt__` is still\n // present — persist tool call ids in the caller-owned set so we\n // only execute + resume once per pending tool call.\n if (toolCallId && handledIds.has(toolCallId)) continue;\n if (toolCallId && seenToolCallIds.has(toolCallId)) continue;\n if (toolCallId) seenToolCallIds.add(toolCallId);\n\n // Claim before defer so a second flush in the same tick cannot\n // schedule a duplicate execute/resume for the same interrupt.\n handledIds.add(interruptId);\n if (toolCallId) handledIds.add(toolCallId);\n\n pending.push({ interruptId, headlessInterrupt, toolCallId });\n }\n\n if (pending.length === 0) return;\n\n defer(() => {\n void (async () => {\n const results = await Promise.all(\n pending.map(async ({ interruptId, headlessInterrupt, toolCallId }) => {\n const result = await handleHeadlessToolInterrupt(\n headlessInterrupt,\n tools,\n options.onTool\n );\n return {\n interruptId,\n toolCallId: result.toolCallId ?? toolCallId,\n value: result.value,\n };\n })\n );\n await Promise.resolve(\n options.resumeSubmit(headlessToolsBatchResumeCommand(results))\n );\n })();\n });\n}\n"],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -36,3 +36,4 @@ exports.headlessToolResumeCommand = require_headless_tools.headlessToolResumeCom
|
|
|
36
36
|
exports.isHeadlessToolInterrupt = require_headless_tools.isHeadlessToolInterrupt;
|
|
37
37
|
exports.overrideFetchImplementation = require_fetch.overrideFetchImplementation;
|
|
38
38
|
exports.parseHeadlessToolInterruptPayload = require_headless_tools.parseHeadlessToolInterruptPayload;
|
|
39
|
+
exports.scheduleCoalescedHeadlessToolFlush = require_headless_tools.scheduleCoalescedHeadlessToolFlush;
|
package/dist/index.d.cts
CHANGED
|
@@ -16,11 +16,11 @@ import { HttpAgentServerAdapter, HttpAgentServerAdapterOptions } from "./client/
|
|
|
16
16
|
import { Client } from "./client/index.cjs";
|
|
17
17
|
import { overrideFetchImplementation } from "./singletons/fetch.cjs";
|
|
18
18
|
import { BagTemplate } from "./types.template.cjs";
|
|
19
|
-
import { AnyHeadlessToolImplementation, FlushPendingHeadlessToolInterruptsOptions, HeadlessToolImplementation, HeadlessToolInterrupt, OnToolCallback, ToolEvent, executeHeadlessTool, filterOutHeadlessToolInterrupts, findHeadlessTool, flushPendingHeadlessToolInterrupts, handleHeadlessToolInterrupt, headlessToolResumeCommand, isHeadlessToolInterrupt, parseHeadlessToolInterruptPayload } from "./headless-tools.cjs";
|
|
19
|
+
import { AnyHeadlessToolImplementation, FlushPendingHeadlessToolInterruptsOptions, HeadlessToolImplementation, HeadlessToolInterrupt, OnToolCallback, ToolEvent, executeHeadlessTool, filterOutHeadlessToolInterrupts, findHeadlessTool, flushPendingHeadlessToolInterrupts, handleHeadlessToolInterrupt, headlessToolResumeCommand, isHeadlessToolInterrupt, parseHeadlessToolInterruptPayload, scheduleCoalescedHeadlessToolFlush } from "./headless-tools.cjs";
|
|
20
20
|
import { BaseStream, StateRecord } from "./ui/stream/base.cjs";
|
|
21
21
|
import { UseAgentStream, UseAgentStreamOptions } from "./ui/stream/agent.cjs";
|
|
22
22
|
import { UseDeepAgentStream, UseDeepAgentStreamOptions } from "./ui/stream/deep-agent.cjs";
|
|
23
23
|
import { InferBag, InferNodeNames, InferNodeReturnTypes, InferStateType, InferSubagentStates, InferToolCalls, ResolveStreamInterface, ResolveStreamOptions } from "./ui/stream/index.cjs";
|
|
24
24
|
import { NAMESPACE_SEPARATOR } from "./stream/constants.cjs";
|
|
25
25
|
import { StreamError } from "./ui/errors.cjs";
|
|
26
|
-
export { type AIMessage, type AgentServerAdapter, type AnyHeadlessToolImplementation, type AnyMediaHandle, type AssembledMessage, type Assistant, type AssistantBase, type AssistantGraph, type AssistantVersion, type AssistantsSearchResponse, type AudioMedia, type BagTemplate, BaseStream, type Checkpoint, Client, type ClientConfig, type Command, type Config, type Cron, type CronCreateForThreadResponse, type CronCreateResponse, type CustomStreamEvent, type DebugStreamEvent, type DefaultToolCall, type DefaultValues, type ErrorStreamEvent, type EventSubscription, type EventsStreamEvent, type FeedbackStreamEvent, type FileMedia, type FlushPendingHeadlessToolInterruptsOptions, type FunctionMessage, type GraphSchema, type HeadlessToolImplementation, type HeadlessToolInterrupt, HttpAgentServerAdapter, type HttpAgentServerAdapterOptions, type HumanMessage, type ImageMedia, InferBag, InferNodeNames, InferNodeReturnTypes, InferStateType, InferSubagentStates, InferToolCalls, type InferToolOutput, type Interrupt, type Item, type ListNamespaceResponse, MediaAssembler, type MediaAssemblerCallbacks, type MediaAssemblerOptions, MediaAssemblyError, type MediaAssemblyErrorKind, type MediaBase, type MediaBlockType, type Message, MessageAssembler, type MessageSubscription, type MessagesStreamEvent, type MessagesTupleStreamEvent, type Metadata, type MetadataStreamEvent, NAMESPACE_SEPARATOR, type OnConflictBehavior, type OnToolCallback, ProtocolError, type ProtocolRequestHook, ProtocolSseTransportAdapter, type ProtocolSseTransportOptions, type ProtocolTransportPaths, ProtocolWebSocketTransportAdapter, type ProtocolWebSocketTransportOptions, type RemoveMessage, type RequestHook, ResolveStreamInterface, ResolveStreamOptions, type Run, type RunsInvokePayload, type SearchItem, type SearchItemsResponse, type SessionOrderingState, StateRecord, StreamError, type StreamEvent, type StreamMode, SubscriptionHandle, type SystemMessage, type Thread, type ThreadExtension, type ThreadExtensions, type ThreadState, type ThreadStatus, ThreadStream, type ThreadStreamOptions, type ThreadTask, type ToolCallFromTool, type ToolCallState, type ToolCallWithResult, type ToolCallsFromTools, type ToolEvent, type ToolMessage, type ToolProgress, type ToolsStreamEvent, type TransportAdapter, type UnwrapExtension, type UpdatesStreamEvent, UseAgentStream, UseAgentStreamOptions, UseDeepAgentStream, UseDeepAgentStreamOptions, type ValuesStreamEvent, type VideoMedia, executeHeadlessTool, filterOutHeadlessToolInterrupts, findHeadlessTool, flushPendingHeadlessToolInterrupts, getApiKey, handleHeadlessToolInterrupt, headlessToolResumeCommand, isHeadlessToolInterrupt, overrideFetchImplementation, parseHeadlessToolInterruptPayload };
|
|
26
|
+
export { type AIMessage, type AgentServerAdapter, type AnyHeadlessToolImplementation, type AnyMediaHandle, type AssembledMessage, type Assistant, type AssistantBase, type AssistantGraph, type AssistantVersion, type AssistantsSearchResponse, type AudioMedia, type BagTemplate, BaseStream, type Checkpoint, Client, type ClientConfig, type Command, type Config, type Cron, type CronCreateForThreadResponse, type CronCreateResponse, type CustomStreamEvent, type DebugStreamEvent, type DefaultToolCall, type DefaultValues, type ErrorStreamEvent, type EventSubscription, type EventsStreamEvent, type FeedbackStreamEvent, type FileMedia, type FlushPendingHeadlessToolInterruptsOptions, type FunctionMessage, type GraphSchema, type HeadlessToolImplementation, type HeadlessToolInterrupt, HttpAgentServerAdapter, type HttpAgentServerAdapterOptions, type HumanMessage, type ImageMedia, InferBag, InferNodeNames, InferNodeReturnTypes, InferStateType, InferSubagentStates, InferToolCalls, type InferToolOutput, type Interrupt, type Item, type ListNamespaceResponse, MediaAssembler, type MediaAssemblerCallbacks, type MediaAssemblerOptions, MediaAssemblyError, type MediaAssemblyErrorKind, type MediaBase, type MediaBlockType, type Message, MessageAssembler, type MessageSubscription, type MessagesStreamEvent, type MessagesTupleStreamEvent, type Metadata, type MetadataStreamEvent, NAMESPACE_SEPARATOR, type OnConflictBehavior, type OnToolCallback, ProtocolError, type ProtocolRequestHook, ProtocolSseTransportAdapter, type ProtocolSseTransportOptions, type ProtocolTransportPaths, ProtocolWebSocketTransportAdapter, type ProtocolWebSocketTransportOptions, type RemoveMessage, type RequestHook, ResolveStreamInterface, ResolveStreamOptions, type Run, type RunsInvokePayload, type SearchItem, type SearchItemsResponse, type SessionOrderingState, StateRecord, StreamError, type StreamEvent, type StreamMode, SubscriptionHandle, type SystemMessage, type Thread, type ThreadExtension, type ThreadExtensions, type ThreadState, type ThreadStatus, ThreadStream, type ThreadStreamOptions, type ThreadTask, type ToolCallFromTool, type ToolCallState, type ToolCallWithResult, type ToolCallsFromTools, type ToolEvent, type ToolMessage, type ToolProgress, type ToolsStreamEvent, type TransportAdapter, type UnwrapExtension, type UpdatesStreamEvent, UseAgentStream, UseAgentStreamOptions, UseDeepAgentStream, UseDeepAgentStreamOptions, type ValuesStreamEvent, type VideoMedia, executeHeadlessTool, filterOutHeadlessToolInterrupts, findHeadlessTool, flushPendingHeadlessToolInterrupts, getApiKey, handleHeadlessToolInterrupt, headlessToolResumeCommand, isHeadlessToolInterrupt, overrideFetchImplementation, parseHeadlessToolInterruptPayload, scheduleCoalescedHeadlessToolFlush };
|
package/dist/index.d.ts
CHANGED
|
@@ -16,11 +16,11 @@ import { HttpAgentServerAdapter, HttpAgentServerAdapterOptions } from "./client/
|
|
|
16
16
|
import { Client } from "./client/index.js";
|
|
17
17
|
import { overrideFetchImplementation } from "./singletons/fetch.js";
|
|
18
18
|
import { BagTemplate } from "./types.template.js";
|
|
19
|
-
import { AnyHeadlessToolImplementation, FlushPendingHeadlessToolInterruptsOptions, HeadlessToolImplementation, HeadlessToolInterrupt, OnToolCallback, ToolEvent, executeHeadlessTool, filterOutHeadlessToolInterrupts, findHeadlessTool, flushPendingHeadlessToolInterrupts, handleHeadlessToolInterrupt, headlessToolResumeCommand, isHeadlessToolInterrupt, parseHeadlessToolInterruptPayload } from "./headless-tools.js";
|
|
19
|
+
import { AnyHeadlessToolImplementation, FlushPendingHeadlessToolInterruptsOptions, HeadlessToolImplementation, HeadlessToolInterrupt, OnToolCallback, ToolEvent, executeHeadlessTool, filterOutHeadlessToolInterrupts, findHeadlessTool, flushPendingHeadlessToolInterrupts, handleHeadlessToolInterrupt, headlessToolResumeCommand, isHeadlessToolInterrupt, parseHeadlessToolInterruptPayload, scheduleCoalescedHeadlessToolFlush } from "./headless-tools.js";
|
|
20
20
|
import { BaseStream, StateRecord } from "./ui/stream/base.js";
|
|
21
21
|
import { UseAgentStream, UseAgentStreamOptions } from "./ui/stream/agent.js";
|
|
22
22
|
import { UseDeepAgentStream, UseDeepAgentStreamOptions } from "./ui/stream/deep-agent.js";
|
|
23
23
|
import { InferBag, InferNodeNames, InferNodeReturnTypes, InferStateType, InferSubagentStates, InferToolCalls, ResolveStreamInterface, ResolveStreamOptions } from "./ui/stream/index.js";
|
|
24
24
|
import { NAMESPACE_SEPARATOR } from "./stream/constants.js";
|
|
25
25
|
import { StreamError } from "./ui/errors.js";
|
|
26
|
-
export { type AIMessage, type AgentServerAdapter, type AnyHeadlessToolImplementation, type AnyMediaHandle, type AssembledMessage, type Assistant, type AssistantBase, type AssistantGraph, type AssistantVersion, type AssistantsSearchResponse, type AudioMedia, type BagTemplate, BaseStream, type Checkpoint, Client, type ClientConfig, type Command, type Config, type Cron, type CronCreateForThreadResponse, type CronCreateResponse, type CustomStreamEvent, type DebugStreamEvent, type DefaultToolCall, type DefaultValues, type ErrorStreamEvent, type EventSubscription, type EventsStreamEvent, type FeedbackStreamEvent, type FileMedia, type FlushPendingHeadlessToolInterruptsOptions, type FunctionMessage, type GraphSchema, type HeadlessToolImplementation, type HeadlessToolInterrupt, HttpAgentServerAdapter, type HttpAgentServerAdapterOptions, type HumanMessage, type ImageMedia, InferBag, InferNodeNames, InferNodeReturnTypes, InferStateType, InferSubagentStates, InferToolCalls, type InferToolOutput, type Interrupt, type Item, type ListNamespaceResponse, MediaAssembler, type MediaAssemblerCallbacks, type MediaAssemblerOptions, MediaAssemblyError, type MediaAssemblyErrorKind, type MediaBase, type MediaBlockType, type Message, MessageAssembler, type MessageSubscription, type MessagesStreamEvent, type MessagesTupleStreamEvent, type Metadata, type MetadataStreamEvent, NAMESPACE_SEPARATOR, type OnConflictBehavior, type OnToolCallback, ProtocolError, type ProtocolRequestHook, ProtocolSseTransportAdapter, type ProtocolSseTransportOptions, type ProtocolTransportPaths, ProtocolWebSocketTransportAdapter, type ProtocolWebSocketTransportOptions, type RemoveMessage, type RequestHook, ResolveStreamInterface, ResolveStreamOptions, type Run, type RunsInvokePayload, type SearchItem, type SearchItemsResponse, type SessionOrderingState, StateRecord, StreamError, type StreamEvent, type StreamMode, SubscriptionHandle, type SystemMessage, type Thread, type ThreadExtension, type ThreadExtensions, type ThreadState, type ThreadStatus, ThreadStream, type ThreadStreamOptions, type ThreadTask, type ToolCallFromTool, type ToolCallState, type ToolCallWithResult, type ToolCallsFromTools, type ToolEvent, type ToolMessage, type ToolProgress, type ToolsStreamEvent, type TransportAdapter, type UnwrapExtension, type UpdatesStreamEvent, UseAgentStream, UseAgentStreamOptions, UseDeepAgentStream, UseDeepAgentStreamOptions, type ValuesStreamEvent, type VideoMedia, executeHeadlessTool, filterOutHeadlessToolInterrupts, findHeadlessTool, flushPendingHeadlessToolInterrupts, getApiKey, handleHeadlessToolInterrupt, headlessToolResumeCommand, isHeadlessToolInterrupt, overrideFetchImplementation, parseHeadlessToolInterruptPayload };
|
|
26
|
+
export { type AIMessage, type AgentServerAdapter, type AnyHeadlessToolImplementation, type AnyMediaHandle, type AssembledMessage, type Assistant, type AssistantBase, type AssistantGraph, type AssistantVersion, type AssistantsSearchResponse, type AudioMedia, type BagTemplate, BaseStream, type Checkpoint, Client, type ClientConfig, type Command, type Config, type Cron, type CronCreateForThreadResponse, type CronCreateResponse, type CustomStreamEvent, type DebugStreamEvent, type DefaultToolCall, type DefaultValues, type ErrorStreamEvent, type EventSubscription, type EventsStreamEvent, type FeedbackStreamEvent, type FileMedia, type FlushPendingHeadlessToolInterruptsOptions, type FunctionMessage, type GraphSchema, type HeadlessToolImplementation, type HeadlessToolInterrupt, HttpAgentServerAdapter, type HttpAgentServerAdapterOptions, type HumanMessage, type ImageMedia, InferBag, InferNodeNames, InferNodeReturnTypes, InferStateType, InferSubagentStates, InferToolCalls, type InferToolOutput, type Interrupt, type Item, type ListNamespaceResponse, MediaAssembler, type MediaAssemblerCallbacks, type MediaAssemblerOptions, MediaAssemblyError, type MediaAssemblyErrorKind, type MediaBase, type MediaBlockType, type Message, MessageAssembler, type MessageSubscription, type MessagesStreamEvent, type MessagesTupleStreamEvent, type Metadata, type MetadataStreamEvent, NAMESPACE_SEPARATOR, type OnConflictBehavior, type OnToolCallback, ProtocolError, type ProtocolRequestHook, ProtocolSseTransportAdapter, type ProtocolSseTransportOptions, type ProtocolTransportPaths, ProtocolWebSocketTransportAdapter, type ProtocolWebSocketTransportOptions, type RemoveMessage, type RequestHook, ResolveStreamInterface, ResolveStreamOptions, type Run, type RunsInvokePayload, type SearchItem, type SearchItemsResponse, type SessionOrderingState, StateRecord, StreamError, type StreamEvent, type StreamMode, SubscriptionHandle, type SystemMessage, type Thread, type ThreadExtension, type ThreadExtensions, type ThreadState, type ThreadStatus, ThreadStream, type ThreadStreamOptions, type ThreadTask, type ToolCallFromTool, type ToolCallState, type ToolCallWithResult, type ToolCallsFromTools, type ToolEvent, type ToolMessage, type ToolProgress, type ToolsStreamEvent, type TransportAdapter, type UnwrapExtension, type UpdatesStreamEvent, UseAgentStream, UseAgentStreamOptions, UseDeepAgentStream, UseDeepAgentStreamOptions, type ValuesStreamEvent, type VideoMedia, executeHeadlessTool, filterOutHeadlessToolInterrupts, findHeadlessTool, flushPendingHeadlessToolInterrupts, getApiKey, handleHeadlessToolInterrupt, headlessToolResumeCommand, isHeadlessToolInterrupt, overrideFetchImplementation, parseHeadlessToolInterruptPayload, scheduleCoalescedHeadlessToolFlush };
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { MessageAssembler } from "./client/stream/messages.js";
|
|
|
4
4
|
import { MediaAssembler, MediaAssemblyError } from "./client/stream/media.js";
|
|
5
5
|
import { ProtocolError } from "./client/stream/error.js";
|
|
6
6
|
import { NAMESPACE_SEPARATOR } from "./stream/constants.js";
|
|
7
|
-
import { executeHeadlessTool, filterOutHeadlessToolInterrupts, findHeadlessTool, flushPendingHeadlessToolInterrupts, handleHeadlessToolInterrupt, headlessToolResumeCommand, isHeadlessToolInterrupt, parseHeadlessToolInterruptPayload } from "./headless-tools.js";
|
|
7
|
+
import { executeHeadlessTool, filterOutHeadlessToolInterrupts, findHeadlessTool, flushPendingHeadlessToolInterrupts, handleHeadlessToolInterrupt, headlessToolResumeCommand, isHeadlessToolInterrupt, parseHeadlessToolInterruptPayload, scheduleCoalescedHeadlessToolFlush } from "./headless-tools.js";
|
|
8
8
|
import { SubscriptionHandle, ThreadStream } from "./client/stream/index.js";
|
|
9
9
|
import { ProtocolSseTransportAdapter } from "./client/stream/transport/http.js";
|
|
10
10
|
import { ProtocolWebSocketTransportAdapter } from "./client/stream/transport/websocket.js";
|
|
@@ -13,4 +13,4 @@ import { Client } from "./client/index.js";
|
|
|
13
13
|
import "./client.js";
|
|
14
14
|
import "./stream/index.js";
|
|
15
15
|
import { StreamError } from "./ui/errors.js";
|
|
16
|
-
export { Client, HttpAgentServerAdapter, MediaAssembler, MediaAssemblyError, MessageAssembler, NAMESPACE_SEPARATOR, ProtocolError, ProtocolSseTransportAdapter, ProtocolWebSocketTransportAdapter, StreamError, SubscriptionHandle, ThreadStream, executeHeadlessTool, filterOutHeadlessToolInterrupts, findHeadlessTool, flushPendingHeadlessToolInterrupts, getApiKey, handleHeadlessToolInterrupt, headlessToolResumeCommand, isHeadlessToolInterrupt, overrideFetchImplementation, parseHeadlessToolInterruptPayload };
|
|
16
|
+
export { Client, HttpAgentServerAdapter, MediaAssembler, MediaAssemblyError, MessageAssembler, NAMESPACE_SEPARATOR, ProtocolError, ProtocolSseTransportAdapter, ProtocolWebSocketTransportAdapter, StreamError, SubscriptionHandle, ThreadStream, executeHeadlessTool, filterOutHeadlessToolInterrupts, findHeadlessTool, flushPendingHeadlessToolInterrupts, getApiKey, handleHeadlessToolInterrupt, headlessToolResumeCommand, isHeadlessToolInterrupt, overrideFetchImplementation, parseHeadlessToolInterruptPayload, scheduleCoalescedHeadlessToolFlush };
|