{"version":3,"file":"optimistic-input.cjs","names":["toMessageDict","ensureMessageInstances"],"sources":["../../src/stream/optimistic-input.ts"],"sourcesContent":["/**\n * Pure helpers for the optimistic `submit()` path.\n *\n * Splitting the input-shaping logic out of {@link StreamController}\n * keeps it unit-testable in isolation: given a raw submit input it\n * produces (a) the payload to dispatch to the server — with stable ids\n * minted for any id-less message so the server echo reconciles by id —\n * and (b) the coerced `BaseMessage` instances to append to the root\n * projection immediately.\n *\n * No mutation of the caller's input is performed: message entries are\n * rebuilt as fresh dicts before ids are injected, and the top-level\n * object is shallow-cloned.\n */\nimport type { BaseMessage } from \"@langchain/core/messages\";\nimport type { Message } from \"../types.messages.js\";\nimport { toMessageDict } from \"../ui/messages.js\";\nimport { ensureMessageInstances } from \"./message-coercion.js\";\n\n/**\n * Pre-submit snapshot of a single non-message `values` key, captured so\n * it can be rolled back if the run fails before the server echoes any\n * `values`.\n */\nexport interface OptimisticKeySnapshot {\n  readonly key: string;\n  /** Whether the key existed in `values` before the optimistic merge. */\n  readonly hadKey: boolean;\n  /** The pre-submit value (meaningful only when `hadKey` is true). */\n  readonly prevValue: unknown;\n}\n\n/**\n * Opaque handle returned by the controller's optimistic apply step and\n * threaded back through the submit coordinator to the terminal\n * reconciliation step. Carries the echoed message ids (to transition\n * `pending` → `sent` / `failed`) and the non-message key snapshot (to\n * roll back on failure-before-echo).\n */\nexport interface OptimisticHandle {\n  readonly echoedIds: string[];\n  readonly restoreKeys: OptimisticKeySnapshot[];\n}\n\n/**\n * Result of preparing a raw submit input for optimistic dispatch.\n */\nexport interface PreparedOptimisticInput {\n  /**\n   * Input to actually send to the server. The messages key (when\n   * present) is normalized to an array of message dicts, each carrying\n   * a stable id; all other keys are copied verbatim.\n   */\n  readonly dispatchInput: Record<string, unknown>;\n  /** Coerced message instances (with ids) to append to the projection. */\n  readonly optimisticMessages: BaseMessage[];\n  /** Ids of the messages echoed optimistically (minted or pre-existing). */\n  readonly echoedIds: string[];\n  /** Non-message input keys to shallow-merge into `values`. */\n  readonly extraValues: Record<string, unknown>;\n}\n\nfunction isBaseMessageInstance(value: unknown): value is BaseMessage {\n  return (\n    value != null &&\n    typeof (value as { getType?: unknown }).getType === \"function\"\n  );\n}\n\n/**\n * Serialize `BaseMessage` instances under a state update's message key\n * into plain message dicts so they survive JSON transport.\n *\n * `respond({ update })` / `respondAll({}, { update })` fold `update` into\n * `Command(update=...)` on the wire. A `BaseMessage`'s default JSON form\n * is the `lc` \"constructor\" envelope (`{ lc, type: \"constructor\", id,\n * kwargs }`), which the server's `add_messages` reducer does **not**\n * coerce — whereas the flat `{ type, content, ... }` dict that\n * {@link toMessageDict} emits (and that `submit()` already sends) does.\n * Mirroring the `submit()` path lets\n * `respond({ update: { messages: [new AIMessage(...)] } })` behave like\n * `submit({ messages: [new AIMessage(...)] })`.\n *\n * Only the configured `messagesKey` is touched — in both the object form\n * and the `[key, value][]` tuple form. Every other key, and any already\n * plain (non-`BaseMessage`) entry, passes through untouched.\n */\nexport function serializeUpdateMessages(\n  update: Record<string, unknown> | [string, unknown][],\n  messagesKey: string\n): Record<string, unknown> | [string, unknown][] {\n  if (Array.isArray(update)) {\n    return update.map((entry) =>\n      Array.isArray(entry) && entry[0] === messagesKey\n        ? [entry[0], serializeMessageValue(entry[1])]\n        : entry\n    ) as [string, unknown][];\n  }\n  if (!(messagesKey in update)) return update;\n  return {\n    ...update,\n    [messagesKey]: serializeMessageValue(update[messagesKey]),\n  };\n}\n\nfunction serializeMessageValue(value: unknown): unknown {\n  if (isBaseMessageInstance(value)) return toMessageDict(value);\n  if (Array.isArray(value)) {\n    return value.map((item) =>\n      isBaseMessageInstance(item) ? toMessageDict(item) : item\n    );\n  }\n  return value;\n}\n\nfunction extractId(value: unknown): string | undefined {\n  const id = (value as { id?: unknown } | null)?.id;\n  return typeof id === \"string\" && id.length > 0 ? id : undefined;\n}\n\n/**\n * Normalize a message-key value into an array of entries. Mirrors the\n * server's `add_messages` coercion: a bare string or single message\n * object is treated as a one-element list.\n */\nfunction toEntryArray(value: unknown): unknown[] {\n  if (Array.isArray(value)) return value;\n  return [value];\n}\n\n/**\n * Build a message dict carrying `id` from an arbitrary input entry,\n * without mutating the original.\n */\nfunction toDispatchDict(entry: unknown, id: string): Message {\n  if (typeof entry === \"string\") {\n    return { type: \"human\", content: entry, id } as unknown as Message;\n  }\n  if (isBaseMessageInstance(entry)) {\n    return { ...toMessageDict(entry), id } as Message;\n  }\n  return { ...(entry as object), id } as Message;\n}\n\n/**\n * Prepare a raw submit input for optimistic dispatch.\n *\n * @param raw         - Raw input passed to `submit()`. Must be a\n *   non-null, non-array object (caller guards this).\n * @param messagesKey - State key holding the message array.\n * @param mintId      - Factory for stable client message ids.\n * @returns The dispatch payload, optimistic messages, echoed ids, and\n *   the non-message portion of the input.\n */\nexport function prepareOptimisticInput(\n  raw: Record<string, unknown>,\n  messagesKey: string,\n  mintId: () => string\n): PreparedOptimisticInput {\n  const dispatchInput: Record<string, unknown> = { ...raw };\n  const extraValues: Record<string, unknown> = {};\n  for (const key of Object.keys(raw)) {\n    if (key !== messagesKey) extraValues[key] = raw[key];\n  }\n\n  const echoedIds: string[] = [];\n  const messagesValue = raw[messagesKey];\n  if (messagesValue == null) {\n    return { dispatchInput, optimisticMessages: [], echoedIds, extraValues };\n  }\n\n  const entries = toEntryArray(messagesValue);\n  const dispatchEntries: unknown[] = [];\n  const optimisticDicts: Message[] = [];\n  for (const entry of entries) {\n    const echoable =\n      typeof entry === \"string\" ||\n      isBaseMessageInstance(entry) ||\n      (entry != null && typeof entry === \"object\" && !Array.isArray(entry));\n    if (!echoable) {\n      // Non-message-shaped entry (number/bool/null): forward as-is,\n      // nothing to echo.\n      dispatchEntries.push(entry);\n      continue;\n    }\n    const id = extractId(entry) ?? mintId();\n    const dict = toDispatchDict(entry, id);\n    dispatchEntries.push(dict);\n    optimisticDicts.push(dict);\n    echoedIds.push(id);\n  }\n\n  dispatchInput[messagesKey] = dispatchEntries;\n  const optimisticMessages = ensureMessageInstances(\n    optimisticDicts\n  ) as BaseMessage[];\n  return { dispatchInput, optimisticMessages, echoedIds, extraValues };\n}\n"],"mappings":";;;AA8DA,SAAS,sBAAsB,OAAsC;AACnE,QACE,SAAS,QACT,OAAQ,MAAgC,YAAY;;;;;;;;;;;;;;;;;;;;AAsBxD,SAAgB,wBACd,QACA,aAC+C;AAC/C,KAAI,MAAM,QAAQ,OAAO,CACvB,QAAO,OAAO,KAAK,UACjB,MAAM,QAAQ,MAAM,IAAI,MAAM,OAAO,cACjC,CAAC,MAAM,IAAI,sBAAsB,MAAM,GAAG,CAAC,GAC3C,MACL;AAEH,KAAI,EAAE,eAAe,QAAS,QAAO;AACrC,QAAO;EACL,GAAG;GACF,cAAc,sBAAsB,OAAO,aAAa;EAC1D;;AAGH,SAAS,sBAAsB,OAAyB;AACtD,KAAI,sBAAsB,MAAM,CAAE,QAAOA,iBAAAA,cAAc,MAAM;AAC7D,KAAI,MAAM,QAAQ,MAAM,CACtB,QAAO,MAAM,KAAK,SAChB,sBAAsB,KAAK,GAAGA,iBAAAA,cAAc,KAAK,GAAG,KACrD;AAEH,QAAO;;AAGT,SAAS,UAAU,OAAoC;CACrD,MAAM,KAAM,OAAmC;AAC/C,QAAO,OAAO,OAAO,YAAY,GAAG,SAAS,IAAI,KAAK,KAAA;;;;;;;AAQxD,SAAS,aAAa,OAA2B;AAC/C,KAAI,MAAM,QAAQ,MAAM,CAAE,QAAO;AACjC,QAAO,CAAC,MAAM;;;;;;AAOhB,SAAS,eAAe,OAAgB,IAAqB;AAC3D,KAAI,OAAO,UAAU,SACnB,QAAO;EAAE,MAAM;EAAS,SAAS;EAAO;EAAI;AAE9C,KAAI,sBAAsB,MAAM,CAC9B,QAAO;EAAE,GAAGA,iBAAAA,cAAc,MAAM;EAAE;EAAI;AAExC,QAAO;EAAE,GAAI;EAAkB;EAAI;;;;;;;;;;;;AAarC,SAAgB,uBACd,KACA,aACA,QACyB;CACzB,MAAM,gBAAyC,EAAE,GAAG,KAAK;CACzD,MAAM,cAAuC,EAAE;AAC/C,MAAK,MAAM,OAAO,OAAO,KAAK,IAAI,CAChC,KAAI,QAAQ,YAAa,aAAY,OAAO,IAAI;CAGlD,MAAM,YAAsB,EAAE;CAC9B,MAAM,gBAAgB,IAAI;AAC1B,KAAI,iBAAiB,KACnB,QAAO;EAAE;EAAe,oBAAoB,EAAE;EAAE;EAAW;EAAa;CAG1E,MAAM,UAAU,aAAa,cAAc;CAC3C,MAAM,kBAA6B,EAAE;CACrC,MAAM,kBAA6B,EAAE;AACrC,MAAK,MAAM,SAAS,SAAS;AAK3B,MAAI,EAHF,OAAO,UAAU,YACjB,sBAAsB,MAAM,IAC3B,SAAS,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,MAAM,GACvD;AAGb,mBAAgB,KAAK,MAAM;AAC3B;;EAEF,MAAM,KAAK,UAAU,MAAM,IAAI,QAAQ;EACvC,MAAM,OAAO,eAAe,OAAO,GAAG;AACtC,kBAAgB,KAAK,KAAK;AAC1B,kBAAgB,KAAK,KAAK;AAC1B,YAAU,KAAK,GAAG;;AAGpB,eAAc,eAAe;AAI7B,QAAO;EAAE;EAAe,oBAHGC,yBAAAA,uBACzB,gBACD;EAC2C;EAAW;EAAa"}