@langchain/langgraph-sdk 1.9.8 → 1.9.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/client/stream/index.cjs +37 -5
  2. package/dist/client/stream/index.cjs.map +1 -1
  3. package/dist/client/stream/index.d.cts +7 -14
  4. package/dist/client/stream/index.d.cts.map +1 -1
  5. package/dist/client/stream/index.d.ts +7 -14
  6. package/dist/client/stream/index.d.ts.map +1 -1
  7. package/dist/client/stream/index.js +37 -5
  8. package/dist/client/stream/index.js.map +1 -1
  9. package/dist/client/stream/transport/utils.cjs +2 -1
  10. package/dist/client/stream/transport/utils.cjs.map +1 -1
  11. package/dist/client/stream/transport/utils.js +2 -1
  12. package/dist/client/stream/transport/utils.js.map +1 -1
  13. package/dist/client/stream/transport/websocket.cjs +1 -1
  14. package/dist/client/stream/transport/websocket.cjs.map +1 -1
  15. package/dist/client/stream/transport/websocket.d.cts.map +1 -1
  16. package/dist/client/stream/transport/websocket.d.ts.map +1 -1
  17. package/dist/client/stream/transport/websocket.js +2 -2
  18. package/dist/client/stream/transport/websocket.js.map +1 -1
  19. package/dist/client/stream/types.d.cts +8 -3
  20. package/dist/client/stream/types.d.cts.map +1 -1
  21. package/dist/client/stream/types.d.ts +8 -3
  22. package/dist/client/stream/types.d.ts.map +1 -1
  23. package/dist/headless-tools.cjs +0 -24
  24. package/dist/headless-tools.cjs.map +1 -1
  25. package/dist/headless-tools.d.cts.map +1 -1
  26. package/dist/headless-tools.d.ts.map +1 -1
  27. package/dist/headless-tools.js +1 -24
  28. package/dist/headless-tools.js.map +1 -1
  29. package/dist/stream/controller.cjs +156 -19
  30. package/dist/stream/controller.cjs.map +1 -1
  31. package/dist/stream/controller.d.cts +113 -10
  32. package/dist/stream/controller.d.cts.map +1 -1
  33. package/dist/stream/controller.d.ts +113 -10
  34. package/dist/stream/controller.d.ts.map +1 -1
  35. package/dist/stream/controller.js +157 -20
  36. package/dist/stream/controller.js.map +1 -1
  37. package/dist/stream/index.d.cts +2 -2
  38. package/dist/stream/index.d.ts +2 -2
  39. package/dist/stream/message-metadata-tracker.cjs +1 -1
  40. package/dist/stream/message-metadata-tracker.cjs.map +1 -1
  41. package/dist/stream/message-metadata-tracker.d.cts +1 -1
  42. package/dist/stream/message-metadata-tracker.d.ts +1 -1
  43. package/dist/stream/message-metadata-tracker.js +1 -1
  44. package/dist/stream/message-metadata-tracker.js.map +1 -1
  45. package/dist/stream/submit-coordinator.cjs +45 -79
  46. package/dist/stream/submit-coordinator.cjs.map +1 -1
  47. package/dist/stream/submit-coordinator.d.cts.map +1 -1
  48. package/dist/stream/submit-coordinator.d.ts.map +1 -1
  49. package/dist/stream/submit-coordinator.js +45 -79
  50. package/dist/stream/submit-coordinator.js.map +1 -1
  51. package/dist/stream/types.d.cts +52 -30
  52. package/dist/stream/types.d.cts.map +1 -1
  53. package/dist/stream/types.d.ts +52 -30
  54. package/dist/stream/types.d.ts.map +1 -1
  55. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","names":[],"sources":["../../../../src/client/stream/transport/utils.ts"],"sourcesContent":["import type { HeaderValue } from \"./types.js\";\nimport type { CommandResponse, ErrorResponse } from \"@langchain/protocol\";\n\nexport const isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null;\n\nexport const toAbsoluteUrl = (apiUrl: string, path: string) =>\n new URL(path, apiUrl.endsWith(\"/\") ? apiUrl : `${apiUrl}/`);\n\nexport const toError = (error: unknown) =>\n // oxlint-disable-next-line no-instanceof/no-instanceof\n error instanceof Error ? error : new Error(String(error));\n\nexport const toWebSocketUrl = (apiUrl: string): string => {\n // Extract path from the input (e.g. \"http://host/threads/X/stream\") and\n // swap the scheme to ws/wss. The caller passes a fully-formed URL\n // with the desired path.\n const url = new URL(apiUrl);\n url.protocol = url.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n url.search = \"\";\n url.hash = \"\";\n return url.toString();\n};\n\nexport const hasHeaders = (headers?: Record<string, HeaderValue>) =>\n Object.values(headers ?? {}).some((value) => value != null);\n\nexport function mergeHeaders(\n ...headerGroups: Array<\n HeadersInit | Record<string, HeaderValue> | undefined | null\n >\n): Headers {\n const merged = new Headers();\n\n for (const group of headerGroups) {\n if (!group) {\n continue;\n }\n\n // oxlint-disable-next-line no-instanceof/no-instanceof\n if (group instanceof Headers) {\n group.forEach((value, key) => {\n merged.set(key, value);\n });\n continue;\n }\n\n if (Array.isArray(group)) {\n for (const [key, value] of group) {\n if (value == null) {\n merged.delete(key);\n } else {\n merged.set(key, value);\n }\n }\n continue;\n }\n\n for (const [key, value] of Object.entries(group)) {\n if (value == null) {\n merged.delete(key);\n } else {\n merged.set(key, value);\n }\n }\n }\n\n return merged;\n}\n\nexport function isProtocolResponse(\n value: unknown\n): value is CommandResponse | ErrorResponse {\n return (\n isRecord(value) &&\n typeof value.type === \"string\" &&\n (value.type === \"success\" || value.type === \"error\")\n );\n}\n"],"mappings":";AAGA,MAAa,YAAY,UACvB,OAAO,UAAU,YAAY,UAAU;AAEzC,MAAa,iBAAiB,QAAgB,SAC5C,IAAI,IAAI,MAAM,OAAO,SAAS,IAAI,GAAG,SAAS,GAAG,OAAO,GAAG;AAE7D,MAAa,WAAW,UAEtB,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC;AAE3D,MAAa,kBAAkB,WAA2B;CAIxD,MAAM,MAAM,IAAI,IAAI,OAAO;AAC3B,KAAI,WAAW,IAAI,aAAa,WAAW,SAAS;AACpD,KAAI,SAAS;AACb,KAAI,OAAO;AACX,QAAO,IAAI,UAAU;;AAGvB,MAAa,cAAc,YACzB,OAAO,OAAO,WAAW,EAAE,CAAC,CAAC,MAAM,UAAU,SAAS,KAAK;AAE7D,SAAgB,aACd,GAAG,cAGM;CACT,MAAM,SAAS,IAAI,SAAS;AAE5B,MAAK,MAAM,SAAS,cAAc;AAChC,MAAI,CAAC,MACH;AAIF,MAAI,iBAAiB,SAAS;AAC5B,SAAM,SAAS,OAAO,QAAQ;AAC5B,WAAO,IAAI,KAAK,MAAM;KACtB;AACF;;AAGF,MAAI,MAAM,QAAQ,MAAM,EAAE;AACxB,QAAK,MAAM,CAAC,KAAK,UAAU,MACzB,KAAI,SAAS,KACX,QAAO,OAAO,IAAI;OAElB,QAAO,IAAI,KAAK,MAAM;AAG1B;;AAGF,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,CAC9C,KAAI,SAAS,KACX,QAAO,OAAO,IAAI;MAElB,QAAO,IAAI,KAAK,MAAM;;AAK5B,QAAO;;AAGT,SAAgB,mBACd,OAC0C;AAC1C,QACE,SAAS,MAAM,IACf,OAAO,MAAM,SAAS,aACrB,MAAM,SAAS,aAAa,MAAM,SAAS"}
