{"version":3,"file":"branching.cjs","names":["findLast"],"sources":["../../src/ui/branching.ts"],"sourcesContent":["import type { ThreadState } from \"../schema.js\";\nimport { Message } from \"../types.messages.js\";\nimport { findLast } from \"./utils.js\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ninterface Node<StateType = any> {\n  type: \"node\";\n  value: ThreadState<StateType>;\n  path: string[];\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ninterface Fork<StateType = any> {\n  type: \"fork\";\n  items: Array<Sequence<StateType>>;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport interface Sequence<StateType = any> {\n  type: \"sequence\";\n  items: Array<Node<StateType> | Fork<StateType>>;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ninterface ValidFork<StateType = any> {\n  type: \"fork\";\n  items: Array<ValidSequence<StateType>>;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ninterface ValidSequence<StateType = any> {\n  type: \"sequence\";\n  items: [Node<StateType>, ...(Node<StateType> | ValidFork<StateType>)[]];\n}\n\nexport function getBranchSequence<StateType extends Record<string, unknown>>(\n  history: ThreadState<StateType>[]\n) {\n  const nodeIds = new Set<string>();\n  const childrenMap: Record<string, ThreadState<StateType>[]> = {};\n\n  // Short circuit if there's only a singular one state\n  if (history.length <= 1) {\n    return {\n      rootSequence: {\n        type: \"sequence\",\n        items: history.map((value) => ({ type: \"node\", value, path: [] })),\n      } satisfies Sequence<StateType>,\n      paths: [],\n    };\n  }\n\n  // First pass - collect nodes for each checkpoint\n  history.forEach((state) => {\n    const checkpointId = state.parent_checkpoint?.checkpoint_id ?? \"$\";\n    childrenMap[checkpointId] ??= [];\n    childrenMap[checkpointId].push(state);\n\n    if (state.checkpoint?.checkpoint_id != null) {\n      nodeIds.add(state.checkpoint.checkpoint_id);\n    }\n  });\n\n  // If dealing with partial history, take the branch\n  // with the latest checkpoint and mark it as the root.\n  const maxId = (...ids: (string | null)[]) =>\n    ids\n      .filter((i): i is string => i != null)\n      .sort((a, b) => a.localeCompare(b))\n      .at(-1)!;\n\n  const lastOrphanedNode =\n    childrenMap.$ == null\n      ? Object.keys(childrenMap)\n          .filter((parentId) => !nodeIds.has(parentId))\n          .map((parentId) => {\n            const queue: string[] = [parentId];\n            const seen = new Set<string>();\n\n            let lastId = parentId;\n\n            while (queue.length > 0) {\n              const current = queue.shift()!;\n\n              if (seen.has(current)) continue;\n              seen.add(current);\n\n              const children = (childrenMap[current] ?? []).flatMap(\n                (i) => i.checkpoint?.checkpoint_id ?? []\n              );\n\n              lastId = maxId(lastId, ...children);\n              queue.push(...children);\n            }\n\n            return { parentId, lastId };\n          })\n          .sort((a, b) => a.lastId.localeCompare(b.lastId))\n          .at(-1)?.parentId\n      : undefined;\n\n  if (lastOrphanedNode != null) childrenMap.$ = childrenMap[lastOrphanedNode];\n\n  // Second pass - create a tree of sequences\n  type Task = { id: string; sequence: Sequence; path: string[] };\n  const rootSequence: Sequence = { type: \"sequence\", items: [] };\n  const queue: Task[] = [{ id: \"$\", sequence: rootSequence, path: [] }];\n\n  const paths: string[][] = [];\n\n  const visited = new Set<string>();\n  while (queue.length > 0) {\n    const task = queue.shift()!;\n    if (visited.has(task.id)) continue;\n    visited.add(task.id);\n\n    const children = childrenMap[task.id];\n    if (children == null || children.length === 0) continue;\n\n    // If we've encountered a fork (2+ children), push the fork\n    // to the sequence and add a new sequence for each child\n    let fork: Fork | undefined;\n    if (children.length > 1) {\n      fork = { type: \"fork\", items: [] };\n      task.sequence.items.push(fork);\n    }\n\n    for (const value of children) {\n      const id = value.checkpoint?.checkpoint_id;\n      if (id == null) continue;\n\n      let { sequence } = task;\n      let { path } = task;\n      if (fork != null) {\n        sequence = { type: \"sequence\", items: [] };\n        fork.items.unshift(sequence);\n\n        path = path.slice();\n        path.push(id);\n        paths.push(path);\n      }\n\n      sequence.items.push({ type: \"node\", value, path });\n      queue.push({ id, sequence, path });\n    }\n  }\n\n  return { rootSequence, paths };\n}\n\nconst PATH_SEP = \">\";\nconst ROOT_ID = \"$\";\n\ntype BranchByCheckpoint = Record<\n  string,\n  { branch: string | undefined; branchOptions: string[] | undefined }\n>;\n\n// Get flat view\nexport function getBranchView<StateType extends Record<string, unknown>>(\n  sequence: Sequence<StateType>,\n  paths: string[][],\n  branch: string\n) {\n  const path = branch.split(PATH_SEP);\n  const pathMap: Record<string, string[][]> = {};\n\n  for (const path of paths) {\n    const parent = path.at(-2) ?? ROOT_ID;\n    pathMap[parent] ??= [];\n    pathMap[parent].unshift(path);\n  }\n\n  const history: ThreadState<StateType>[] = [];\n  const branchByCheckpoint: BranchByCheckpoint = {};\n\n  const forkStack = path.slice();\n  const queue: (Node<StateType> | Fork<StateType>)[] = [...sequence.items];\n\n  while (queue.length > 0) {\n    const item = queue.shift()!;\n\n    if (item.type === \"node\") {\n      history.push(item.value);\n      const checkpointId = item.value.checkpoint?.checkpoint_id;\n      if (checkpointId == null) continue;\n\n      branchByCheckpoint[checkpointId] = {\n        branch: item.path.join(PATH_SEP),\n        branchOptions: (item.path.length > 0\n          ? (pathMap[item.path.at(-2) ?? ROOT_ID] ?? [])\n          : []\n        ).map((p) => p.join(PATH_SEP)),\n      };\n    }\n    if (item.type === \"fork\") {\n      const forkId = forkStack.shift();\n      const index =\n        forkId != null\n          ? item.items.findIndex((value) => {\n              const firstItem = value.items.at(0);\n              if (!firstItem || firstItem.type !== \"node\") return false;\n              return firstItem.value.checkpoint?.checkpoint_id === forkId;\n            })\n          : -1;\n\n      const nextItems = item.items.at(index)?.items ?? [];\n      queue.push(...nextItems);\n    }\n  }\n\n  return { history, branchByCheckpoint };\n}\n\nexport function getBranchContext<StateType extends Record<string, unknown>>(\n  branch: string,\n  history: ThreadState<StateType>[] | undefined\n) {\n  const { rootSequence: branchTree, paths } = getBranchSequence(history ?? []);\n  const { history: flatHistory, branchByCheckpoint } = getBranchView(\n    branchTree,\n    paths,\n    branch\n  );\n\n  return {\n    branchTree,\n    flatHistory,\n    branchByCheckpoint,\n    threadHead: flatHistory.at(-1),\n  };\n}\n\nexport function getMessagesMetadataMap<\n  StateType extends Record<string, unknown>,\n>(options: {\n  initialValues: StateType | null | undefined;\n  history: ThreadState<StateType>[] | null | undefined;\n  getMessages: (values: StateType) => Message[];\n\n  branchContext: {\n    threadHead: ThreadState<StateType> | undefined;\n    branchByCheckpoint: BranchByCheckpoint;\n  };\n}) {\n  const currentValues =\n    options.branchContext.threadHead?.values ??\n    options.initialValues ??\n    ({} as StateType);\n\n  const alreadyShown = new Set<string>();\n  return options.getMessages(currentValues).map((message, idx) => {\n    const messageId = message.id ?? idx;\n\n    // Find the first checkpoint where the message was seen\n    const firstSeenState = findLast(\n      options.history ?? [],\n      (state) =>\n        state.values != null &&\n        options\n          .getMessages(state.values)\n          .map((m, idx) => m.id ?? idx)\n          .includes(messageId)\n    );\n\n    const checkpointId = firstSeenState?.checkpoint?.checkpoint_id;\n    let branch =\n      checkpointId != null\n        ? options.branchContext.branchByCheckpoint[checkpointId]\n        : undefined;\n    if (!branch?.branch?.length) branch = undefined;\n\n    // serialize branches\n    const optionsShown = branch?.branchOptions?.flat(2).join(\",\");\n    if (optionsShown) {\n      if (alreadyShown.has(optionsShown)) branch = undefined;\n      alreadyShown.add(optionsShown);\n    }\n\n    return {\n      messageId: messageId.toString(),\n      firstSeenState,\n\n      branch: branch?.branch,\n      branchOptions: branch?.branchOptions,\n    };\n  });\n}\n"],"mappings":"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"}