{"version":3,"file":"convert.cjs","names":[],"sources":["../../src/stream/convert.ts"],"sourcesContent":["/**\n * Protocol event conversion — maps raw `[ns, mode, payload]` stream chunks\n * from graph.stream() to CDDL-aligned ProtocolEvents.\n */\n\nimport type { StreamMode } from \"../pregel/types.js\";\nimport type {\n  Namespace,\n  ProtocolEvent,\n  ToolsEventData,\n  UpdatesEventData,\n} from \"./types.js\";\n\n/**\n * The set of stream modes requested by\n * `streamEvents(..., { version: \"v3\" })` — every mode the protocol maps\n * to a channel.\n *\n * The verbose `\"debug\"` mode is intentionally excluded: it was a thin\n * re-wrap of `checkpoints` + `tasks` carrying no new information.\n *\n * The `\"checkpoints\"` mode is likewise excluded from the stream-mode\n * request because the protocol's `checkpoints` channel carries only a\n * lightweight envelope (`id`, `parent_id`, `step`, `source`) emitted as a\n * separate ``[namespace, \"checkpoints\", envelope]`` chunk before each paired\n * `values` chunk — not the full-state shape from Pregel's `checkpoints`\n * stream mode when subscribed via `debug`.\n */\nexport const STREAM_EVENTS_V3_MODES: StreamMode[] = [\n  \"values\",\n  \"updates\",\n  \"messages\",\n  \"tools\",\n  \"custom\",\n  \"tasks\",\n];\n\n/**\n * True when `payload` is a lightweight checkpoint envelope (not a full-state\n * Pregel `checkpoints` debug payload).\n */\nexport function isCheckpointEnvelope(payload: unknown): boolean {\n  if (payload == null || typeof payload !== \"object\") return false;\n  const p = payload as Record<string, unknown>;\n  return (\n    typeof p.id === \"string\" &&\n    (\"source\" in p || typeof p.step === \"number\") &&\n    !(\"values\" in p) &&\n    !(\"config\" in p)\n  );\n}\n\nexport interface ConvertToProtocolEventOptions {\n  namespace: Namespace;\n  mode: StreamMode;\n  payload: unknown;\n  seq: number;\n}\n\nfunction unwrapMessagesPayload(payload: unknown) {\n  if (!Array.isArray(payload) || payload.length !== 2) {\n    return { data: payload };\n  }\n\n  const [data, metadata] = payload;\n  if (metadata == null || typeof metadata !== \"object\") {\n    return { data: payload };\n  }\n\n  const record = metadata as Record<string, unknown>;\n  const node =\n    typeof record.langgraph_node === \"string\"\n      ? record.langgraph_node\n      : undefined;\n  const runId = typeof record.run_id === \"string\" ? record.run_id : undefined;\n  const dataWithRunId =\n    runId != null && data != null && typeof data === \"object\"\n      ? { ...(data as Record<string, unknown>), run_id: runId }\n      : data;\n\n  return { data: dataWithRunId, node };\n}\n\nexport function convertToProtocolEvent({\n  namespace: ns,\n  mode,\n  payload,\n  seq,\n}: ConvertToProtocolEventOptions): ProtocolEvent[] {\n  const timestamp = Date.now();\n  const base = { type: \"event\" as const };\n\n  switch (mode) {\n    case \"messages\": {\n      const { data, node } = unwrapMessagesPayload(payload);\n      return [\n        {\n          ...base,\n          seq,\n          method: \"messages\",\n          params: { namespace: ns, timestamp, ...(node ? { node } : {}), data },\n        },\n      ];\n    }\n\n    case \"tools\":\n      return [\n        {\n          ...base,\n          seq,\n          method: \"tools\",\n          params: {\n            namespace: ns,\n            timestamp,\n            data: convertToolsPayload(payload),\n          },\n        },\n      ];\n\n    case \"checkpoints\": {\n      if (!isCheckpointEnvelope(payload)) {\n        return [];\n      }\n      return [\n        {\n          ...base,\n          seq,\n          method: \"checkpoints\",\n          params: { namespace: ns, timestamp, data: payload },\n        },\n      ];\n    }\n\n    case \"values\": {\n      return [\n        {\n          ...base,\n          seq,\n          method: \"values\",\n          params: { namespace: ns, timestamp, data: payload },\n        },\n      ];\n    }\n\n    case \"updates\": {\n      const data = convertUpdatesPayload(payload);\n      return [\n        {\n          ...base,\n          seq,\n          method: \"updates\",\n          params: {\n            namespace: ns,\n            timestamp,\n            // Surface the completed node at the top level of `params` so\n            // transformers (notably `LifecycleTransformer`) can attribute\n            // a finished task to its child namespace without re-parsing\n            // the payload.  The same value is retained inside `data` for\n            // downstream consumers that inspect the event body.\n            ...(typeof data.node === \"string\" ? { node: data.node } : {}),\n            data,\n          },\n        },\n      ];\n    }\n\n    case \"custom\": {\n      const data =\n        typeof payload === \"object\" &&\n        payload !== null &&\n        !Array.isArray(payload) &&\n        \"name\" in payload\n          ? payload\n          : { payload };\n      return [\n        {\n          ...base,\n          seq,\n          method: \"custom\",\n          params: { namespace: ns, timestamp, data },\n        },\n      ];\n    }\n\n    case \"tasks\":\n      return [\n        {\n          ...base,\n          seq,\n          method: \"tasks\",\n          params: { namespace: ns, timestamp, data: payload },\n        },\n      ];\n\n    default:\n      return [];\n  }\n}\n\n/**\n * Normalises a raw tools-mode payload into a typed {@link ToolsEventData}\n * discriminated union, mapping internal lifecycle events (`on_tool_start`,\n * `on_tool_end`, etc.) to their protocol counterparts.\n *\n * @param payload - The raw payload from a `\"tools\"` stream chunk.\n * @returns A {@link ToolsEventData} object with the appropriate `event`\n *   discriminant and associated fields.\n */\nfunction convertToolsPayload(payload: unknown): ToolsEventData {\n  if (typeof payload !== \"object\" || payload === null) {\n    return {\n      event: \"tool-error\",\n      tool_call_id: \"\",\n      message: \"Unexpected tools payload shape\",\n    };\n  }\n\n  const p = payload as Record<string, unknown>;\n  const tool_call_id = String(p.toolCallId ?? \"\");\n\n  switch (p.event) {\n    case \"on_tool_start\":\n      return {\n        event: \"tool-started\",\n        tool_call_id,\n        tool_name: String(p.name ?? \"unknown\"),\n        input: p.input,\n      };\n\n    case \"on_tool_event\": {\n      const delta =\n        typeof p.data === \"string\" ? p.data : JSON.stringify(p.data ?? \"\");\n      return {\n        event: \"tool-output-delta\",\n        tool_call_id,\n        delta,\n      };\n    }\n\n    case \"on_tool_end\":\n      return {\n        event: \"tool-finished\",\n        tool_call_id,\n        output: p.output,\n      };\n\n    case \"on_tool_error\": {\n      const err = p.error;\n      const errMessage =\n        typeof err === \"object\" &&\n        err !== null &&\n        \"message\" in err &&\n        typeof (err as { message: unknown }).message === \"string\"\n          ? (err as { message: string }).message\n          : String(err ?? \"unknown error\");\n      return {\n        event: \"tool-error\",\n        tool_call_id,\n        message: errMessage,\n      };\n    }\n\n    default:\n      return {\n        event: \"tool-error\",\n        tool_call_id: \"\",\n        message: `Unknown tool event: ${String(p.event)}`,\n      };\n  }\n}\n\n/**\n * Extracts the first `{node: delta}` entry from an updates-mode payload and\n * reshapes it into an {@link UpdatesEventData} with explicit `node` and\n * `values` fields.  Non-object payloads are coerced to `{ values: {} }`.\n *\n * @param payload - The raw payload from an `\"updates\"` stream chunk,\n *   expected to be a `Record<string, unknown>` keyed by node name.\n * @returns An {@link UpdatesEventData} containing the extracted node name\n *   and its associated delta values.\n */\nfunction convertUpdatesPayload(payload: unknown): UpdatesEventData {\n  if (typeof payload !== \"object\" || payload === null) {\n    return { values: {} };\n  }\n\n  const entries = Object.entries(payload as Record<string, unknown>);\n  if (entries.length === 0) {\n    return { values: {} };\n  }\n\n  const [node, values] = entries[0];\n  return {\n    node,\n    values: (typeof values === \"object\" && values !== null\n      ? values\n      : { value: values }) as Record<string, unknown>,\n  };\n}\n"],"mappings":"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"}