@langchain/langgraph-sdk 1.9.10 → 1.9.12

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.
@@ -97,7 +97,10 @@ async function handleHeadlessToolInterrupt(interrupt, tools, onTool) {
97
97
  };
98
98
  }
99
99
  function headlessToolResumeCommand(result) {
100
- return { resume: result.toolCallId ? { [result.toolCallId]: result.value } : result.value };
100
+ return {
101
+ resume: result.toolCallId ? { [result.toolCallId]: result.value } : result.value,
102
+ keyedByInterruptId: false
103
+ };
101
104
  }
102
105
  /**
103
106
  * Merge headless-tool results into one resume command. Use interrupt-id keys
@@ -105,7 +108,10 @@ function headlessToolResumeCommand(result) {
105
108
  * a pending interrupt from mutable client state.
106
109
  */
107
110
  function headlessToolsBatchResumeCommand(entries) {
108
- if (entries.length === 0) return { resume: {} };
111
+ if (entries.length === 0) return {
112
+ resume: {},
113
+ keyedByInterruptId: false
114
+ };
109
115
  if (!entries.every((entry) => entry.interruptId.length > 0) && entries.length === 1) {
110
116
  const [entry] = entries;
111
117
  return headlessToolResumeCommand({
@@ -118,7 +124,49 @@ function headlessToolsBatchResumeCommand(entries) {
118
124
  if (entry.interruptId.length === 0) continue;
119
125
  resume[entry.interruptId] = entry.toolCallId != null && entry.toolCallId.length > 0 ? { [entry.toolCallId]: entry.value } : entry.value;
120
126
  }
121
- return { resume };
127
+ return {
128
+ resume,
129
+ keyedByInterruptId: true
130
+ };
131
+ }
132
+ /**
133
+ * Resume a headless-tool batch on the v1 commands transport.
134
+ *
135
+ * {@link headlessToolsBatchResumeCommand} still returns a legacy
136
+ * `{ resume }` command shape for callers on the old runs/stream API.
137
+ * On v1 `StreamController`, that payload must be sent through
138
+ * {@link HeadlessToolResumeController.respond} /
139
+ * {@link HeadlessToolResumeController.respondAll} — not `submit(null,
140
+ * { command })`, which dispatches `run.start` without a resume value.
141
+ */
142
+ function applyHeadlessToolResumeCommand(controller, command) {
143
+ const { resume, keyedByInterruptId } = command;
144
+ if (resume == null) return Promise.resolve();
145
+ if (keyedByInterruptId === true || keyedByInterruptId !== false && isInterruptIdKeyedResume(resume)) return controller.respondAll(resume);
146
+ return controller.respond(resume);
147
+ }
148
+ /**
149
+ * True when a resume payload is keyed by protocol interrupt id at the top
150
+ * level (for {@link HeadlessToolResumeController.respondAll}).
151
+ *
152
+ * Prefer {@link HeadlessToolResumeCommand.keyedByInterruptId} from
153
+ * {@link headlessToolsBatchResumeCommand} when ids are not inferable
154
+ * (for example `{ "int-1": result }` without a nested tool-call map).
155
+ *
156
+ * When `interrupts` is provided, a single top-level key that matches a
157
+ * known protocol interrupt id is treated as interrupt-keyed.
158
+ */
159
+ function isInterruptIdKeyedResume(resume, interrupts) {
160
+ if (resume == null || typeof resume !== "object" || Array.isArray(resume)) return false;
161
+ const record = resume;
162
+ const keys = Object.keys(record);
163
+ if (keys.length === 0) return false;
164
+ if (keys.length > 1) return true;
165
+ const [key] = keys;
166
+ if (interrupts?.length) {
167
+ if (new Set(interrupts.map((entry) => entry.interruptId)).has(key)) return true;
168
+ }
169
+ return /^[0-9a-f]{32}$/i.test(key);
122
170
  }
123
171
  /**
124
172
  * Reads the tool-call id from a headless-tool resume command shaped as
@@ -223,6 +271,7 @@ function flushPendingHeadlessToolInterrupts(values, tools, handledIds, options)
223
271
  });
224
272
  }
225
273
  //#endregion
274
+ exports.applyHeadlessToolResumeCommand = applyHeadlessToolResumeCommand;
226
275
  exports.executeHeadlessTool = executeHeadlessTool;
227
276
  exports.filterOutHeadlessToolInterrupts = filterOutHeadlessToolInterrupts;
228
277
  exports.findHeadlessTool = findHeadlessTool;
@@ -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\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\n/**\n * Protocol interrupt entry tracked on {@link ThreadStream.interrupts}.\n * Used by {@link resolveInterruptTargetForHeadlessResume} when `respond()`\n * omits an explicit target.\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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"}
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\n/**\n * Resume command produced by {@link headlessToolResumeCommand} /\n * {@link headlessToolsBatchResumeCommand}.\n */\nexport interface HeadlessToolResumeCommand {\n resume: unknown;\n /**\n * When true, top-level {@link resume} keys are protocol interrupt ids and\n * must be sent through {@link HeadlessToolResumeController.respondAll}.\n */\n keyedByInterruptId?: boolean;\n}\n\nexport function headlessToolResumeCommand(result: {\n toolCallId: string | undefined;\n value: unknown;\n}): HeadlessToolResumeCommand {\n return {\n resume: result.toolCallId\n ? { [result.toolCallId]: result.value }\n : result.value,\n keyedByInterruptId: false,\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): HeadlessToolResumeCommand {\n if (entries.length === 0) {\n return { resume: {}, keyedByInterruptId: false };\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, keyedByInterruptId: true };\n}\n\n/**\n * Minimal controller surface for servicing a headless-tool resume on the\n * v1 stream protocol (`input.respond`).\n */\nexport interface HeadlessToolResumeController {\n respond: (\n response: unknown,\n options?: { interruptId?: string }\n ) => Promise<void>;\n respondAll: (responsesById: Record<string, unknown>) => Promise<void>;\n}\n\n/**\n * Resume a headless-tool batch on the v1 commands transport.\n *\n * {@link headlessToolsBatchResumeCommand} still returns a legacy\n * `{ resume }` command shape for callers on the old runs/stream API.\n * On v1 `StreamController`, that payload must be sent through\n * {@link HeadlessToolResumeController.respond} /\n * {@link HeadlessToolResumeController.respondAll} — not `submit(null,\n * { command })`, which dispatches `run.start` without a resume value.\n */\nexport function applyHeadlessToolResumeCommand(\n controller: HeadlessToolResumeController,\n command: HeadlessToolResumeCommand\n): Promise<void> {\n const { resume, keyedByInterruptId } = command;\n if (resume == null) return Promise.resolve();\n\n const useRespondAll =\n keyedByInterruptId === true ||\n (keyedByInterruptId !== false && isInterruptIdKeyedResume(resume));\n\n if (useRespondAll) {\n return controller.respondAll(resume as Record<string, unknown>);\n }\n\n return controller.respond(resume);\n}\n\n/**\n * True when a resume payload is keyed by protocol interrupt id at the top\n * level (for {@link HeadlessToolResumeController.respondAll}).\n *\n * Prefer {@link HeadlessToolResumeCommand.keyedByInterruptId} from\n * {@link headlessToolsBatchResumeCommand} when ids are not inferable\n * (for example `{ \"int-1\": result }` without a nested tool-call map).\n *\n * When `interrupts` is provided, a single top-level key that matches a\n * known protocol interrupt id is treated as interrupt-keyed.\n */\nexport function isInterruptIdKeyedResume(\n resume: unknown,\n interrupts?: readonly ProtocolInterruptEntry[]\n): boolean {\n if (resume == null || typeof resume !== \"object\" || Array.isArray(resume)) {\n return false;\n }\n const record = resume as Record<string, unknown>;\n const keys = Object.keys(record);\n if (keys.length === 0) return false;\n if (keys.length > 1) return true;\n\n const [key] = keys;\n if (interrupts?.length) {\n const knownIds = new Set(interrupts.map((entry) => entry.interruptId));\n if (knownIds.has(key!)) return true;\n }\n\n // Legacy graph task ids from `values.__interrupt__`.\n return /^[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, interrupts)) {\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\n/**\n * Protocol interrupt entry tracked on {@link ThreadStream.interrupts}.\n * Used by {@link resolveInterruptTargetForHeadlessResume} when `respond()`\n * omits an explicit target.\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: HeadlessToolResumeCommand) => 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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"}
@@ -58,17 +58,46 @@ declare function handleHeadlessToolInterrupt(interrupt: HeadlessToolInterrupt, t
58
58
  toolCallId: string | undefined;
59
59
  value: unknown;
60
60
  }>;
61
+ /**
62
+ * Resume command produced by {@link headlessToolResumeCommand} /
63
+ * {@link headlessToolsBatchResumeCommand}.
64
+ */
65
+ interface HeadlessToolResumeCommand {
66
+ resume: unknown;
67
+ /**
68
+ * When true, top-level {@link resume} keys are protocol interrupt ids and
69
+ * must be sent through {@link HeadlessToolResumeController.respondAll}.
70
+ */
71
+ keyedByInterruptId?: boolean;
72
+ }
61
73
  declare function headlessToolResumeCommand(result: {
62
74
  toolCallId: string | undefined;
63
75
  value: unknown;
64
- }): {
65
- resume: unknown;
66
- };
76
+ }): HeadlessToolResumeCommand;
77
+ /**
78
+ * Minimal controller surface for servicing a headless-tool resume on the
79
+ * v1 stream protocol (`input.respond`).
80
+ */
81
+ interface HeadlessToolResumeController {
82
+ respond: (response: unknown, options?: {
83
+ interruptId?: string;
84
+ }) => Promise<void>;
85
+ respondAll: (responsesById: Record<string, unknown>) => Promise<void>;
86
+ }
87
+ /**
88
+ * Resume a headless-tool batch on the v1 commands transport.
89
+ *
90
+ * {@link headlessToolsBatchResumeCommand} still returns a legacy
91
+ * `{ resume }` command shape for callers on the old runs/stream API.
92
+ * On v1 `StreamController`, that payload must be sent through
93
+ * {@link HeadlessToolResumeController.respond} /
94
+ * {@link HeadlessToolResumeController.respondAll} — not `submit(null,
95
+ * { command })`, which dispatches `run.start` without a resume value.
96
+ */
97
+ declare function applyHeadlessToolResumeCommand(controller: HeadlessToolResumeController, command: HeadlessToolResumeCommand): Promise<void>;
67
98
  interface FlushPendingHeadlessToolInterruptsOptions {
68
99
  onTool?: OnToolCallback;
69
- resumeSubmit: (command: {
70
- resume: unknown;
71
- }) => void | Promise<void>;
100
+ resumeSubmit: (command: HeadlessToolResumeCommand) => void | Promise<void>;
72
101
  defer?: (run: () => void) => void;
73
102
  }
74
103
  /**
@@ -85,5 +114,5 @@ declare function scheduleCoalescedHeadlessToolFlush(handledIds: Set<string>, run
85
114
  */
86
115
  declare function flushPendingHeadlessToolInterrupts(values: Record<string, unknown> | null | undefined, tools: HeadlessToolImplementation[] | undefined, handledIds: Set<string>, options: FlushPendingHeadlessToolInterruptsOptions): void;
87
116
  //#endregion
88
- export { AnyHeadlessToolImplementation, FlushPendingHeadlessToolInterruptsOptions, HeadlessToolImplementation, HeadlessToolInterrupt, OnToolCallback, ToolEvent, executeHeadlessTool, filterOutHeadlessToolInterrupts, findHeadlessTool, flushPendingHeadlessToolInterrupts, handleHeadlessToolInterrupt, headlessToolResumeCommand, isHeadlessToolInterrupt, parseHeadlessToolInterruptPayload, scheduleCoalescedHeadlessToolFlush };
117
+ export { AnyHeadlessToolImplementation, FlushPendingHeadlessToolInterruptsOptions, HeadlessToolImplementation, HeadlessToolInterrupt, HeadlessToolResumeCommand, HeadlessToolResumeController, OnToolCallback, ToolEvent, applyHeadlessToolResumeCommand, executeHeadlessTool, filterOutHeadlessToolInterrupts, findHeadlessTool, flushPendingHeadlessToolInterrupts, handleHeadlessToolInterrupt, headlessToolResumeCommand, isHeadlessToolInterrupt, parseHeadlessToolInterruptPayload, scheduleCoalescedHeadlessToolFlush };
89
118
  //# 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,UAsJW,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;;;;AA5NF;iBAgPgB,kCAAA,CACd,MAAA,EAAQ,MAAA,sCACR,KAAA,EAAO,0BAAA,gBACP,UAAA,EAAY,GAAA,UACZ,OAAA,EAAS,yCAAA"}
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;AAzE7C;;;;AAAA,UAwHiB,yBAAA;EACf,MAAA;EAxHqB;;;;EA6HrB,kBAAA;AAAA;AAAA,iBAGc,yBAAA,CAA0B,MAAA;EACxC,UAAA;EACA,KAAA;AAAA,IACE,yBAAA;;;;;UAmDa,4BAAA;EACf,OAAA,GACE,QAAA,WACA,OAAA;IAAY,WAAA;EAAA,MACT,OAAA;EACL,UAAA,GAAa,aAAA,EAAe,MAAA,sBAA4B,OAAA;AAAA;;;;AApK1D;;;;;;;iBAiLgB,8BAAA,CACd,UAAA,EAAY,4BAAA,EACZ,OAAA,EAAS,yBAAA,GACR,OAAA;AAAA,UA2Ic,yCAAA;EACf,MAAA,GAAS,cAAA;EACT,YAAA,GAAe,OAAA,EAAS,yBAAA,YAAqC,OAAA;EAC7D,KAAA,IAAS,GAAA;AAAA;;;AAzNX;;;;;iBAwOgB,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"}
@@ -58,17 +58,46 @@ declare function handleHeadlessToolInterrupt(interrupt: HeadlessToolInterrupt, t
58
58
  toolCallId: string | undefined;
59
59
  value: unknown;
60
60
  }>;
61
+ /**
62
+ * Resume command produced by {@link headlessToolResumeCommand} /
63
+ * {@link headlessToolsBatchResumeCommand}.
64
+ */
65
+ interface HeadlessToolResumeCommand {
66
+ resume: unknown;
67
+ /**
68
+ * When true, top-level {@link resume} keys are protocol interrupt ids and
69
+ * must be sent through {@link HeadlessToolResumeController.respondAll}.
70
+ */
71
+ keyedByInterruptId?: boolean;
72
+ }
61
73
  declare function headlessToolResumeCommand(result: {
62
74
  toolCallId: string | undefined;
63
75
  value: unknown;
64
- }): {
65
- resume: unknown;
66
- };
76
+ }): HeadlessToolResumeCommand;
77
+ /**
78
+ * Minimal controller surface for servicing a headless-tool resume on the
79
+ * v1 stream protocol (`input.respond`).
80
+ */
81
+ interface HeadlessToolResumeController {
82
+ respond: (response: unknown, options?: {
83
+ interruptId?: string;
84
+ }) => Promise<void>;
85
+ respondAll: (responsesById: Record<string, unknown>) => Promise<void>;
86
+ }
87
+ /**
88
+ * Resume a headless-tool batch on the v1 commands transport.
89
+ *
90
+ * {@link headlessToolsBatchResumeCommand} still returns a legacy
91
+ * `{ resume }` command shape for callers on the old runs/stream API.
92
+ * On v1 `StreamController`, that payload must be sent through
93
+ * {@link HeadlessToolResumeController.respond} /
94
+ * {@link HeadlessToolResumeController.respondAll} — not `submit(null,
95
+ * { command })`, which dispatches `run.start` without a resume value.
96
+ */
97
+ declare function applyHeadlessToolResumeCommand(controller: HeadlessToolResumeController, command: HeadlessToolResumeCommand): Promise<void>;
67
98
  interface FlushPendingHeadlessToolInterruptsOptions {
68
99
  onTool?: OnToolCallback;
69
- resumeSubmit: (command: {
70
- resume: unknown;
71
- }) => void | Promise<void>;
100
+ resumeSubmit: (command: HeadlessToolResumeCommand) => void | Promise<void>;
72
101
  defer?: (run: () => void) => void;
73
102
  }
74
103
  /**
@@ -85,5 +114,5 @@ declare function scheduleCoalescedHeadlessToolFlush(handledIds: Set<string>, run
85
114
  */
86
115
  declare function flushPendingHeadlessToolInterrupts(values: Record<string, unknown> | null | undefined, tools: HeadlessToolImplementation[] | undefined, handledIds: Set<string>, options: FlushPendingHeadlessToolInterruptsOptions): void;
87
116
  //#endregion
88
- export { AnyHeadlessToolImplementation, FlushPendingHeadlessToolInterruptsOptions, HeadlessToolImplementation, HeadlessToolInterrupt, OnToolCallback, ToolEvent, executeHeadlessTool, filterOutHeadlessToolInterrupts, findHeadlessTool, flushPendingHeadlessToolInterrupts, handleHeadlessToolInterrupt, headlessToolResumeCommand, isHeadlessToolInterrupt, parseHeadlessToolInterruptPayload, scheduleCoalescedHeadlessToolFlush };
117
+ export { AnyHeadlessToolImplementation, FlushPendingHeadlessToolInterruptsOptions, HeadlessToolImplementation, HeadlessToolInterrupt, HeadlessToolResumeCommand, HeadlessToolResumeController, OnToolCallback, ToolEvent, applyHeadlessToolResumeCommand, executeHeadlessTool, filterOutHeadlessToolInterrupts, findHeadlessTool, flushPendingHeadlessToolInterrupts, handleHeadlessToolInterrupt, headlessToolResumeCommand, isHeadlessToolInterrupt, parseHeadlessToolInterruptPayload, scheduleCoalescedHeadlessToolFlush };
89
118
  //# 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,UAsJW,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;;;;AA5NF;iBAgPgB,kCAAA,CACd,MAAA,EAAQ,MAAA,sCACR,KAAA,EAAO,0BAAA,gBACP,UAAA,EAAY,GAAA,UACZ,OAAA,EAAS,yCAAA"}
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;AAzE7C;;;;AAAA,UAwHiB,yBAAA;EACf,MAAA;EAxHqB;;;;EA6HrB,kBAAA;AAAA;AAAA,iBAGc,yBAAA,CAA0B,MAAA;EACxC,UAAA;EACA,KAAA;AAAA,IACE,yBAAA;;;;;UAmDa,4BAAA;EACf,OAAA,GACE,QAAA,WACA,OAAA;IAAY,WAAA;EAAA,MACT,OAAA;EACL,UAAA,GAAa,aAAA,EAAe,MAAA,sBAA4B,OAAA;AAAA;;;;AApK1D;;;;;;;iBAiLgB,8BAAA,CACd,UAAA,EAAY,4BAAA,EACZ,OAAA,EAAS,yBAAA,GACR,OAAA;AAAA,UA2Ic,yCAAA;EACf,MAAA,GAAS,cAAA;EACT,YAAA,GAAe,OAAA,EAAS,yBAAA,YAAqC,OAAA;EAC7D,KAAA,IAAS,GAAA;AAAA;;;AAzNX;;;;;iBAwOgB,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"}
@@ -97,7 +97,10 @@ async function handleHeadlessToolInterrupt(interrupt, tools, onTool) {
97
97
  };
98
98
  }