1
+ {"version":3,"file":"utils.js","names":[],"sources":["../../../../src/client/stream/transport/utils.ts"],"sourcesContent":["import type { HeaderValue } from \"./types.js\";\nimport type { CommandResponse, ErrorResponse } from \"@langchain/protocol\";\n\nexport const isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null;\n\n/** Match {@link BaseClient.prepareFetchOptions}: preserve any apiUrl path prefix. */\nexport const toAbsoluteUrl = (apiUrl: string, path: string) =>\n new URL(`${apiUrl.replace(/\\/$/, \"\")}${path}`);\n\nexport const toError = (error: unknown) =>\n // oxlint-disable-next-line no-instanceof/no-instanceof\n error instanceof Error ? error : new Error(String(error));\n\nexport const toWebSocketUrl = (apiUrl: string): string => {\n // Extract path from the input (e.g. \"http://host/threads/X/stream\") and\n // swap the scheme to ws/wss. The caller passes a fully-formed URL\n // with the desired path.\n const url = new URL(apiUrl);\n url.protocol = url.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n url.search = \"\";\n url.hash = \"\";\n return url.toString();\n};\n\nexport const hasHeaders = (headers?: Record<string, HeaderValue>) =>\n Object.values(headers ?? {}).some((value) => value != null);\n\nexport function mergeHeaders(\n ...headerGroups: Array<\n HeadersInit | Record<string, HeaderValue> | undefined | null\n >\n): Headers {\n const merged = new Headers();\n\n for (const group of headerGroups) {\n if (!group) {\n continue;\n }\n\n // oxlint-disable-next-line no-instanceof/no-instanceof\n if (group instanceof Headers) {\n group.forEach((value, key) => {\n merged.set(key, value);\n });\n continue;\n }\n\n if (Array.isArray(group)) {\n for (const [key, value] of group) {\n if (value == null) {\n merged.delete(key);\n } else {\n merged.set(key, value);\n }\n }\n continue;\n }\n\n for (const [key, value] of Object.entries(group)) {\n if (value == null) {\n merged.delete(key);\n } else {\n merged.set(key, value);\n }\n }\n }\n\n return merged;\n}\n\nexport function isProtocolResponse(\n value: unknown\n): value is CommandResponse | ErrorResponse {\n return (\n isRecord(value) &&\n typeof value.type === \"string\" &&\n (value.type === \"success\" || value.type === \"error\")\n );\n}\n"],"mappings":";AAGA,MAAa,YAAY,UACvB,OAAO,UAAU,YAAY,UAAU;;AAGzC,MAAa,iBAAiB,QAAgB,SAC5C,IAAI,IAAI,GAAG,OAAO,QAAQ,OAAO,GAAG,GAAG,OAAO;AAEhD,MAAa,WAAW,UAEtB,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC;AAE3D,MAAa,kBAAkB,WAA2B;CAIxD,MAAM,MAAM,IAAI,IAAI,OAAO;AAC3B,KAAI,WAAW,IAAI,aAAa,WAAW,SAAS;AACpD,KAAI,SAAS;AACb,KAAI,OAAO;AACX,QAAO,IAAI,UAAU;;AAGvB,MAAa,cAAc,YACzB,OAAO,OAAO,WAAW,EAAE,CAAC,CAAC,MAAM,UAAU,SAAS,KAAK;AAE7D,SAAgB,aACd,GAAG,cAGM;CACT,MAAM,SAAS,IAAI,SAAS;AAE5B,MAAK,MAAM,SAAS,cAAc;AAChC,MAAI,CAAC,MACH;AAIF,MAAI,iBAAiB,SAAS;AAC5B,SAAM,SAAS,OAAO,QAAQ;AAC5B,WAAO,IAAI,KAAK,MAAM;KACtB;AACF;;AAGF,MAAI,MAAM,QAAQ,MAAM,EAAE;AACxB,QAAK,MAAM,CAAC,KAAK,UAAU,MACzB,KAAI,SAAS,KACX,QAAO,OAAO,IAAI;OAElB,QAAO,IAAI,KAAK,MAAM;AAG1B;;AAGF,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,CAC9C,KAAI,SAAS,KACX,QAAO,OAAO,IAAI;MAElB,QAAO,IAAI,KAAK,MAAM;;AAK5B,QAAO;;AAGT,SAAgB,mBACd,OAC0C;AAC1C,QACE,SAAS,MAAM,IACf,OAAO,MAAM,SAAS,aACrB,MAAM,SAAS,aAAa,MAAM,SAAS"}
@@ -29,7 +29,7 @@ var ProtocolWebSocketTransportAdapter = class {
29
29
  async open() {
30
30
  if (this.socket != null) return;
31
31
  this.assertBrowserSafeTransportConfig();
32
- const wsUrl = require_utils.toWebSocketUrl(new URL(this.streamUrl, this.apiUrl.endsWith("/") ? this.apiUrl : `${this.apiUrl}/`).toString());
32
+ const wsUrl = require_utils.toWebSocketUrl(require_utils.toAbsoluteUrl(this.apiUrl, this.streamUrl).toString());
33
33
  const socket = this.webSocketFactory(wsUrl);
34
34
  this.socket = socket;
35
35
  this.closed = false;
@@ -1 +1 @@
1
- {"version":3,"file":"websocket.cjs","names":["AsyncQueue","toWebSocketUrl","hasHeaders","toError","isRecord"],"sources":["../../../../src/client/stream/transport/websocket.ts"],"sourcesContent":["import { AsyncQueue } from \"./queue.js\";\nimport type {\n Message,\n Command,\n CommandResponse,\n ErrorResponse,\n} from \"@langchain/protocol\";\n\nimport { toWebSocketUrl, isRecord, hasHeaders, toError } from \"./utils.js\";\nimport type {\n HeaderValue,\n ProtocolRequestHook,\n PendingResponse,\n ProtocolWebSocketTransportOptions,\n} from \"./types.js\";\nimport type { TransportAdapter } from \"../transport.js\";\n\n/**\n * Transport adapter that speaks the thread-centric protocol over a\n * bidirectional WebSocket. Bound to a specific `threadId` — the socket\n * connects to `ws://.../threads/:thread_id/stream/events`.\n */\nexport class ProtocolWebSocketTransportAdapter implements TransportAdapter {\n readonly threadId: string;\n\n private readonly queue = new AsyncQueue<Message>();\n\n private readonly apiUrl: string;\n\n private readonly defaultHeaders?: Record<string, HeaderValue>;\n\n private readonly onRequest?: ProtocolRequestHook;\n\n private readonly webSocketFactory: (url: string) => WebSocket;\n\n private readonly streamUrl: string;\n\n private readonly pending = new Map<number, PendingResponse>();\n\n private socket: WebSocket | null = null;\n\n private closed = false;\n\n private intentionalClose = false;\n\n constructor(options: ProtocolWebSocketTransportOptions) {\n this.apiUrl = options.apiUrl;\n this.threadId = options.threadId;\n this.defaultHeaders = options.defaultHeaders;\n this.onRequest = options.onRequest;\n this.webSocketFactory =\n options.webSocketFactory ?? ((url) => new WebSocket(url));\n this.streamUrl =\n options.paths?.stream ?? `/threads/${this.threadId}/stream/events`;\n }\n\n async open(): Promise<void> {\n if (this.socket != null) return;\n this.assertBrowserSafeTransportConfig();\n\n const wsUrl = toWebSocketUrl(\n new URL(\n this.streamUrl,\n this.apiUrl.endsWith(\"/\") ? this.apiUrl : `${this.apiUrl}/`\n ).toString()\n );\n const socket = this.webSocketFactory(wsUrl);\n this.socket = socket;\n this.closed = false;\n this.intentionalClose = false;\n\n socket.addEventListener(\"message\", this.handleMessage);\n socket.addEventListener(\"close\", this.handleClose);\n socket.addEventListener(\"error\", this.handleSocketError);\n\n await new Promise<void>((resolve, reject) => {\n const onOpen = () => {\n cleanup();\n resolve();\n };\n const onError = () => {\n cleanup();\n reject(new Error(\"Failed to open protocol WebSocket.\"));\n };\n const cleanup = () => {\n socket.removeEventListener(\"open\", onOpen);\n socket.removeEventListener(\"error\", onError);\n };\n socket.addEventListener(\"open\", onOpen, { once: true });\n socket.addEventListener(\"error\", onError, { once: true });\n });\n }\n\n async send(\n command: Command\n ): Promise<CommandResponse | ErrorResponse | void> {\n return await this.sendCommand(command);\n }\n\n events(): AsyncIterable<Message> {\n const queue = this.queue;\n return {\n [Symbol.asyncIterator]: () => ({\n next: async () => await queue.shift(),\n return: async () => {\n queue.close();\n return { done: true, value: undefined };\n },\n }),\n };\n }\n\n async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n\n this.closed = true;\n this.intentionalClose = true;\n\n for (const { reject } of this.pending.values()) {\n reject(new Error(\"Protocol WebSocket connection closed.\"));\n }\n this.pending.clear();\n this.queue.close();\n\n const socket = this.socket;\n this.socket = null;\n if (!socket) {\n return;\n }\n\n await new Promise<void>((resolve) => {\n if (socket.readyState === WebSocket.CLOSED) {\n resolve();\n return;\n }\n\n const onClose = () => {\n socket.removeEventListener(\"close\", onClose);\n resolve();\n };\n\n socket.addEventListener(\"close\", onClose, { once: true });\n if (\n socket.readyState === WebSocket.OPEN ||\n socket.readyState === WebSocket.CONNECTING\n ) {\n socket.close();\n } else {\n resolve();\n }\n });\n }\n\n private assertBrowserSafeTransportConfig(): void {\n if (hasHeaders(this.defaultHeaders) || this.onRequest != null) {\n throw new Error(\n \"Browser WebSocket protocol transport does not support defaultHeaders or onRequest hooks. Supply a custom protocolWebSocketFactory if you need custom WebSocket setup.\"\n );\n }\n }\n\n private async sendCommand(\n command: Command\n ): Promise<CommandResponse | ErrorResponse> {\n const socket = this.socket;\n if (socket == null || socket.readyState !== WebSocket.OPEN) {\n throw new Error(\"Protocol WebSocket is not open.\");\n }\n\n return await new Promise<CommandResponse | ErrorResponse>(\n (resolve, reject) => {\n this.pending.set(command.id, { resolve, reject });\n\n try {\n socket.send(JSON.stringify(command));\n } catch (error) {\n this.pending.delete(command.id);\n reject(toError(error));\n }\n }\n );\n }\n\n private readonly handleMessage = (event: MessageEvent): void => {\n let payload: unknown;\n try {\n payload = JSON.parse(String(event.data));\n } catch {\n return;\n }\n\n if (\n isRecord(payload) &&\n typeof payload.id === \"number\" &&\n (payload.type === \"success\" || payload.type === \"error\")\n ) {\n const pending = this.pending.get(payload.id);\n if (pending) {\n this.pending.delete(payload.id);\n pending.resolve(payload as CommandResponse | ErrorResponse);\n }\n return;\n }\n\n if (isRecord(payload) && payload.type === \"event\") {\n this.queue.push(payload as Message);\n }\n };\n\n private readonly handleClose = (): void => {\n this.socket = null;\n\n if (this.intentionalClose || this.closed) {\n this.queue.close();\n return;\n }\n\n const error = new Error(\"Protocol WebSocket closed unexpectedly.\");\n for (const { reject } of this.pending.values()) {\n reject(error);\n }\n this.pending.clear();\n this.queue.close(error);\n };\n\n private readonly handleSocketError = (): void => {\n if (this.closed || this.intentionalClose) {\n return;\n }\n\n const error = new Error(\"Protocol WebSocket encountered an error.\");\n for (const { reject } of this.pending.values()) {\n reject(error);\n }\n this.pending.clear();\n this.queue.close(error);\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"websocket.cjs","names":["AsyncQueue","toWebSocketUrl","toAbsoluteUrl","hasHeaders","toError","isRecord"],"sources":["../../../../src/client/stream/transport/websocket.ts"],"sourcesContent":["import { AsyncQueue } from \"./queue.js\";\nimport type {\n Message,\n Command,\n CommandResponse,\n ErrorResponse,\n} from \"@langchain/protocol\";\n\nimport {\n toAbsoluteUrl,\n toWebSocketUrl,\n isRecord,\n hasHeaders,\n toError,\n} from \"./utils.js\";\nimport type {\n HeaderValue,\n ProtocolRequestHook,\n PendingResponse,\n ProtocolWebSocketTransportOptions,\n} from \"./types.js\";\nimport type { TransportAdapter } from \"../transport.js\";\n\n/**\n * Transport adapter that speaks the thread-centric protocol over a\n * bidirectional WebSocket. Bound to a specific `threadId` — the socket\n * connects to `ws://.../threads/:thread_id/stream/events`.\n */\nexport class ProtocolWebSocketTransportAdapter implements TransportAdapter {\n readonly threadId: string;\n\n private readonly queue = new AsyncQueue<Message>();\n\n private readonly apiUrl: string;\n\n private readonly defaultHeaders?: Record<string, HeaderValue>;\n\n private readonly onRequest?: ProtocolRequestHook;\n\n private readonly webSocketFactory: (url: string) => WebSocket;\n\n private readonly streamUrl: string;\n\n private readonly pending = new Map<number, PendingResponse>();\n\n private socket: WebSocket | null = null;\n\n private closed = false;\n\n private intentionalClose = false;\n\n constructor(options: ProtocolWebSocketTransportOptions) {\n this.apiUrl = options.apiUrl;\n this.threadId = options.threadId;\n this.defaultHeaders = options.defaultHeaders;\n this.onRequest = options.onRequest;\n this.webSocketFactory =\n options.webSocketFactory ?? ((url) => new WebSocket(url));\n this.streamUrl =\n options.paths?.stream ?? `/threads/${this.threadId}/stream/events`;\n }\n\n async open(): Promise<void> {\n if (this.socket != null) return;\n this.assertBrowserSafeTransportConfig();\n\n const wsUrl = toWebSocketUrl(\n toAbsoluteUrl(this.apiUrl, this.streamUrl).toString()\n );\n const socket = this.webSocketFactory(wsUrl);\n this.socket = socket;\n this.closed = false;\n this.intentionalClose = false;\n\n socket.addEventListener(\"message\", this.handleMessage);\n socket.addEventListener(\"close\", this.handleClose);\n socket.addEventListener(\"error\", this.handleSocketError);\n\n await new Promise<void>((resolve, reject) => {\n const onOpen = () => {\n cleanup();\n resolve();\n };\n const onError = () => {\n cleanup();\n reject(new Error(\"Failed to open protocol WebSocket.\"));\n };\n const cleanup = () => {\n socket.removeEventListener(\"open\", onOpen);\n socket.removeEventListener(\"error\", onError);\n };\n socket.addEventListener(\"open\", onOpen, { once: true });\n socket.addEventListener(\"error\", onError, { once: true });\n });\n }\n\n async send(\n command: Command\n ): Promise<CommandResponse | ErrorResponse | void> {\n return await this.sendCommand(command);\n }\n\n events(): AsyncIterable<Message> {\n const queue = this.queue;\n return {\n [Symbol.asyncIterator]: () => ({\n next: async () => await queue.shift(),\n return: async () => {\n queue.close();\n return { done: true, value: undefined };\n },\n }),\n };\n }\n\n async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n\n this.closed = true;\n this.intentionalClose = true;\n\n for (const { reject } of this.pending.values()) {\n reject(new Error(\"Protocol WebSocket connection closed.\"));\n }\n this.pending.clear();\n this.queue.close();\n\n const socket = this.socket;\n this.socket = null;\n if (!socket) {\n return;\n }\n\n await new Promise<void>((resolve) => {\n if (socket.readyState === WebSocket.CLOSED) {\n resolve();\n return;\n }\n\n const onClose = () => {\n socket.removeEventListener(\"close\", onClose);\n resolve();\n };\n\n socket.addEventListener(\"close\", onClose, { once: true });\n if (\n socket.readyState === WebSocket.OPEN ||\n socket.readyState === WebSocket.CONNECTING\n ) {\n socket.close();\n } else {\n resolve();\n }\n });\n }\n\n private assertBrowserSafeTransportConfig(): void {\n if (hasHeaders(this.defaultHeaders) || this.onRequest != null) {\n throw new Error(\n \"Browser WebSocket protocol transport does not support defaultHeaders or onRequest hooks. Supply a custom protocolWebSocketFactory if you need custom WebSocket setup.\"\n );\n }\n }\n\n private async sendCommand(\n command: Command\n ): Promise<CommandResponse | ErrorResponse> {\n const socket = this.socket;\n if (socket == null || socket.readyState !== WebSocket.OPEN) {\n throw new Error(\"Protocol WebSocket is not open.\");\n }\n\n return await new Promise<CommandResponse | ErrorResponse>(\n (resolve, reject) => {\n this.pending.set(command.id, { resolve, reject });\n\n try {\n socket.send(JSON.stringify(command));\n } catch (error) {\n this.pending.delete(command.id);\n reject(toError(error));\n }\n }\n );\n }\n\n private readonly handleMessage = (event: MessageEvent): void => {\n let payload: unknown;\n try {\n payload = JSON.parse(String(event.data));\n } catch {\n return;\n }\n\n if (\n isRecord(payload) &&\n typeof payload.id === \"number\" &&\n (payload.type === \"success\" || payload.type === \"error\")\n ) {\n const pending = this.pending.get(payload.id);\n if (pending) {\n this.pending.delete(payload.id);\n pending.resolve(payload as CommandResponse | ErrorResponse);\n }\n return;\n }\n\n if (isRecord(payload) && payload.type === \"event\") {\n this.queue.push(payload as Message);\n }\n };\n\n private readonly handleClose = (): void => {\n this.socket = null;\n\n if (this.intentionalClose || this.closed) {\n this.queue.close();\n return;\n }\n\n const error = new Error(\"Protocol WebSocket closed unexpectedly.\");\n for (const { reject } of this.pending.values()) {\n reject(error);\n }\n this.pending.clear();\n this.queue.close(error);\n };\n\n private readonly handleSocketError = (): void => {\n if (this.closed || this.intentionalClose) {\n return;\n }\n\n const error = new Error(\"Protocol WebSocket encountered an error.\");\n for (const { reject } of this.pending.values()) {\n reject(error);\n }\n this.pending.clear();\n this.queue.close(error);\n };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"websocket.d.cts","names":[],"sources":["../../../../src/client/stream/transport/websocket.ts"],"mappings":";;;;;;;AAsBA;;;cAAa,iCAAA,YAA6C,gBAAA;EAAA,SAC/C,QAAA;EAAA,iBAEQ,KAAA;EAAA,iBAEA,MAAA;EAAA,iBAEA,cAAA;EAAA,iBAEA,SAAA;EAAA,iBAEA,gBAAA;EAAA,iBAEA,SAAA;EAAA,iBAEA,OAAA;EAAA,QAET,MAAA;EAAA,QAEA,MAAA;EAAA,QAEA,gBAAA;EAER,WAAA,CAAY,OAAA,EAAS,iCAAA;EAWf,IAAA,CAAA,GAAQ,OAAA;EAqCR,IAAA,CACJ,OAAA,EAAS,OAAA,GACR,OAAA,CAAQ,eAAA,GAAkB,aAAA;EAI7B,MAAA,CAAA,GAAU,aAAA,CAAc,OAAA;EAalB,KAAA,CAAA,GAAS,OAAA;EAAA,QA2CP,gCAAA;EAAA,QAQM,WAAA;EAAA,iBAsBG,aAAA;EAAA,iBA0BA,WAAA;EAAA,iBAgBA,iBAAA;AAAA"}
1
+ {"version":3,"file":"websocket.d.cts","names":[],"sources":["../../../../src/client/stream/transport/websocket.ts"],"mappings":";;;;;;;AA4BA;;;cAAa,iCAAA,YAA6C,gBAAA;EAAA,SAC/C,QAAA;EAAA,iBAEQ,KAAA;EAAA,iBAEA,MAAA;EAAA,iBAEA,cAAA;EAAA,iBAEA,SAAA;EAAA,iBAEA,gBAAA;EAAA,iBAEA,SAAA;EAAA,iBAEA,OAAA;EAAA,QAET,MAAA;EAAA,QAEA,MAAA;EAAA,QAEA,gBAAA;EAER,WAAA,CAAY,OAAA,EAAS,iCAAA;EAWf,IAAA,CAAA,GAAQ,OAAA;EAkCR,IAAA,CACJ,OAAA,EAAS,OAAA,GACR,OAAA,CAAQ,eAAA,GAAkB,aAAA;EAI7B,MAAA,CAAA,GAAU,aAAA,CAAc,OAAA;EAalB,KAAA,CAAA,GAAS,OAAA;EAAA,QA2CP,gCAAA;EAAA,QAQM,WAAA;EAAA,iBAsBG,aAAA;EAAA,iBA0BA,WAAA;EAAA,iBAgBA,iBAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"websocket.d.ts","names":[],"sources":["../../../../src/client/stream/transport/websocket.ts"],"mappings":";;;;;;;AAsBA;;;cAAa,iCAAA,YAA6C,gBAAA;EAAA,SAC/C,QAAA;EAAA,iBAEQ,KAAA;EAAA,iBAEA,MAAA;EAAA,iBAEA,cAAA;EAAA,iBAEA,SAAA;EAAA,iBAEA,gBAAA;EAAA,iBAEA,SAAA;EAAA,iBAEA,OAAA;EAAA,QAET,MAAA;EAAA,QAEA,MAAA;EAAA,QAEA,gBAAA;EAER,WAAA,CAAY,OAAA,EAAS,iCAAA;EAWf,IAAA,CAAA,GAAQ,OAAA;EAqCR,IAAA,CACJ,OAAA,EAAS,OAAA,GACR,OAAA,CAAQ,eAAA,GAAkB,aAAA;EAI7B,MAAA,CAAA,GAAU,aAAA,CAAc,OAAA;EAalB,KAAA,CAAA,GAAS,OAAA;EAAA,QA2CP,gCAAA;EAAA,QAQM,WAAA;EAAA,iBAsBG,aAAA;EAAA,iBA0BA,WAAA;EAAA,iBAgBA,iBAAA;AAAA"}
1
+ {"version":3,"file":"websocket.d.ts","names":[],"sources":["../../../../src/client/stream/transport/websocket.ts"],"mappings":";;;;;;;AA4BA;;;cAAa,iCAAA,YAA6C,gBAAA;EAAA,SAC/C,QAAA;EAAA,iBAEQ,KAAA;EAAA,iBAEA,MAAA;EAAA,iBAEA,cAAA;EAAA,iBAEA,SAAA;EAAA,iBAEA,gBAAA;EAAA,iBAEA,SAAA;EAAA,iBAEA,OAAA;EAAA,QAET,MAAA;EAAA,QAEA,MAAA;EAAA,QAEA,gBAAA;EAER,WAAA,CAAY,OAAA,EAAS,iCAAA;EAWf,IAAA,CAAA,GAAQ,OAAA;EAkCR,IAAA,CACJ,OAAA,EAAS,OAAA,GACR,OAAA,CAAQ,eAAA,GAAkB,aAAA;EAI7B,MAAA,CAAA,GAAU,aAAA,CAAc,OAAA;EAalB,KAAA,CAAA,GAAS,OAAA;EAAA,QA2CP,gCAAA;EAAA,QAQM,WAAA;EAAA,iBAsBG,aAAA;EAAA,iBA0BA,WAAA;EAAA,iBAgBA,iBAAA;AAAA"}
@@ -1,5 +1,5 @@
1
1
  import { AsyncQueue } from "./queue.js";
2
- import { hasHeaders, isRecord, toError, toWebSocketUrl } from "./utils.js";
2
+ import { hasHeaders, isRecord, toAbsoluteUrl, toError, toWebSocketUrl } from "./utils.js";
3
3
  //#region src/client/stream/transport/websocket.ts
4
4
  /**
5
5
  * Transport adapter that speaks the thread-centric protocol over a
@@ -29,7 +29,7 @@ var ProtocolWebSocketTransportAdapter = class {
29
29
  async open() {
30
30
  if (this.socket != null) return;
31
31
  this.assertBrowserSafeTransportConfig();
32
- const wsUrl = toWebSocketUrl(new URL(this.streamUrl, this.apiUrl.endsWith("/") ? this.apiUrl : `${this.apiUrl}/`).toString());
32
+ const wsUrl = toWebSocketUrl(toAbsoluteUrl(this.apiUrl, this.streamUrl).toString());
33
33
  const socket = this.webSocketFactory(wsUrl);
34
34
  this.socket = socket;
35
35
  this.closed = false;
@@ -1 +1 @@
1
- {"version":3,"file":"websocket.js","names":[],"sources":["../../../../src/client/stream/transport/websocket.ts"],"sourcesContent":["import { AsyncQueue } from \"./queue.js\";\nimport type {\n Message,\n Command,\n CommandResponse,\n ErrorResponse,\n} from \"@langchain/protocol\";\n\nimport { toWebSocketUrl, isRecord, hasHeaders, toError } from \"./utils.js\";\nimport type {\n HeaderValue,\n ProtocolRequestHook,\n PendingResponse,\n ProtocolWebSocketTransportOptions,\n} from \"./types.js\";\nimport type { TransportAdapter } from \"../transport.js\";\n\n/**\n * Transport adapter that speaks the thread-centric protocol over a\n * bidirectional WebSocket. Bound to a specific `threadId` — the socket\n * connects to `ws://.../threads/:thread_id/stream/events`.\n */\nexport class ProtocolWebSocketTransportAdapter implements TransportAdapter {\n readonly threadId: string;\n\n private readonly queue = new AsyncQueue<Message>();\n\n private readonly apiUrl: string;\n\n private readonly defaultHeaders?: Record<string, HeaderValue>;\n\n private readonly onRequest?: ProtocolRequestHook;\n\n private readonly webSocketFactory: (url: string) => WebSocket;\n\n private readonly streamUrl: string;\n\n private readonly pending = new Map<number, PendingResponse>();\n\n private socket: WebSocket | null = null;\n\n private closed = false;\n\n private intentionalClose = false;\n\n constructor(options: ProtocolWebSocketTransportOptions) {\n this.apiUrl = options.apiUrl;\n this.threadId = options.threadId;\n this.defaultHeaders = options.defaultHeaders;\n this.onRequest = options.onRequest;\n this.webSocketFactory =\n options.webSocketFactory ?? ((url) => new WebSocket(url));\n this.streamUrl =\n options.paths?.stream ?? `/threads/${this.threadId}/stream/events`;\n }\n\n async open(): Promise<void> {\n if (this.socket != null) return;\n this.assertBrowserSafeTransportConfig();\n\n const wsUrl = toWebSocketUrl(\n new URL(\n this.streamUrl,\n this.apiUrl.endsWith(\"/\") ? this.apiUrl : `${this.apiUrl}/`\n ).toString()\n );\n const socket = this.webSocketFactory(wsUrl);\n this.socket = socket;\n this.closed = false;\n this.intentionalClose = false;\n\n socket.addEventListener(\"message\", this.handleMessage);\n socket.addEventListener(\"close\", this.handleClose);\n socket.addEventListener(\"error\", this.handleSocketError);\n\n await new Promise<void>((resolve, reject) => {\n const onOpen = () => {\n cleanup();\n resolve();\n };\n const onError = () => {\n cleanup();\n reject(new Error(\"Failed to open protocol WebSocket.\"));\n };\n const cleanup = () => {\n socket.removeEventListener(\"open\", onOpen);\n socket.removeEventListener(\"error\", onError);\n };\n socket.addEventListener(\"open\", onOpen, { once: true });\n socket.addEventListener(\"error\", onError, { once: true });\n });\n }\n\n async send(\n command: Command\n ): Promise<CommandResponse | ErrorResponse | void> {\n return await this.sendCommand(command);\n }\n\n events(): AsyncIterable<Message> {\n const queue = this.queue;\n return {\n [Symbol.asyncIterator]: () => ({\n next: async () => await queue.shift(),\n return: async () => {\n queue.close();\n return { done: true, value: undefined };\n },\n }),\n };\n }\n\n async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n\n this.closed = true;\n this.intentionalClose = true;\n\n for (const { reject } of this.pending.values()) {\n reject(new Error(\"Protocol WebSocket connection closed.\"));\n }\n this.pending.clear();\n this.queue.close();\n\n const socket = this.socket;\n this.socket = null;\n if (!socket) {\n return;\n }\n\n await new Promise<void>((resolve) => {\n if (socket.readyState === WebSocket.CLOSED) {\n resolve();\n return;\n }\n\n const onClose = () => {\n socket.removeEventListener(\"close\", onClose);\n resolve();\n };\n\n socket.addEventListener(\"close\", onClose, { once: true });\n if (\n socket.readyState === WebSocket.OPEN ||\n socket.readyState === WebSocket.CONNECTING\n ) {\n socket.close();\n } else {\n resolve();\n }\n });\n }\n\n private assertBrowserSafeTransportConfig(): void {\n if (hasHeaders(this.defaultHeaders) || this.onRequest != null) {\n throw new Error(\n \"Browser WebSocket protocol transport does not support defaultHeaders or onRequest hooks. Supply a custom protocolWebSocketFactory if you need custom WebSocket setup.\"\n );\n }\n }\n\n private async sendCommand(\n command: Command\n ): Promise<CommandResponse | ErrorResponse> {\n const socket = this.socket;\n if (socket == null || socket.readyState !== WebSocket.OPEN) {\n throw new Error(\"Protocol WebSocket is not open.\");\n }\n\n return await new Promise<CommandResponse | ErrorResponse>(\n (resolve, reject) => {\n this.pending.set(command.id, { resolve, reject });\n\n try {\n socket.send(JSON.stringify(command));\n } catch (error) {\n this.pending.delete(command.id);\n reject(toError(error));\n }\n }\n );\n }\n\n private readonly handleMessage = (event: MessageEvent): void => {\n let payload: unknown;\n try {\n payload = JSON.parse(String(event.data));\n } catch {\n return;\n }\n\n if (\n isRecord(payload) &&\n typeof payload.id === \"number\" &&\n (payload.type === \"success\" || payload.type === \"error\")\n ) {\n const pending = this.pending.get(payload.id);\n if (pending) {\n this.pending.delete(payload.id);\n pending.resolve(payload as CommandResponse | ErrorResponse);\n }\n return;\n }\n\n if (isRecord(payload) && payload.type === \"event\") {\n this.queue.push(payload as Message);\n }\n };\n\n private readonly handleClose = (): void => {\n this.socket = null;\n\n if (this.intentionalClose || this.closed) {\n this.queue.close();\n return;\n }\n\n const error = new Error(\"Protocol WebSocket closed unexpectedly.\");\n for (const { reject } of this.pending.values()) {\n reject(error);\n }\n this.pending.clear();\n this.queue.close(error);\n };\n\n private readonly handleSocketError = (): void => {\n if (this.closed || this.intentionalClose) {\n return;\n }\n\n const error = new Error(\"Protocol WebSocket encountered an error.\");\n for (const { reject } of this.pending.values()) {\n reject(error);\n }\n this.pending.clear();\n this.queue.close(error);\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"websocket.js","names":[],"sources":["../../../../src/client/stream/transport/websocket.ts"],"sourcesContent":["import { AsyncQueue } from \"./queue.js\";\nimport type {\n Message,\n Command,\n CommandResponse,\n ErrorResponse,\n} from \"@langchain/protocol\";\n\nimport {\n toAbsoluteUrl,\n toWebSocketUrl,\n isRecord,\n hasHeaders,\n toError,\n} from \"./utils.js\";\nimport type {\n HeaderValue,\n ProtocolRequestHook,\n PendingResponse,\n ProtocolWebSocketTransportOptions,\n} from \"./types.js\";\nimport type { TransportAdapter } from \"../transport.js\";\n\n/**\n * Transport adapter that speaks the thread-centric protocol over a\n * bidirectional WebSocket. Bound to a specific `threadId` — the socket\n * connects to `ws://.../threads/:thread_id/stream/events`.\n */\nexport class ProtocolWebSocketTransportAdapter implements TransportAdapter {\n readonly threadId: string;\n\n private readonly queue = new AsyncQueue<Message>();\n\n private readonly apiUrl: string;\n\n private readonly defaultHeaders?: Record<string, HeaderValue>;\n\n private readonly onRequest?: ProtocolRequestHook;\n\n private readonly webSocketFactory: (url: string) => WebSocket;\n\n private readonly streamUrl: string;\n\n private readonly pending = new Map<number, PendingResponse>();\n\n private socket: WebSocket | null = null;\n\n private closed = false;\n\n private intentionalClose = false;\n\n constructor(options: ProtocolWebSocketTransportOptions) {\n this.apiUrl = options.apiUrl;\n this.threadId = options.threadId;\n this.defaultHeaders = options.defaultHeaders;\n this.onRequest = options.onRequest;\n this.webSocketFactory =\n options.webSocketFactory ?? ((url) => new WebSocket(url));\n this.streamUrl =\n options.paths?.stream ?? `/threads/${this.threadId}/stream/events`;\n }\n\n async open(): Promise<void> {\n if (this.socket != null) return;\n this.assertBrowserSafeTransportConfig();\n\n const wsUrl = toWebSocketUrl(\n toAbsoluteUrl(this.apiUrl, this.streamUrl).toString()\n );\n const socket = this.webSocketFactory(wsUrl);\n this.socket = socket;\n this.closed = false;\n this.intentionalClose = false;\n\n socket.addEventListener(\"message\", this.handleMessage);\n socket.addEventListener(\"close\", this.handleClose);\n socket.addEventListener(\"error\", this.handleSocketError);\n\n await new Promise<void>((resolve, reject) => {\n const onOpen = () => {\n cleanup();\n resolve();\n };\n const onError = () => {\n cleanup();\n reject(new Error(\"Failed to open protocol WebSocket.\"));\n };\n const cleanup = () => {\n socket.removeEventListener(\"open\", onOpen);\n socket.removeEventListener(\"error\", onError);\n };\n socket.addEventListener(\"open\", onOpen, { once: true });\n socket.addEventListener(\"error\", onError, { once: true });\n });\n }\n\n async send(\n command: Command\n ): Promise<CommandResponse | ErrorResponse | void> {\n return await this.sendCommand(command);\n }\n\n events(): AsyncIterable<Message> {\n const queue = this.queue;\n return {\n [Symbol.asyncIterator]: () => ({\n next: async () => await queue.shift(),\n return: async () => {\n queue.close();\n return { done: true, value: undefined };\n },\n }),\n };\n }\n\n async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n\n this.closed = true;\n this.intentionalClose = true;\n\n for (const { reject } of this.pending.values()) {\n reject(new Error(\"Protocol WebSocket connection closed.\"));\n }\n this.pending.clear();\n this.queue.close();\n\n const socket = this.socket;\n this.socket = null;\n if (!socket) {\n return;\n }\n\n await new Promise<void>((resolve) => {\n if (socket.readyState === WebSocket.CLOSED) {\n resolve();\n return;\n }\n\n const onClose = () => {\n socket.removeEventListener(\"close\", onClose);\n resolve();\n };\n\n socket.addEventListener(\"close\", onClose, { once: true });\n if (\n socket.readyState === WebSocket.OPEN ||\n socket.readyState === WebSocket.CONNECTING\n ) {\n socket.close();\n } else {\n resolve();\n }\n });\n }\n\n private assertBrowserSafeTransportConfig(): void {\n if (hasHeaders(this.defaultHeaders) || this.onRequest != null) {\n throw new Error(\n \"Browser WebSocket protocol transport does not support defaultHeaders or onRequest hooks. Supply a custom protocolWebSocketFactory if you need custom WebSocket setup.\"\n );\n }\n }\n\n private async sendCommand(\n command: Command\n ): Promise<CommandResponse | ErrorResponse> {\n const socket = this.socket;\n if (socket == null || socket.readyState !== WebSocket.OPEN) {\n throw new Error(\"Protocol WebSocket is not open.\");\n }\n\n return await new Promise<CommandResponse | ErrorResponse>(\n (resolve, reject) => {\n this.pending.set(command.id, { resolve, reject });\n\n try {\n socket.send(JSON.stringify(command));\n } catch (error) {\n this.pending.delete(command.id);\n reject(toError(error));\n }\n }\n );\n }\n\n private readonly handleMessage = (event: MessageEvent): void => {\n let payload: unknown;\n try {\n payload = JSON.parse(String(event.data));\n } catch {\n return;\n }\n\n if (\n isRecord(payload) &&\n typeof payload.id === \"number\" &&\n (payload.type === \"success\" || payload.type === \"error\")\n ) {\n const pending = this.pending.get(payload.id);\n if (pending) {\n this.pending.delete(payload.id);\n pending.resolve(payload as CommandResponse | ErrorResponse);\n }\n return;\n }\n\n if (isRecord(payload) && payload.type === \"event\") {\n this.queue.push(payload as Message);\n }\n };\n\n private readonly handleClose = (): void => {\n this.socket = null;\n\n if (this.intentionalClose || this.closed) {\n this.queue.close();\n return;\n }\n\n const error = new Error(\"Protocol WebSocket closed unexpectedly.\");\n for (const { reject } of this.pending.values()) {\n reject(error);\n }\n this.pending.clear();\n this.queue.close(error);\n };\n\n private readonly handleSocketError = (): void => {\n if (this.closed || this.intentionalClose) {\n return;\n }\n\n const error = new Error(\"Protocol WebSocket encountered an error.\");\n for (const { reject } of this.pending.values()) {\n reject(error);\n }\n this.pending.clear();\n this.queue.close(error);\n };\n}\n"],"mappings":"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"}
@@ -4,9 +4,7 @@ import { AgentResult, Channel, Event, InputInjectParams, InputRespondParams, Lis
4
4
 
5
5
  //#region src/client/stream/types.d.ts
6
6
  interface ExtendedRunStartParams extends RunStartParams {
7
- forkFrom?: {
8
- checkpointId: string;
9
- };
7
+ forkFrom?: string;
10
8
  multitaskStrategy?: "reject" | "rollback" | "interrupt" | "enqueue";
11
9
  }
12
10
  type SubscribeOptions = Omit<SubscribeParams, "channels">;
@@ -146,10 +144,17 @@ interface ThreadModules {
146
144
  /**
147
145
  * Human-in-the-loop interrupt payload surfaced from lifecycle events.
148
146
  * Matches the in-process `InterruptPayload` type.
147
+ *
148
+ * {@link ThreadStream.interrupts} collects these entries in arrival order.
149
+ * Use them (via {@link StreamController.getThread `getThread()`}) when
150
+ * you need the protocol `namespace` tuple for
151
+ * {@link StreamController.respond `respond()`} — for example subgraph
152
+ * interrupts that are not mirrored on {@link RootSnapshot.interrupts}.
149
153
  */
150
154
  interface InterruptPayload<TPayload = unknown> {
151
155
  interruptId: string;
152
156
  payload: TPayload;
157
+ /** Protocol namespace tuple the server validates on resume (`[]` at root). */
153
158
  namespace: string[];
154
159
  }
155
160
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.cts","names":[],"sources":["../../../src/client/stream/types.ts"],"mappings":";;;;;UAoBiB,sBAAA,SAA+B,cAAA;EAC9C,QAAA;IAAa,YAAA;EAAA;EACb,iBAAA;AAAA;AAAA,KAGU,gBAAA,GAAmB,IAAA,CAAK,eAAA;AAAA,KAExB,oBAAA;EACV,MAAA;EACA,OAAA;EACA,QAAA;EACA,KAAA;EACA,MAAA;EACA,SAAA;EACA,KAAA;EACA,KAAA;EACA,WAAA;EACA,KAAA;AAAA;AAAA,KAGU,eAAA,kBAAiC,OAAA,IAC3C,QAAA,eAAuB,oBAAA,GACnB,OAAA,CAAQ,KAAA;EAAS,MAAA,EAAQ,oBAAA,CAAqB,QAAA;AAAA,KAC9C,QAAA,8BACE,OAAA,CAAQ,KAAA;EAAS,MAAA;AAAA;AAAA,KAGb,gBAAA,4BAA4C,OAAA,MACtD,eAAA,CAAgB,SAAA;;;;;;;KAQN,eAAA,kBAAiC,OAAA,IAC3C,QAAA,wCAAgD,eAAA,CAAgB,QAAA;AAAA,KAEtD,gBAAA,4BAA4C,OAAA,MACtD,eAAA,CAAgB,SAAA;AApBlB;;;;;;;;AAAA,KA8BY,yBAAA;;;;;;;;;KAUA,qBAAA,GACR,yBAAA,GACA,kBAAA;;;;UAKa,mBAAA;EA7Cc;;;;;EAmD7B,WAAA;EAjD6B;;AAG/B;;;;;;;;;;;EA4DE,SAAA,GAAY,qBAAA;EA3Da;;AAQ3B;;EAwDE,iBAAA;EAxD2C;;;;;;;EAgE3C,KAAA,UAAe,KAAA;EA/Df;;;;;AAEF;EAoEE,gBAAA,IAAoB,GAAA,aAAgB,SAAA;AAAA;AAAA,UAGrB,oBAAA;EACf,WAAA;EACA,qBAAA;EACA,WAAA;AAAA;AAAA,UAGe,iBAAA,UACN,KAAA,UACD,aAAA,CAAc,MAAA;EAAA,SACb,cAAA;EAAA,SACA,MAAA,EAAQ,eAAA;EACjB,WAAA,IAAe,OAAA;AAAA;AAAA,UAGA,mBAAA,SAA4B,aAAA,CAAc,gBAAA;EAAA,SAChD,cAAA;EAAA,SACA,MAAA,EAAQ,eAAA;EACjB,WAAA,IAAe,OAAA;AAAA;AAAA,UAGA,WAAA;EACf,OAAA,CAAQ,MAAA,EAAQ,kBAAA,GAAqB,OAAA;EACrC,MAAA,CAAO,MAAA,EAAQ,iBAAA,GAAoB,OAAA;AAAA;AAAA,UAGpB,WAAA;EACf,GAAA,CAAI,MAAA,EAAQ,cAAA,GAAiB,OAAA,CAAQ,cAAA;EACrC,eAAA,CACE,MAAA,EAAQ,qBAAA,GACP,OAAA,CAAQ,qBAAA;EACX,IAAA,CAAK,MAAA,EAAQ,eAAA,GAAkB,OAAA,CAAQ,eAAA;AAAA;;;;UAMxB,aAAA;EACf,GAAA;IA1EA;;;;;IAgFE,KAAA,CACE,MAAA,EAAQ,IAAA,CAAK,sBAAA,oBACZ,OAAA,CAAQ,SAAA;EAAA;EAEb,KAAA;IACE,OAAA,CAAQ,MAAA;MAAW,MAAA;IAAA,IAAoB,OAAA,CAAQ,WAAA;EAAA;EAEjD,KAAA,EAAO,WAAA;EACP,KAAA,EAAO,WAAA;AAAA;;;;;UAOQ,gBAAA;EACf,WAAA;EACA,OAAA,EAAS,QAAA;EACT,SAAA;AAAA;;;;;;;;;;;;;;;;;;AA/CF;UAoEiB,eAAA,sBACP,aAAA,CAAc,CAAA,GAAI,WAAA,CAAY,CAAA;;;;;;;;;;;;;;KAe5B,eAAA,MACV,CAAA,SAAU,WAAA,YAAuB,CAAA,GAAI,CAAA,SAAU,aAAA,YAAyB,CAAA,GAAI,CAAA;;AA/E9E;;;;;;;;;;;;;;KAgGY,gBAAA,qBACU,MAAA,oBAA0B,MAAA,4CAEzB,WAAA,GAAc,eAAA,CACjC,eAAA,CAAgB,WAAA,CAAY,CAAA;EAAA,UAGpB,IAAA,WAAe,eAAA;AAAA"}
1
+ {"version":3,"file":"types.d.cts","names":[],"sources":["../../../src/client/stream/types.ts"],"mappings":";;;;;UAoBiB,sBAAA,SAA+B,cAAA;EAC9C,QAAA;EACA,iBAAA;AAAA;AAAA,KAGU,gBAAA,GAAmB,IAAA,CAAK,eAAA;AAAA,KAExB,oBAAA;EACV,MAAA;EACA,OAAA;EACA,QAAA;EACA,KAAA;EACA,MAAA;EACA,SAAA;EACA,KAAA;EACA,KAAA;EACA,WAAA;EACA,KAAA;AAAA;AAAA,KAGU,eAAA,kBAAiC,OAAA,IAC3C,QAAA,eAAuB,oBAAA,GACnB,OAAA,CAAQ,KAAA;EAAS,MAAA,EAAQ,oBAAA,CAAqB,QAAA;AAAA,KAC9C,QAAA,8BACE,OAAA,CAAQ,KAAA;EAAS,MAAA;AAAA;AAAA,KAGb,gBAAA,4BAA4C,OAAA,MACtD,eAAA,CAAgB,SAAA;;;;;;;KAQN,eAAA,kBAAiC,OAAA,IAC3C,QAAA,wCAAgD,eAAA,CAAgB,QAAA;AAAA,KAEtD,gBAAA,4BAA4C,OAAA,MACtD,eAAA,CAAgB,SAAA;;AApBlB;;;;;;;KA8BY,yBAAA;;;;;;;;;KAUA,qBAAA,GACR,yBAAA,GACA,kBAAA;;;;UAKa,mBAAA;EA7CM;;;;;EAmDrB,WAAA;EAjDuB;;;AAGzB;;;;;;;;;;EA4DE,SAAA,GAAY,qBAAA;EA3DI;;;AAQlB;EAwDE,iBAAA;EAxDyB;;;;;;;EAgEzB,KAAA,UAAe,KAAA;EAhE4B;;;;;;EAuE3C,gBAAA,IAAoB,GAAA,aAAgB,SAAA;AAAA;AAAA,UAGrB,oBAAA;EACf,WAAA;EACA,qBAAA;EACA,WAAA;AAAA;AAAA,UAGe,iBAAA,UACN,KAAA,UACD,aAAA,CAAc,MAAA;EAAA,SACb,cAAA;EAAA,SACA,MAAA,EAAQ,eAAA;EACjB,WAAA,IAAe,OAAA;AAAA;AAAA,UAGA,mBAAA,SAA4B,aAAA,CAAc,gBAAA;EAAA,SAChD,cAAA;EAAA,SACA,MAAA,EAAQ,eAAA;EACjB,WAAA,IAAe,OAAA;AAAA;AAAA,UAGA,WAAA;EACf,OAAA,CAAQ,MAAA,EAAQ,kBAAA,GAAqB,OAAA;EACrC,MAAA,CAAO,MAAA,EAAQ,iBAAA,GAAoB,OAAA;AAAA;AAAA,UAGpB,WAAA;EACf,GAAA,CAAI,MAAA,EAAQ,cAAA,GAAiB,OAAA,CAAQ,cAAA;EACrC,eAAA,CACE,MAAA,EAAQ,qBAAA,GACP,OAAA,CAAQ,qBAAA;EACX,IAAA,CAAK,MAAA,EAAQ,eAAA,GAAkB,OAAA,CAAQ,eAAA;AAAA;;;;UAMxB,aAAA;EACf,GAAA;IAxC6C;;;;;IA8C3C,KAAA,CACE,MAAA,EAAQ,IAAA,CAAK,sBAAA,oBACZ,OAAA,CAAQ,SAAA;EAAA;EAEb,KAAA;IACE,OAAA,CAAQ,MAAA;MAAW,MAAA;IAAA,IAAoB,OAAA,CAAQ,WAAA;EAAA;EAEjD,KAAA,EAAO,WAAA;EACP,KAAA,EAAO,WAAA;AAAA;;;;;;;AA7CT;;;;UA0DiB,gBAAA;EACf,WAAA;EACA,OAAA,EAAS,QAAA;EA1DD;EA4DR,SAAA;AAAA;;;;;;;;;;;;AAtDF;;;;;;;UA2EiB,eAAA,sBACP,aAAA,CAAc,CAAA,GAAI,WAAA,CAAY,CAAA;;;;;;;;;;AAtExC;;;;KAqFY,eAAA,MACV,CAAA,SAAU,WAAA,YAAuB,CAAA,GAAI,CAAA,SAAU,aAAA,YAAyB,CAAA,GAAI,CAAA;;;;;;;;;;;;;;;AAjF9E;KAkGY,gBAAA,qBACU,MAAA,oBAA0B,MAAA,4CAEzB,WAAA,GAAc,eAAA,CACjC,eAAA,CAAgB,WAAA,CAAY,CAAA;EAAA,UAGpB,IAAA,WAAe,eAAA;AAAA"}
@@ -4,9 +4,7 @@ import { AgentResult, Channel, Event, InputInjectParams, InputRespondParams, Lis
4
4
 
5
5
  //#region src/client/stream/types.d.ts
6
6
  interface ExtendedRunStartParams extends RunStartParams {
7
- forkFrom?: {
8
- checkpointId: string;
9
- };
7
+ forkFrom?: string;
10
8
  multitaskStrategy?: "reject" | "rollback" | "interrupt" | "enqueue";
11
9
  }
12
10
  type SubscribeOptions = Omit<SubscribeParams, "channels">;
@@ -146,10 +144,17 @@ interface ThreadModules {
146
144
  /**
147
145
  * Human-in-the-loop interrupt payload surfaced from lifecycle events.
148
146
  * Matches the in-process `InterruptPayload` type.
147
+ *
148
+ * {@link ThreadStream.interrupts} collects these entries in arrival order.
149
+ * Use them (via {@link StreamController.getThread `getThread()`}) when
150
+ * you need the protocol `namespace` tuple for
151
+ * {@link StreamController.respond `respond()`} — for example subgraph
152
+ * interrupts that are not mirrored on {@link RootSnapshot.interrupts}.
149
153
  */
150
154
  interface InterruptPayload<TPayload = unknown> {
151
155
  interruptId: string;
152
156
  payload: TPayload;
157
+ /** Protocol namespace tuple the server validates on resume (`[]` at root). */
153
158
  namespace: string[];
154
159
  }
155
160
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/client/stream/types.ts"],"mappings":";;;;;UAoBiB,sBAAA,SAA+B,cAAA;EAC9C,QAAA;IAAa,YAAA;EAAA;EACb,iBAAA;AAAA;AAAA,KAGU,gBAAA,GAAmB,IAAA,CAAK,eAAA;AAAA,KAExB,oBAAA;EACV,MAAA;EACA,OAAA;EACA,QAAA;EACA,KAAA;EACA,MAAA;EACA,SAAA;EACA,KAAA;EACA,KAAA;EACA,WAAA;EACA,KAAA;AAAA;AAAA,KAGU,eAAA,kBAAiC,OAAA,IAC3C,QAAA,eAAuB,oBAAA,GACnB,OAAA,CAAQ,KAAA;EAAS,MAAA,EAAQ,oBAAA,CAAqB,QAAA;AAAA,KAC9C,QAAA,8BACE,OAAA,CAAQ,KAAA;EAAS,MAAA;AAAA;AAAA,KAGb,gBAAA,4BAA4C,OAAA,MACtD,eAAA,CAAgB,SAAA;;;;;;;KAQN,eAAA,kBAAiC,OAAA,IAC3C,QAAA,wCAAgD,eAAA,CAAgB,QAAA;AAAA,KAEtD,gBAAA,4BAA4C,OAAA,MACtD,eAAA,CAAgB,SAAA;AApBlB;;;;;;;;AAAA,KA8BY,yBAAA;;;;;;;;;KAUA,qBAAA,GACR,yBAAA,GACA,kBAAA;;;;UAKa,mBAAA;EA7Cc;;;;;EAmD7B,WAAA;EAjD6B;;AAG/B;;;;;;;;;;;EA4DE,SAAA,GAAY,qBAAA;EA3Da;;AAQ3B;;EAwDE,iBAAA;EAxD2C;;;;;;;EAgE3C,KAAA,UAAe,KAAA;EA/Df;;;;;AAEF;EAoEE,gBAAA,IAAoB,GAAA,aAAgB,SAAA;AAAA;AAAA,UAGrB,oBAAA;EACf,WAAA;EACA,qBAAA;EACA,WAAA;AAAA;AAAA,UAGe,iBAAA,UACN,KAAA,UACD,aAAA,CAAc,MAAA;EAAA,SACb,cAAA;EAAA,SACA,MAAA,EAAQ,eAAA;EACjB,WAAA,IAAe,OAAA;AAAA;AAAA,UAGA,mBAAA,SAA4B,aAAA,CAAc,gBAAA;EAAA,SAChD,cAAA;EAAA,SACA,MAAA,EAAQ,eAAA;EACjB,WAAA,IAAe,OAAA;AAAA;AAAA,UAGA,WAAA;EACf,OAAA,CAAQ,MAAA,EAAQ,kBAAA,GAAqB,OAAA;EACrC,MAAA,CAAO,MAAA,EAAQ,iBAAA,GAAoB,OAAA;AAAA;AAAA,UAGpB,WAAA;EACf,GAAA,CAAI,MAAA,EAAQ,cAAA,GAAiB,OAAA,CAAQ,cAAA;EACrC,eAAA,CACE,MAAA,EAAQ,qBAAA,GACP,OAAA,CAAQ,qBAAA;EACX,IAAA,CAAK,MAAA,EAAQ,eAAA,GAAkB,OAAA,CAAQ,eAAA;AAAA;;;;UAMxB,aAAA;EACf,GAAA;IA1EA;;;;;IAgFE,KAAA,CACE,MAAA,EAAQ,IAAA,CAAK,sBAAA,oBACZ,OAAA,CAAQ,SAAA;EAAA;EAEb,KAAA;IACE,OAAA,CAAQ,MAAA;MAAW,MAAA;IAAA,IAAoB,OAAA,CAAQ,WAAA;EAAA;EAEjD,KAAA,EAAO,WAAA;EACP,KAAA,EAAO,WAAA;AAAA;;;;;UAOQ,gBAAA;EACf,WAAA;EACA,OAAA,EAAS,QAAA;EACT,SAAA;AAAA;;;;;;;;;;;;;;;;;;AA/CF;UAoEiB,eAAA,sBACP,aAAA,CAAc,CAAA,GAAI,WAAA,CAAY,CAAA;;;;;;;;;;;;;;KAe5B,eAAA,MACV,CAAA,SAAU,WAAA,YAAuB,CAAA,GAAI,CAAA,SAAU,aAAA,YAAyB,CAAA,GAAI,CAAA;;AA/E9E;;;;;;;;;;;;;;KAgGY,gBAAA,qBACU,MAAA,oBAA0B,MAAA,4CAEzB,WAAA,GAAc,eAAA,CACjC,eAAA,CAAgB,WAAA,CAAY,CAAA;EAAA,UAGpB,IAAA,WAAe,eAAA;AAAA"}
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/client/stream/types.ts"],"mappings":";;;;;UAoBiB,sBAAA,SAA+B,cAAA;EAC9C,QAAA;EACA,iBAAA;AAAA;AAAA,KAGU,gBAAA,GAAmB,IAAA,CAAK,eAAA;AAAA,KAExB,oBAAA;EACV,MAAA;EACA,OAAA;EACA,QAAA;EACA,KAAA;EACA,MAAA;EACA,SAAA;EACA,KAAA;EACA,KAAA;EACA,WAAA;EACA,KAAA;AAAA;AAAA,KAGU,eAAA,kBAAiC,OAAA,IAC3C,QAAA,eAAuB,oBAAA,GACnB,OAAA,CAAQ,KAAA;EAAS,MAAA,EAAQ,oBAAA,CAAqB,QAAA;AAAA,KAC9C,QAAA,8BACE,OAAA,CAAQ,KAAA;EAAS,MAAA;AAAA;AAAA,KAGb,gBAAA,4BAA4C,OAAA,MACtD,eAAA,CAAgB,SAAA;;;;;;;KAQN,eAAA,kBAAiC,OAAA,IAC3C,QAAA,wCAAgD,eAAA,CAAgB,QAAA;AAAA,KAEtD,gBAAA,4BAA4C,OAAA,MACtD,eAAA,CAAgB,SAAA;;AApBlB;;;;;;;KA8BY,yBAAA;;;;;;;;;KAUA,qBAAA,GACR,yBAAA,GACA,kBAAA;;;;UAKa,mBAAA;EA7CM;;;;;EAmDrB,WAAA;EAjDuB;;;AAGzB;;;;;;;;;;EA4DE,SAAA,GAAY,qBAAA;EA3DI;;;AAQlB;EAwDE,iBAAA;EAxDyB;;;;;;;EAgEzB,KAAA,UAAe,KAAA;EAhE4B;;;;;;EAuE3C,gBAAA,IAAoB,GAAA,aAAgB,SAAA;AAAA;AAAA,UAGrB,oBAAA;EACf,WAAA;EACA,qBAAA;EACA,WAAA;AAAA;AAAA,UAGe,iBAAA,UACN,KAAA,UACD,aAAA,CAAc,MAAA;EAAA,SACb,cAAA;EAAA,SACA,MAAA,EAAQ,eAAA;EACjB,WAAA,IAAe,OAAA;AAAA;AAAA,UAGA,mBAAA,SAA4B,aAAA,CAAc,gBAAA;EAAA,SAChD,cAAA;EAAA,SACA,MAAA,EAAQ,eAAA;EACjB,WAAA,IAAe,OAAA;AAAA;AAAA,UAGA,WAAA;EACf,OAAA,CAAQ,MAAA,EAAQ,kBAAA,GAAqB,OAAA;EACrC,MAAA,CAAO,MAAA,EAAQ,iBAAA,GAAoB,OAAA;AAAA;AAAA,UAGpB,WAAA;EACf,GAAA,CAAI,MAAA,EAAQ,cAAA,GAAiB,OAAA,CAAQ,cAAA;EACrC,eAAA,CACE,MAAA,EAAQ,qBAAA,GACP,OAAA,CAAQ,qBAAA;EACX,IAAA,CAAK,MAAA,EAAQ,eAAA,GAAkB,OAAA,CAAQ,eAAA;AAAA;;;;UAMxB,aAAA;EACf,GAAA;IAxC6C;;;;;IA8C3C,KAAA,CACE,MAAA,EAAQ,IAAA,CAAK,sBAAA,oBACZ,OAAA,CAAQ,SAAA;EAAA;EAEb,KAAA;IACE,OAAA,CAAQ,MAAA;MAAW,MAAA;IAAA,IAAoB,OAAA,CAAQ,WAAA;EAAA;EAEjD,KAAA,EAAO,WAAA;EACP,KAAA,EAAO,WAAA;AAAA;;;;;;;AA7CT;;;;UA0DiB,gBAAA;EACf,WAAA;EACA,OAAA,EAAS,QAAA;EA1DD;EA4DR,SAAA;AAAA;;;;;;;;;;;;AAtDF;;;;;;;UA2EiB,eAAA,sBACP,aAAA,CAAc,CAAA,GAAI,WAAA,CAAY,CAAA;;;;;;;;;;AAtExC;;;;KAqFY,eAAA,MACV,CAAA,SAAU,WAAA,YAAuB,CAAA,GAAI,CAAA,SAAU,aAAA,YAAyB,CAAA,GAAI,CAAA;;;;;;;;;;;;;;;AAjF9E;KAkGY,gBAAA,qBACU,MAAA,oBAA0B,MAAA,4CAEzB,WAAA,GAAc,eAAA,CACjC,eAAA,CAAgB,WAAA,CAAY,CAAA;EAAA,UAGpB,IAAA,WAAe,eAAA;AAAA"}
@@ -121,29 +121,6 @@ function headlessToolsBatchResumeCommand(entries) {
121
121
  return { resume };
122
122
  }
123
123
  /**
124
- * True when every top-level resume key is a graph task interrupt id
125
- * (32-char hex from `values.__interrupt__`).
126
- */
127
- function isInterruptIdKeyedResume(resume) {
128
- if (resume == null || typeof resume !== "object" || Array.isArray(resume)) return false;
129
- const keys = Object.keys(resume);
130
- if (keys.length === 0) return false;
131
- return keys.every((key) => /^[0-9a-f]{32}$/i.test(key));
132
- }
133
- /**
134
- * Normalize `command.resume` into the `run.start` input the API turns
135
- * into `Command({ resume })`. Interrupt-id keyed payloads pass through;
136
- * tool-call-keyed and generic payloads are wrapped under the matching
137
- * protocol interrupt id.
138
- */
139
- function buildResumeRunInput(resume, interrupts, resolvedInterruptIds) {
140
- if (resume == null) return null;
141
- if (isInterruptIdKeyedResume(resume)) return resume;
142
- const target = resolveInterruptTargetForHeadlessResume(resume, interrupts, resolvedInterruptIds);
143
- if (target == null) return null;
144
- return { [target.interruptId]: resume };
145
- }
146
- /**
147
124
  * Reads the tool-call id from a headless-tool resume command shaped as
148
125
  * `{ [toolCallId]: result }`.
149
126
  */
@@ -246,7 +223,6 @@ function flushPendingHeadlessToolInterrupts(values, tools, handledIds, options)
246
223
  });
247
224
  }
248
225
  //#endregion
249
- exports.buildResumeRunInput = buildResumeRunInput;
250
226
  exports.executeHeadlessTool = executeHeadlessTool;
251
227
  exports.filterOutHeadlessToolInterrupts = filterOutHeadlessToolInterrupts;
252
228
  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\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":";;;;;AAuBA,SAAgB,kCACd,OAC8B;AAC9B,KAAI,OAAO,UAAU,YAAY,SAAS,KACxC,QAAO;CAET,MAAM,IAAI;AACV,KAAI,EAAE,SAAS,OACb,QAAO;CAGT,MAAM,QAAQ,EAAE,YAAY,EAAE;AAC9B,KAAI,OAAO,UAAU,YAAY,SAAS,KACxC,QAAO;CAET,MAAM,KAAK;AACX,KAAI,OAAO,GAAG,SAAS,SACrB,QAAO;AAKT,QAAO;EACL,MAAM;EACN,UAAU;GACR,IALO,OAAO,GAAG,OAAO,WAAW,GAAG,KAAK,KAAA;GAM3C,MAAM,GAAG;GACT,MAAM,GAAG;GACV;EACF;;;;;AAiCH,SAAgB,gCACd,YACK;AACL,QAAO,WAAW,QACf,cACC,UAAU,SAAS,QAAQ,CAAC,wBAAwB,UAAU,MAAM,CACvE;;AAGH,SAAgB,wBACd,WACoC;AACpC,QAAO,kCAAkC,UAAU,IAAI;;AAGzD,SAAgB,iBACd,OACA,MACsD;AACtD,QAAO,MAAM,MAAM,SAAS,KAAK,KAAK,SAAS,KAAK;;AAKtD,eAAsB,oBACpB,gBACA,MACA,QAGA;CACA,MAAM,YAAY,KAAK,KAAK;AAE5B,UAAS;EACP,OAAO;EACP,MAAM,eAAe,KAAK;EAC1B;EACD,CAAC;AAEF,KAAI;EACF,MAAM,SAAS,MAAM,eAAe,QAAQ,KAAK;EACjD,MAAM,WAAW,KAAK,KAAK,GAAG;AAE9B,WAAS;GACP,OAAO;GACP,MAAM,eAAe,KAAK;GAC1B;GACA;GACA;GACD,CAAC;AAEF,SAAO;GAAE,SAAS;GAAM;GAAQ;UACzB,KAAK;EAEZ,MAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,IAAI,CAAC;EACjE,MAAM,WAAW,KAAK,KAAK,GAAG;AAE9B,WAAS;GACP,OAAO;GACP,MAAM,eAAe,KAAK;GAC1B;GACA;GACA;GACD,CAAC;AAEF,SAAO;GAAE,SAAS;GAAO;GAAO;;;AAIpC,eAAsB,4BACpB,WACA,OACA,QAC6D;CAC7D,MAAM,EAAE,aAAa;CACrB,MAAM,iBAAiB,iBAAiB,OAAO,SAAS,KAAK;AAE7D,KAAI,CAAC,gBAAgB;EACnB,MAAM,wBAAQ,IAAI,MAChB,kBAAkB,SAAS,KAAK,wCACV,MAAM,KAAK,SAAS,KAAK,KAAK,KAAK,CAAC,KAAK,KAAK,IAAI,SACzE;AAED,WAAS;GACP,OAAO;GACP,MAAM,SAAS;GACf,MAAM,SAAS;GACf;GACA,UAAU;GACX,CAAC;AAEF,SAAO;GACL,YAAY,SAAS;GACrB,OAAO,EAAE,OAAO,MAAM,SAAS;GAChC;;CAGH,MAAM,SAAS,MAAM,oBACnB,gBACA,SAAS,MACT,OACD;AAED,KAAI,OAAO,QACT,QAAO;EACL,YAAY,SAAS;EACrB,OAAO,OAAO;EACf;AAGH,QAAO;EACL,YAAY,SAAS;EACrB,OAAO,EAAE,OAAO,OAAO,MAAM,SAAS;EACvC;;AAGH,SAAgB,0BAA0B,QAGlB;AACtB,QAAO,EACL,QAAQ,OAAO,aACX,GAAG,OAAO,aAAa,OAAO,OAAO,GACrC,OAAO,OACZ;;;;;;;AAQH,SAAgB,gCACd,SAKqB;AACrB,KAAI,QAAQ,WAAW,EACrB,QAAO,EAAE,QAAQ,EAAE,EAAE;AAMvB,KAAI,CAHoB,QAAQ,OAC7B,UAAU,MAAM,YAAY,SAAS,EACvC,IACuB,QAAQ,WAAW,GAAG;EAC5C,MAAM,CAAC,SAAS;AAChB,SAAO,0BAA0B;GAC/B,YAAY,MAAM;GAClB,OAAO,MAAM;GACd,CAAC;;CAGJ,MAAM,SAAkC,EAAE;AAC1C,MAAK,MAAM,SAAS,SAAS;AAC3B,MAAI,MAAM,YAAY,WAAW,EAAG;AACpC,SAAO,MAAM,eACX,MAAM,cAAc,QAAQ,MAAM,WAAW,SAAS,IAClD,GAAG,MAAM,aAAa,MAAM,OAAO,GACnC,MAAM;;AAEd,QAAO,EAAE,QAAQ;;;;;;AAOnB,SAAgB,yBAAyB,QAA0B;AACjE,KAAI,UAAU,QAAQ,OAAO,WAAW,YAAY,MAAM,QAAQ,OAAO,CACvE,QAAO;CAET,MAAM,OAAO,OAAO,KAAK,OAAkC;AAC3D,KAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,QAAO,KAAK,OAAO,QAAQ,kBAAkB,KAAK,IAAI,CAAC;;;;;;;;AASzD,SAAgB,oBACd,QACA,YACA,sBACgC;AAChC,KAAI,UAAU,KAAM,QAAO;AAC3B,KAAI,yBAAyB,OAAO,CAClC,QAAO;CAGT,MAAM,SAAS,wCACb,QACA,YACA,qBACD;AACD,KAAI,UAAU,KAAM,QAAO;AAE3B,QAAO,GAAG,OAAO,cAAc,QAAQ;;;;;;AAOzC,SAAgB,2CACd,QACoB;AACpB,KAAI,UAAU,QAAQ,OAAO,WAAW,YAAY,MAAM,QAAQ,OAAO,CACvE;CAEF,MAAM,OAAO,OAAO,KAAK,OAAkC;AAC3D,KAAI,KAAK,WAAW,EAAG,QAAO,KAAA;AAC9B,QAAO,KAAK;;;;;;AAad,SAAgB,wCACd,QACA,YACA,sBACqD;CACrD,MAAM,aAAa,2CAA2C,OAAO;AACrE,KAAI,cAAc,KAChB,MAAK,IAAI,IAAI,WAAW,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG;EAClD,MAAM,QAAQ,WAAW;AACzB,MAAI,SAAS,QAAQ,qBAAqB,IAAI,MAAM,YAAY,CAC9D;AAGF,MADiB,kCAAkC,MAAM,QAAQ,EACnD,SAAS,OAAO,WAC5B,QAAO;GACL,aAAa,MAAM;GACnB,WAAW,CAAC,GAAG,MAAM,UAAU;GAChC;;AAKP,MAAK,IAAI,IAAI,WAAW,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG;EAClD,MAAM,QAAQ,WAAW;AACzB,MAAI,SAAS,QAAQ,qBAAqB,IAAI,MAAM,YAAY,CAC9D;AAEF,SAAO;GACL,aAAa,MAAM;GACnB,WAAW,CAAC,GAAG,MAAM,UAAU;GAChC;;AAEH,QAAO;;AAST,MAAM,2CAA2B,IAAI,SAGlC;;;;;;;;AASH,SAAgB,mCACd,YACA,KACM;CACN,IAAI,QAAQ,yBAAyB,IAAI,WAAW;AACpD,KAAI,SAAS,MAAM;AACjB,UAAQ;GAAE,WAAW;GAAO,WAAW;GAAI;AAC3C,2BAAyB,IAAI,YAAY,MAAM;;AAEjD,OAAM,MAAM;AACZ,KAAI,MAAM,UAAW;AACrB,OAAM,YAAY;AACb,SAAQ,SAAS,CAAC,WAAW;AAChC,QAAO,YAAY;AACnB,QAAO,KAAK;GACZ;;;;;;AAOJ,SAAgB,mCACd,QACA,OACA,YACA,SACM;AACN,KAAI,CAAC,OAAO,UAAU,CAAC,OAAQ;CAE/B,MAAM,aAAa,OAAO;AAC1B,KAAI,CAAC,MAAM,QAAQ,WAAW,IAAI,WAAW,WAAW,EAAG;CAE3D,MAAM,QAAQ,QAAQ,WAAW,QAAQ,KAAK;CAC9C,MAAM,UAID,EAAE;CACP,MAAM,kCAAkB,IAAI,KAAa;AAEzC,MAAK,MAAM,aAAa,YAA2B;EACjD,MAAM,oBAAoB,kCACxB,UAAU,MACX;AACD,MAAI,CAAC,kBAAmB;EAExB,MAAM,cAAc,UAAU,MAAM,kBAAkB,SAAS,MAAM;EACrE,MAAM,aAAa,kBAAkB,SAAS,MAAM;AACpD,MAAI,WAAW,IAAI,YAAY,CAAE;AAQjC,MAAI,cAAc,WAAW,IAAI,WAAW,CAAE;AAC9C,MAAI,cAAc,gBAAgB,IAAI,WAAW,CAAE;AACnD,MAAI,WAAY,iBAAgB,IAAI,WAAW;AAI/C,aAAW,IAAI,YAAY;AAC3B,MAAI,WAAY,YAAW,IAAI,WAAW;AAE1C,UAAQ,KAAK;GAAE;GAAa;GAAmB;GAAY,CAAC;;AAG9D,KAAI,QAAQ,WAAW,EAAG;AAE1B,aAAY;AACV,GAAM,YAAY;GAChB,MAAM,UAAU,MAAM,QAAQ,IAC5B,QAAQ,IAAI,OAAO,EAAE,aAAa,mBAAmB,iBAAiB;IACpE,MAAM,SAAS,MAAM,4BACnB,mBACA,OACA,QAAQ,OACT;AACD,WAAO;KACL;KACA,YAAY,OAAO,cAAc;KACjC,OAAO,OAAO;KACf;KACD,CACH;AACD,SAAM,QAAQ,QACZ,QAAQ,aAAa,gCAAgC,QAAQ,CAAC,CAC/D;MACC;GACJ"}
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 +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,UAiJW,yCAAA;EACf,MAAA,GAAS,cAAA;EACT,YAAA,GAAe,OAAA;IAAW,MAAA;EAAA,aAA6B,OAAA;EACvD,KAAA,IAAS,GAAA;AAAA;;;;;;;;iBAeK,kCAAA,CACd,UAAA,EAAY,GAAA,UACZ,GAAA;;;;;iBAoBc,kCAAA,CACd,MAAA,EAAQ,MAAA,sCACR,KAAA,EAAO,0BAAA,gBACP,UAAA,EAAY,GAAA,UACZ,OAAA,EAAS,yCAAA"}
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 +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,UAiJW,yCAAA;EACf,MAAA,GAAS,cAAA;EACT,YAAA,GAAe,OAAA;IAAW,MAAA;EAAA,aAA6B,OAAA;EACvD,KAAA,IAAS,GAAA;AAAA;;;;;;;;iBAeK,kCAAA,CACd,UAAA,EAAY,GAAA,UACZ,GAAA;;;;;iBAoBc,kCAAA,CACd,MAAA,EAAQ,MAAA,sCACR,KAAA,EAAO,0BAAA,gBACP,UAAA,EAAY,GAAA,UACZ,OAAA,EAAS,yCAAA"}
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"}
@@ -121,29 +121,6 @@ function headlessToolsBatchResumeCommand(entries) {
121
121
  return { resume };
122
122
  }
123
123
  /**
124
- * True when every top-level resume key is a graph task interrupt id
125
- * (32-char hex from `values.__interrupt__`).
126
- */
127
- function isInterruptIdKeyedResume(resume) {
128
- if (resume == null || typeof resume !== "object" || Array.isArray(resume)) return false;
129
- const keys = Object.keys(resume);
130
- if (keys.length === 0) return false;
131
- return keys.every((key) => /^[0-9a-f]{32}$/i.test(key));
132
- }
133
- /**
134
- * Normalize `command.resume` into the `run.start` input the API turns
135
- * into `Command({ resume })`. Interrupt-id keyed payloads pass through;
136
- * tool-call-keyed and generic payloads are wrapped under the matching
137
- * protocol interrupt id.
138
- */
139
- function buildResumeRunInput(resume, interrupts, resolvedInterruptIds) {
140
- if (resume == null) return null;
141
- if (isInterruptIdKeyedResume(resume)) return resume;
142
- const target = resolveInterruptTargetForHeadlessResume(resume, interrupts, resolvedInterruptIds);
143
- if (target == null) return null;
144
- return { [target.interruptId]: resume };
145
- }
146
- /**
147
124
  * Reads the tool-call id from a headless-tool resume command shaped as
148
125
  * `{ [toolCallId]: result }`.
149
126
  */
@@ -246,6 +223,6 @@ function flushPendingHeadlessToolInterrupts(values, tools, handledIds, options)
246
223
  });
247
224
  }
248
225
  //#endregion
249
- export { buildResumeRunInput, executeHeadlessTool, filterOutHeadlessToolInterrupts, findHeadlessTool, flushPendingHeadlessToolInterrupts, handleHeadlessToolInterrupt, headlessToolResumeCommand, isHeadlessToolInterrupt, parseHeadlessToolInterruptPayload, resolveInterruptTargetForHeadlessResume, scheduleCoalescedHeadlessToolFlush };
226
+ export { executeHeadlessTool, filterOutHeadlessToolInterrupts, findHeadlessTool, flushPendingHeadlessToolInterrupts, handleHeadlessToolInterrupt, headlessToolResumeCommand, isHeadlessToolInterrupt, parseHeadlessToolInterruptPayload, resolveInterruptTargetForHeadlessResume, scheduleCoalescedHeadlessToolFlush };
250
227
 
251
228
  //# 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\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\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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"}