{"version":3,"file":"tool-call.cjs","names":["ToolMessage","StreamChannel"],"sources":["../../../src/agents/transformers/tool-call.ts"],"sourcesContent":["import {\n  StreamChannel,\n  type NativeStreamTransformer,\n  type ProtocolEvent,\n  type ToolCallStream,\n  type ToolCallStatus,\n  type ToolsEventData,\n  type Namespace,\n} from \"@langchain/langgraph\";\nimport { ToolMessage } from \"@langchain/core/messages\";\n\ninterface ToolCallProjection {\n  toolCalls: AsyncIterable<ToolCallStream>;\n}\n\n/**\n * Returns true when `ns` belongs to the agent's own graph — i.e. it\n * starts with `path` and is at most one level deeper (the agent's\n * internal nodes like `tools`, `model_request`, etc.).\n *\n * Events from subagent subgraphs (two or more levels deeper) are\n * excluded, so `run.toolCalls` / `run.middleware` only show events\n * from the agent itself, not from its subagents.\n */\nfunction isOwnEvent(ns: Namespace, path: Namespace): boolean {\n  if (ns.length < path.length || ns.length > path.length + 1) return false;\n  for (let i = 0; i < path.length; i += 1) {\n    if (ns[i] !== path[i]) return false;\n  }\n  return true;\n}\n\n/**\n * Detects when a `tool-error` payload is actually a graph interrupt rather\n * than a genuine tool failure.\n *\n * A tool that calls `interrupt()` throws a `GraphInterrupt`, whose message is\n * the JSON-serialized `Interrupt[]` array. Each entry has the LangGraph\n * `Interrupt` shape `{ id, value }`: a stable `id` (a hash of the checkpoint\n * namespace, generated by `interrupt()` and always present during graph\n * execution) plus the `value` passed to `interrupt(...)`. We require BOTH a\n * string `id` and a `value` on every entry — a bare `value` is not a reliable\n * discriminator, since a genuine tool error message can also be a JSON array\n * of `{ value }` records (e.g. a validator emitting\n * `[{ \"value\": \"bad input\", \"message\": \"invalid\" }]`). Keying off the\n * interrupt `id` keeps real tool failures on the error path.\n *\n * An interrupt is control flow that *suspends* the run (the tool re-runs on\n * resume); it is not an error, so the tool call must stay pending rather than\n * have its `output` promise rejected. Any interrupt qualifies regardless of\n * its `value` shape: HITL middleware interrupts (`value.type === \"tool\"`) and\n * raw `interrupt(...)` calls from inside a tool are treated identically —\n * raising an interrupt in a tool must work whether or not\n * `humanInTheLoopMiddleware` is involved.\n */\nfunction isToolInterrupt(message: string): boolean {\n  let parsed: unknown;\n  try {\n    parsed = JSON.parse(message);\n  } catch {\n    return false;\n  }\n  if (!Array.isArray(parsed) || parsed.length === 0) return false;\n  return parsed.every((entry) => {\n    if (entry == null || typeof entry !== \"object\") return false;\n    const record = entry as Record<string, unknown>;\n    return typeof record.id === \"string\" && \"value\" in record;\n  });\n}\n\n/**\n * Detects serialized LangChain `ToolMessage` values that can appear on\n * `tool-finished.output` after crossing a protocol or serialization boundary.\n *\n * @example\n * ```ts\n * {\n *   lc: 1,\n *   type: \"constructor\",\n *   id: [\"langchain_core\", \"messages\", \"ToolMessage\"],\n *   kwargs: { content: \"raw tool result\", tool_call_id: \"call_1\" }\n * }\n * ```\n */\nfunction isSerializedToolMessage(\n  value: unknown\n): value is { kwargs?: { content?: unknown } } {\n  if (value == null || typeof value !== \"object\") return false;\n  const record = value as Record<string, unknown>;\n  if (record.type !== \"constructor\" || !Array.isArray(record.id)) return false;\n  return record.id[record.id.length - 1] === \"ToolMessage\";\n}\n\nfunction normalizeToolOutput(output: unknown): unknown {\n  if (ToolMessage.isInstance(output)) {\n    return output.content;\n  }\n  if (isSerializedToolMessage(output)) {\n    return output.kwargs?.content;\n  }\n  return output;\n}\n\n/**\n * Creates a native transformer that correlates `tools` channel events\n * into per-call {@link ToolCallStream} objects.\n *\n * Marked `__native: true` — projection keys land directly on the\n * `GraphRunStream` instance as `run.toolCalls`.\n */\nexport function createToolCallTransformer(\n  path: Namespace\n): () => NativeStreamTransformer<ToolCallProjection> {\n  return () => {\n    const toolCallsLog = StreamChannel.local<ToolCallStream>();\n\n    const pendingCalls = new Map<\n      string,\n      {\n        resolveOutput: (v: unknown) => void;\n        rejectOutput: (e: unknown) => void;\n        resolveStatus: (v: ToolCallStatus) => void;\n        resolveError: (v: string | undefined) => void;\n      }\n    >();\n\n    function createToolCallEntry(\n      callId: string,\n      name: string,\n      rawInput: unknown\n    ): void {\n      if (pendingCalls.has(callId)) return;\n      const input =\n        typeof rawInput === \"string\" ? JSON.parse(rawInput) : rawInput;\n\n      let resolveOutput!: (v: unknown) => void;\n      let rejectOutput!: (e: unknown) => void;\n      let resolveStatus!: (v: ToolCallStatus) => void;\n      let resolveError!: (v: string | undefined) => void;\n\n      const output = new Promise<unknown>((res, rej) => {\n        resolveOutput = res;\n        rejectOutput = rej;\n      });\n      const status = new Promise<ToolCallStatus>((res) => {\n        resolveStatus = res;\n      });\n      const error = new Promise<string | undefined>((res) => {\n        resolveError = res;\n      });\n\n      pendingCalls.set(callId, {\n        resolveOutput,\n        rejectOutput,\n        resolveStatus,\n        resolveError,\n      });\n\n      toolCallsLog.push({\n        name,\n        callId,\n        input,\n        output,\n        status,\n        error,\n      } as ToolCallStream);\n    }\n\n    return {\n      __native: true as const,\n\n      init: () => ({\n        toolCalls: toolCallsLog,\n      }),\n\n      process(event: ProtocolEvent): boolean {\n        /**\n         * Only process events that are at the same depth as the agent's graph.\n         */\n        if (!isOwnEvent(event.params.namespace, path)) return true;\n\n        if (event.method === \"messages\") {\n          const data = event.params.data as Record<string, unknown>;\n          if (data.event === \"content-block-finish\") {\n            const cb = (data.contentBlock ?? data.content_block) as\n              | Record<string, unknown>\n              | undefined;\n            if (cb?.type === \"tool_call\") {\n              createToolCallEntry(\n                String(cb.id ?? \"\"),\n                String(cb.name ?? \"\"),\n                cb.args ?? cb.input\n              );\n            }\n          }\n        }\n\n        if (event.method === \"tools\") {\n          const data = event.params.data as ToolsEventData;\n          const toolCallId = (data as Record<string, unknown>)\n            .tool_call_id as string;\n\n          if (data.event === \"tool-started\") {\n            createToolCallEntry(\n              toolCallId,\n              ((data as Record<string, unknown>).tool_name as string) ??\n                \"unknown\",\n              (data as Record<string, unknown>).input\n            );\n          }\n\n          const pending = toolCallId ? pendingCalls.get(toolCallId) : undefined;\n\n          if (pending) {\n            if (data.event === \"tool-finished\") {\n              pending.resolveOutput(\n                normalizeToolOutput((data as Record<string, unknown>).output)\n              );\n              pending.resolveStatus(\"finished\");\n              pending.resolveError(undefined);\n              pendingCalls.delete(toolCallId);\n            } else if (data.event === \"tool-error\") {\n              const message =\n                ((data as Record<string, unknown>).message as string) ??\n                \"unknown error\";\n              // An interrupt raised inside a tool (HITL middleware *or* a\n              // raw `interrupt()`) surfaces here as a `tool-error` whose\n              // message is the serialized interrupt. It is control flow,\n              // not a failure: keep the call pending (it re-runs on resume)\n              // and never reject `output`, which would otherwise become an\n              // unhandled rejection and crash the run.\n              if (isToolInterrupt(message)) {\n                return true;\n              }\n              pending.rejectOutput(new Error(message));\n              pending.resolveStatus(\"error\");\n              pending.resolveError(message);\n              pendingCalls.delete(toolCallId);\n            }\n          }\n        }\n\n        return true;\n      },\n\n      finalize(): void {\n        for (const pending of pendingCalls.values()) {\n          pending.resolveStatus(\"finished\");\n          pending.resolveError(undefined);\n          pending.resolveOutput(undefined);\n        }\n        pendingCalls.clear();\n        toolCallsLog.close();\n      },\n\n      fail(err: unknown): void {\n        for (const pending of pendingCalls.values()) {\n          pending.resolveStatus(\"error\");\n          pending.resolveError(\n            err instanceof Error ? err.message : String(err)\n          );\n          pending.rejectOutput(err);\n        }\n        pendingCalls.clear();\n        toolCallsLog.fail(err);\n      },\n    };\n  };\n}\n"],"mappings":"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"}