99
99
  function headlessToolResumeCommand(result) {
100
- return { resume: result.toolCallId ? { [result.toolCallId]: result.value } : result.value };
100
+ return {
101
+ resume: result.toolCallId ? { [result.toolCallId]: result.value } : result.value,
102
+ keyedByInterruptId: false
103
+ };
101
104
  }
102
105
  /**
103
106
  * Merge headless-tool results into one resume command. Use interrupt-id keys
@@ -105,7 +108,10 @@ function headlessToolResumeCommand(result) {
105
108
  * a pending interrupt from mutable client state.
106
109
  */
107
110
  function headlessToolsBatchResumeCommand(entries) {
108
- if (entries.length === 0) return { resume: {} };
111
+ if (entries.length === 0) return {
112
+ resume: {},
113
+ keyedByInterruptId: false
114
+ };
109
115
  if (!entries.every((entry) => entry.interruptId.length > 0) && entries.length === 1) {
110
116
  const [entry] = entries;
111
117
  return headlessToolResumeCommand({
@@ -118,7 +124,49 @@ function headlessToolsBatchResumeCommand(entries) {
118
124
  if (entry.interruptId.length === 0) continue;
119
125
  resume[entry.interruptId] = entry.toolCallId != null && entry.toolCallId.length > 0 ? { [entry.toolCallId]: entry.value } : entry.value;
120
126
  }
121
- return { resume };
127
+ return {
128
+ resume,
129
+ keyedByInterruptId: true
130
+ };
131
+ }
132
+ /**
133
+ * Resume a headless-tool batch on the v1 commands transport.
134
+ *
135
+ * {@link headlessToolsBatchResumeCommand} still returns a legacy
136
+ * `{ resume }` command shape for callers on the old runs/stream API.
137
+ * On v1 `StreamController`, that payload must be sent through
138
+ * {@link HeadlessToolResumeController.respond} /
139
+ * {@link HeadlessToolResumeController.respondAll} — not `submit(null,
140
+ * { command })`, which dispatches `run.start` without a resume value.
141
+ */
142
+ function applyHeadlessToolResumeCommand(controller, command) {
143
+ const { resume, keyedByInterruptId } = command;
144
+ if (resume == null) return Promise.resolve();
145
+ if (keyedByInterruptId === true || keyedByInterruptId !== false && isInterruptIdKeyedResume(resume)) return controller.respondAll(resume);
146
+ return controller.respond(resume);
147
+ }
148
+ /**
149
+ * True when a resume payload is keyed by protocol interrupt id at the top
150
+ * level (for {@link HeadlessToolResumeController.respondAll}).
151
+ *
152
+ * Prefer {@link HeadlessToolResumeCommand.keyedByInterruptId} from
153
+ * {@link headlessToolsBatchResumeCommand} when ids are not inferable
154
+ * (for example `{ "int-1": result }` without a nested tool-call map).
155
+ *
156
+ * When `interrupts` is provided, a single top-level key that matches a
157
+ * known protocol interrupt id is treated as interrupt-keyed.
158
+ */
159
+ function isInterruptIdKeyedResume(resume, interrupts) {
160
+ if (resume == null || typeof resume !== "object" || Array.isArray(resume)) return false;
161
+ const record = resume;
162
+ const keys = Object.keys(record);
163
+ if (keys.length === 0) return false;
164
+ if (keys.length > 1) return true;
165
+ const [key] = keys;
166
+ if (interrupts?.length) {
167
+ if (new Set(interrupts.map((entry) => entry.interruptId)).has(key)) return true;
168
+ }
169
+ return /^[0-9a-f]{32}$/i.test(key);
122
170
  }
123
171
  /**
124
172
  * Reads the tool-call id from a headless-tool resume command shaped as
@@ -223,6 +271,6 @@ function flushPendingHeadlessToolInterrupts(values, tools, handledIds, options)
223
271
  });
224
272
  }
225
273
  //#endregion
226
- export { executeHeadlessTool, filterOutHeadlessToolInterrupts, findHeadlessTool, flushPendingHeadlessToolInterrupts, handleHeadlessToolInterrupt, headlessToolResumeCommand, isHeadlessToolInterrupt, parseHeadlessToolInterruptPayload, resolveInterruptTargetForHeadlessResume, scheduleCoalescedHeadlessToolFlush };
274
+ export { applyHeadlessToolResumeCommand, executeHeadlessTool, filterOutHeadlessToolInterrupts, findHeadlessTool, flushPendingHeadlessToolInterrupts, handleHeadlessToolInterrupt, headlessToolResumeCommand, isHeadlessToolInterrupt, parseHeadlessToolInterruptPayload, resolveInterruptTargetForHeadlessResume, scheduleCoalescedHeadlessToolFlush };
227
275
 
228
276
  //# 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\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\n/**\n * Protocol interrupt entry tracked on {@link ThreadStream.interrupts}.\n * Used by {@link resolveInterruptTargetForHeadlessResume} when `respond()`\n * omits an explicit target.\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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"}
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\n/**\n * Resume command produced by {@link headlessToolResumeCommand} /\n * {@link headlessToolsBatchResumeCommand}.\n */\nexport interface HeadlessToolResumeCommand {\n resume: unknown;\n /**\n * When true, top-level {@link resume} keys are protocol interrupt ids and\n * must be sent through {@link HeadlessToolResumeController.respondAll}.\n */\n keyedByInterruptId?: boolean;\n}\n\nexport function headlessToolResumeCommand(result: {\n toolCallId: string | undefined;\n value: unknown;\n}): HeadlessToolResumeCommand {\n return {\n resume: result.toolCallId\n ? { [result.toolCallId]: result.value }\n : result.value,\n keyedByInterruptId: false,\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): HeadlessToolResumeCommand {\n if (entries.length === 0) {\n return { resume: {}, keyedByInterruptId: false };\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, keyedByInterruptId: true };\n}\n\n/**\n * Minimal controller surface for servicing a headless-tool resume on the\n * v1 stream protocol (`input.respond`).\n */\nexport interface HeadlessToolResumeController {\n respond: (\n response: unknown,\n options?: { interruptId?: string }\n ) => Promise<void>;\n respondAll: (responsesById: Record<string, unknown>) => Promise<void>;\n}\n\n/**\n * Resume a headless-tool batch on the v1 commands transport.\n *\n * {@link headlessToolsBatchResumeCommand} still returns a legacy\n * `{ resume }` command shape for callers on the old runs/stream API.\n * On v1 `StreamController`, that payload must be sent through\n * {@link HeadlessToolResumeController.respond} /\n * {@link HeadlessToolResumeController.respondAll} — not `submit(null,\n * { command })`, which dispatches `run.start` without a resume value.\n */\nexport function applyHeadlessToolResumeCommand(\n controller: HeadlessToolResumeController,\n command: HeadlessToolResumeCommand\n): Promise<void> {\n const { resume, keyedByInterruptId } = command;\n if (resume == null) return Promise.resolve();\n\n const useRespondAll =\n keyedByInterruptId === true ||\n (keyedByInterruptId !== false && isInterruptIdKeyedResume(resume));\n\n if (useRespondAll) {\n return controller.respondAll(resume as Record<string, unknown>);\n }\n\n return controller.respond(resume);\n}\n\n/**\n * True when a resume payload is keyed by protocol interrupt id at the top\n * level (for {@link HeadlessToolResumeController.respondAll}).\n *\n * Prefer {@link HeadlessToolResumeCommand.keyedByInterruptId} from\n * {@link headlessToolsBatchResumeCommand} when ids are not inferable\n * (for example `{ \"int-1\": result }` without a nested tool-call map).\n *\n * When `interrupts` is provided, a single top-level key that matches a\n * known protocol interrupt id is treated as interrupt-keyed.\n */\nexport function isInterruptIdKeyedResume(\n resume: unknown,\n interrupts?: readonly ProtocolInterruptEntry[]\n): boolean {\n if (resume == null || typeof resume !== \"object\" || Array.isArray(resume)) {\n return false;\n }\n const record = resume as Record<string, unknown>;\n const keys = Object.keys(record);\n if (keys.length === 0) return false;\n if (keys.length > 1) return true;\n\n const [key] = keys;\n if (interrupts?.length) {\n const knownIds = new Set(interrupts.map((entry) => entry.interruptId));\n if (knownIds.has(key!)) return true;\n }\n\n // Legacy graph task ids from `values.__interrupt__`.\n return /^[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, interrupts)) {\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\n/**\n * Protocol interrupt entry tracked on {@link ThreadStream.interrupts}.\n * Used by {@link resolveInterruptTargetForHeadlessResume} when `respond()`\n * omits an explicit target.\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: HeadlessToolResumeCommand) => 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
@@ -26,6 +26,7 @@ exports.ProtocolWebSocketTransportAdapter = require_websocket.ProtocolWebSocketT
26
26
  exports.StreamError = require_errors.StreamError;
27
27
  exports.SubscriptionHandle = require_index.SubscriptionHandle;
28
28
  exports.ThreadStream = require_index.ThreadStream;
29
+ exports.applyHeadlessToolResumeCommand = require_headless_tools.applyHeadlessToolResumeCommand;
29
30
  exports.executeHeadlessTool = require_headless_tools.executeHeadlessTool;
30
31
  exports.filterOutHeadlessToolInterrupts = require_headless_tools.filterOutHeadlessToolInterrupts;
31
32
  exports.findHeadlessTool = require_headless_tools.findHeadlessTool;
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, scheduleCoalescedHeadlessToolFlush } from "./headless-tools.cjs";
19
+ import { AnyHeadlessToolImplementation, FlushPendingHeadlessToolInterruptsOptions, HeadlessToolImplementation, HeadlessToolInterrupt, HeadlessToolResumeCommand, HeadlessToolResumeController, OnToolCallback, ToolEvent, applyHeadlessToolResumeCommand, 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, scheduleCoalescedHeadlessToolFlush };
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, type HeadlessToolResumeCommand, type HeadlessToolResumeController, 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, applyHeadlessToolResumeCommand, 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, scheduleCoalescedHeadlessToolFlush } from "./headless-tools.js";
19
+ import { AnyHeadlessToolImplementation, FlushPendingHeadlessToolInterruptsOptions, HeadlessToolImplementation, HeadlessToolInterrupt, HeadlessToolResumeCommand, HeadlessToolResumeController, OnToolCallback, ToolEvent, applyHeadlessToolResumeCommand, 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, scheduleCoalescedHeadlessToolFlush };
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, type HeadlessToolResumeCommand, type HeadlessToolResumeController, 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, applyHeadlessToolResumeCommand, 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, scheduleCoalescedHeadlessToolFlush } from "./headless-tools.js";
7
+ import { applyHeadlessToolResumeCommand, 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, scheduleCoalescedHeadlessToolFlush };
16
+ export { Client, HttpAgentServerAdapter, MediaAssembler, MediaAssemblyError, MessageAssembler, NAMESPACE_SEPARATOR, ProtocolError, ProtocolSseTransportAdapter, ProtocolWebSocketTransportAdapter, StreamError, SubscriptionHandle, ThreadStream, applyHeadlessToolResumeCommand, executeHeadlessTool, filterOutHeadlessToolInterrupts, findHeadlessTool, flushPendingHeadlessToolInterrupts, getApiKey, handleHeadlessToolInterrupt, headlessToolResumeCommand, isHeadlessToolInterrupt, overrideFetchImplementation, parseHeadlessToolInterruptPayload, scheduleCoalescedHeadlessToolFlush };
@@ -4,9 +4,9 @@ const require_sse = require("../utils/sse.cjs");
4
4
  const require_stream = require("../utils/stream.cjs");
5
5
  const require_messages = require("../ui/messages.cjs");
6
6
  const require_headless_tools = require("../headless-tools.cjs");
7
+ const require_interrupts = require("../ui/interrupts.cjs");
7
8
  const require_tools = require("../utils/tools.cjs");
8
9
  const require_manager = require("../ui/manager.cjs");
9
- const require_interrupts = require("../ui/interrupts.cjs");
10
10
  const require_thread = require("./thread.cjs");
11
11
  let react = require("react");
12
12
  //#region src/react/stream.custom.tsx
@@ -3,9 +3,9 @@ import { BytesLineDecoder, SSEDecoder } from "../utils/sse.js";
3
3
  import { IterableReadableStream } from "../utils/stream.js";
4
4
  import { MessageTupleManager } from "../ui/messages.js";
5
5
  import { flushPendingHeadlessToolInterrupts } from "../headless-tools.js";
6
+ import { userFacingInterruptsFromValuesArray } from "../ui/interrupts.js";
6
7
  import { getToolCallsWithResults } from "../utils/tools.js";
7
8
  import { StreamManager } from "../ui/manager.js";
8
- import { userFacingInterruptsFromValuesArray } from "../ui/interrupts.js";
9
9
  import { useControllableThreadId } from "./thread.js";
10
10
  import { useEffect, useRef, useState, useSyncExternalStore } from "react";
11
11
  //#region src/react/stream.custom.tsx