{"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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"}