{"version":3,"file":"controller.cjs","names":["#options","#messagesKey","#subagents","SubagentDiscovery","#subgraphs","SubgraphDiscovery","#messageMetadata","MessageMetadataTracker","#resolvedInterrupts","#selfCreatedThreadIds","#rootEventListeners","#rootBus","ToolCallAssembler","#threadListeners","#currentThreadId","ChannelRegistry","StreamStore","#createInitialSnapshot","#rootMessages","RootMessageProjection","#lifecycleLoading","LifecycleLoadingTracker","#disposed","EMPTY_QUEUE","#submitter","SubmitCoordinator","#ensureThread","#startDeferredRootPump","#abandonDeferredRootPump","#rootPumpReady","#awaitNextTerminal","#thread","buildResumeRunInput","#hydratedActiveInterruptIds","#submitGeneration","#markLocalRunStart","#notifyCreated","#notifyCompleted","#markLocalRunEnd","#hydrationPromise","#createHydrationPromise","#resolveHydration","#rejectHydration","#resetHydrationPromise","#teardownThread","#applyValues","#localRunDepth","#activeRunId","#runLifecycleListener","isRootNamespace","#resolveInterruptForResume","#cancelPendingDispose","#pendingDisposeTimer","#rootPumpDeferred","#startRootPump","#notifyThreadListeners","#threadEventUnsubscribe","#rootSubscription","#rootPump","#rootToolAssembler","#onWildcardEvent","#usesEventStreamTransport","#onRootEvent","#recordRootInterrupt","isInternalWorkNamespace","isLegacySubagentNamespace","upsertToolCall","ensureMessageInstances","reconcileToolCallsFromMessages","resolveInterruptTargetForHeadlessResume"],"sources":["../../src/stream/controller.ts"],"sourcesContent":["/**\n * Framework-agnostic controller for the experimental v2 stream.\n *\n * Responsibilities:\n *  - Owns at most one {@link ThreadStream} at a time (swapped on\n *    `hydrate(newThreadId)` or `dispose`).\n *  - Exposes three always-on observable surfaces via {@link StreamStore}:\n *      - `rootStore`      : root values/messages/toolCalls/interrupts/…\n *      - `subagentStore`  : discovery map of subagents (no content)\n *      - `subgraphStore`  : discovery map of subgraphs  (no content)\n *  - Owns a {@link ChannelRegistry} that framework selector hooks\n *    (`useMessages`, `useToolCalls`, `useExtension`, `useChannel`)\n *    use to lazily open per-namespace subscriptions.\n *  - Imperative run surface: `submit`, `stop`, `respond`, `joinStream`.\n *\n * A single multi-channel subscription (`values`, `lifecycle`, `input`,\n * `messages`, `tools`) powers every always-on projection and both\n * discovery runners. Selector hooks add their own (deduped)\n * subscriptions on top — so even a UI with many subagents only opens\n * one extra subscription per `(channels, namespace)` actually\n * rendered on screen.\n */\nimport { AIMessage, type BaseMessage } from \"@langchain/core/messages\";\nimport type {\n  Channel,\n  Event,\n  LifecycleEvent,\n  MessagesEvent,\n  ToolsEvent,\n  ValuesEvent,\n} from \"@langchain/protocol\";\nimport type { Interrupt } from \"../schema.js\";\nimport type { ThreadStream } from \"../client/stream/index.js\";\nimport type { SubscriptionHandle } from \"../client/stream/index.js\";\nimport { ToolCallAssembler } from \"../client/stream/handles/tools.js\";\nimport { ensureMessageInstances } from \"../ui/messages.js\";\nimport type { Message } from \"../types.messages.js\";\nimport { StreamStore } from \"./store.js\";\nimport { ChannelRegistry } from \"./channel-registry.js\";\nimport {\n  SubagentDiscovery,\n  type SubagentMap,\n  SubgraphDiscovery,\n  type SubgraphMap,\n  type SubgraphByNodeMap,\n} from \"./discovery/index.js\";\nimport {\n  isInternalWorkNamespace,\n  isLegacySubagentNamespace,\n  isRootNamespace,\n} from \"./namespace.js\";\nimport {\n  MessageMetadataTracker,\n  type CheckpointEnvelope,\n  type MessageMetadata,\n  type MessageMetadataMap,\n} from \"./message-metadata-tracker.js\";\nimport { LifecycleLoadingTracker } from \"./lifecycle-loading-tracker.js\";\nimport { RootMessageProjection } from \"./root-message-projection.js\";\nimport {\n  EMPTY_QUEUE,\n  SubmitCoordinator,\n  type SubmissionQueueEntry,\n  type SubmissionQueueSnapshot,\n} from \"./submit-coordinator.js\";\nimport {\n  reconcileToolCallsFromMessages,\n  upsertToolCall,\n} from \"./tool-calls.js\";\nimport {\n  buildResumeRunInput,\n  resolveInterruptTargetForHeadlessResume,\n} from \"../headless-tools.js\";\nimport type {\n  RootEventBus,\n  RootSnapshot,\n  RunExecutionReason,\n  StreamControllerOptions,\n  StreamSubmitOptions,\n} from \"./types.js\";\n\nfunction isAbortLikeError(error: unknown): boolean {\n  if (error == null || typeof error !== \"object\") return false;\n  const maybeError = error as { name?: unknown; message?: unknown };\n  return (\n    maybeError.name === \"AbortError\" ||\n    (typeof maybeError.message === \"string\" &&\n      maybeError.message.includes(\"aborted\"))\n  );\n}\n\nfunction lifecycleReason(event: string | undefined): RunExecutionReason | null {\n  if (event === \"completed\") return \"success\";\n  if (event === \"failed\") return \"error\";\n  if (event === \"interrupted\") return \"interrupt\";\n  return null;\n}\n\nconst ROOT_NAMESPACE: readonly string[] = [];\n\n/**\n * Channel set covered by the always-on root subscription. Exported so\n * projections (and transports) can reason about what the root pump\n * already delivers before opening additional server subscriptions.\n */\nexport const ROOT_PUMP_CHANNELS: readonly Channel[] = [\n  \"values\",\n  \"checkpoints\",\n  \"lifecycle\",\n  \"input\",\n  \"messages\",\n  \"tools\",\n];\n\ninterface ResolvedInterrupt {\n  interruptId: string;\n  namespace: string[];\n}\n\nexport type {\n  MessageMetadata,\n  MessageMetadataMap,\n  SubmissionQueueEntry,\n  SubmissionQueueSnapshot,\n};\n\n/**\n * Coordinates one thread's protocol-v2 stream and exposes stable\n * observable projections for framework bindings.\n *\n * The controller owns the root subscription, lazily binds scoped\n * projections through {@link ChannelRegistry}, and normalizes protocol\n * events into class-message, tool-call, discovery, interrupt, and queue\n * stores.\n *\n * @typeParam StateType - Shape of the graph state exposed on `values`.\n * @typeParam InterruptType - Shape of protocol interrupt payloads.\n * @typeParam ConfigurableType - Shape of `config.configurable` accepted by submit.\n */\nexport class StreamController<\n  StateType extends object = Record<string, unknown>,\n  InterruptType = unknown,\n  ConfigurableType extends object = Record<string, unknown>,\n> {\n  readonly rootStore: StreamStore<RootSnapshot<StateType, InterruptType>>;\n  readonly subagentStore: StreamStore<SubagentMap>;\n  readonly subgraphStore: StreamStore<SubgraphMap>;\n  readonly subgraphByNodeStore: StreamStore<SubgraphByNodeMap>;\n  readonly messageMetadataStore: StreamStore<MessageMetadataMap>;\n  readonly queueStore: StreamStore<SubmissionQueueSnapshot<StateType>>;\n  readonly registry: ChannelRegistry;\n\n  readonly #options: StreamControllerOptions<StateType>;\n  readonly #messagesKey: string;\n  readonly #subagents = new SubagentDiscovery();\n  readonly #subgraphs = new SubgraphDiscovery();\n  readonly #messageMetadata = new MessageMetadataTracker();\n\n  #thread: ThreadStream | undefined;\n  #currentThreadId: string | null;\n  #rootSubscription: SubscriptionHandle<Event> | undefined;\n  #rootPump: Promise<void> | undefined;\n  #rootPumpReady: Promise<void> | undefined;\n  /**\n   * `true` while a self-created thread has its root pump deferred until\n   * the first `submitRun` / `respondInput` commits the thread row\n   * server-side. See `#ensureThread` and `#startDeferredRootPump`.\n   */\n  #rootPumpDeferred = false;\n  #threadEventUnsubscribe: (() => void) | undefined;\n  #disposed = false;\n  #pendingDisposeTimer: ReturnType<typeof setTimeout> | null = null;\n  readonly #resolvedInterrupts = new Set<string>();\n  /**\n   * Set of interrupt IDs the server reports as currently *active* on\n   * the thread (from `state.tasks[].interrupts`). Populated during\n   * {@link hydrate} from `client.threads.getState()` and used as a\n   * strict allowlist when processing replayed `input.requested`\n   * events from the persistent SSE subscription. Without this guard,\n   * SSE replay re-adds historically-requested interrupts that have\n   * since been resolved (no `input.responded` event exists in the\n   * protocol, so the SDK has no other way to tell replay from live\n   * for an idle thread). `null` outside the hydrate-window so\n   * genuinely new live interrupts on an active run aren't filtered;\n   * cleared at the start of `submit()` for the same reason.\n   */\n  #hydratedActiveInterruptIds: Set<string> | null = null;\n  /**\n   * Monotonic counter bumped at the start of each `submit()` and used\n   * by {@link hydrate} to skip its post-fetch allowlist write when a\n   * submit started while the state fetch was in flight. Without this\n   * guard, a submit-then-hydrate race could re-install a stale\n   * allowlist that filters out genuinely-new live interrupts emitted\n   * by the just-started run.\n   */\n  #submitGeneration = 0;\n  /**\n   * Thread ids we minted client-side on first `submit()`. Keeping them\n   * here lets `hydrate()` skip the `threads.getState()` round-trip —\n   * we know there is nothing checkpointed server-side yet (and the\n   * request would 404 and surface a spurious error to the UI).\n   */\n  readonly #selfCreatedThreadIds = new Set<string>();\n  readonly #rootEventListeners = new Set<(event: Event) => void>();\n  readonly #rootBus: RootEventBus;\n  #activeRunId: string | undefined;\n  #localRunDepth = 0;\n\n  /**\n   * Single-shot hydration promise. Exposed via `hydrationPromise`\n   * so Suspense wrappers can throw it until the first hydrate\n   * completes (resolve) or fails (reject). Reset whenever a new\n   * hydrate cycle begins so `<Suspense>` boundaries re-suspend on\n   * thread switch.\n   */\n  #hydrationPromise: Promise<void>;\n  #resolveHydration!: () => void;\n  #rejectHydration!: (error: unknown) => void;\n\n  /**\n   * Tool assembler lives for the lifetime of a thread; reset on\n   * rebind so a fresh thread starts with a clean slate.\n   */\n  #rootToolAssembler = new ToolCallAssembler();\n  #rootMessages!: RootMessageProjection<StateType, InterruptType>;\n  #lifecycleLoading!: LifecycleLoadingTracker<\n    RootSnapshot<StateType, InterruptType>\n  >;\n  #submitter!: SubmitCoordinator<StateType, InterruptType, ConfigurableType>;\n\n  readonly #threadListeners = new Set<\n    (thread: ThreadStream | undefined) => void\n  >();\n\n  /**\n   * Create a controller around a LangGraph client and optional initial thread.\n   *\n   * @param options - Runtime configuration, client, thread id, and initial state.\n   */\n  constructor(options: StreamControllerOptions<StateType>) {\n    this.#options = options;\n    this.#messagesKey = options.messagesKey ?? \"messages\";\n    this.#currentThreadId = options.threadId ?? null;\n    this.#rootBus = {\n      channels: ROOT_PUMP_CHANNELS,\n      subscribe: (listener) => {\n        this.#rootEventListeners.add(listener);\n        return () => {\n          this.#rootEventListeners.delete(listener);\n        };\n      },\n    };\n    this.registry = new ChannelRegistry(this.#rootBus);\n    this.subagentStore = this.#subagents.store;\n    this.subgraphStore = this.#subgraphs.store;\n    this.subgraphByNodeStore = this.#subgraphs.byNodeStore;\n    this.rootStore = new StreamStore<RootSnapshot<StateType, InterruptType>>(\n      this.#createInitialSnapshot()\n    );\n    this.#rootMessages = new RootMessageProjection({\n      messagesKey: this.#messagesKey,\n      store: this.rootStore,\n    });\n    this.#lifecycleLoading = new LifecycleLoadingTracker({\n      store: this.rootStore,\n      isDisposed: () => this.#disposed,\n    });\n    this.messageMetadataStore = this.#messageMetadata.store;\n    this.queueStore = new StreamStore<SubmissionQueueSnapshot<StateType>>(\n      EMPTY_QUEUE as SubmissionQueueSnapshot<StateType>\n    );\n    this.#submitter = new SubmitCoordinator({\n      options: this.#options,\n      rootStore: this.rootStore,\n      queueStore: this.queueStore,\n      getDisposed: () => this.#disposed,\n      getCurrentThreadId: () => this.#currentThreadId,\n      setCurrentThreadId: (threadId) => {\n        this.#currentThreadId = threadId;\n      },\n      rememberSelfCreatedThreadId: (threadId) => {\n        this.#selfCreatedThreadIds.add(threadId);\n      },\n      hydrate: (threadId) => this.hydrate(threadId),\n      ensureThread: (threadId, deferRootPump) =>\n        this.#ensureThread(threadId, deferRootPump),\n      startDeferredRootPump: () => this.#startDeferredRootPump(),\n      abandonDeferredRootPump: () => this.#abandonDeferredRootPump(),\n      forgetSelfCreatedThreadId: (threadId) => {\n        this.#selfCreatedThreadIds.delete(threadId);\n      },\n      waitForRootPumpReady: () => this.#rootPumpReady,\n      awaitNextTerminal: (signal) => this.#awaitNextTerminal(signal),\n      buildResumeRunInput: (resume) => {\n        const thread = this.#thread;\n        if (thread == null) return null;\n        return buildResumeRunInput(\n          resume,\n          thread.interrupts,\n          this.#resolvedInterrupts\n        );\n      },\n      markInterruptResolved: (interruptId) => {\n        this.#resolvedInterrupts.add(interruptId);\n      },\n      onSubmitStart: () => {\n        // Clear the hydrate-window allowlist so genuinely-new live\n        // interrupts on the just-started run aren't filtered. Bump\n        // the generation so any in-flight hydrate skips its\n        // allowlist write on return (see #hydratedActiveInterruptIds).\n        this.#hydratedActiveInterruptIds = null;\n        this.#submitGeneration += 1;\n      },\n      onRunStart: () => this.#markLocalRunStart(),\n      onRunCreated: (runId) => this.#notifyCreated(runId),\n      onRunCompleted: (reason, runId) => this.#notifyCompleted(reason, runId),\n      onRunEnd: () => this.#markLocalRunEnd(),\n    });\n    this.#hydrationPromise = this.#createHydrationPromise();\n    /**\n     * Attach a default no-op catch so orphaned hydrationPromise\n     * rejections (e.g. controllers spawned during Suspense retries\n     * whose promise never gets subscribed to because the suspense\n     * cache already holds an earlier one) don't surface as unhandled\n     * rejections. Callers that attach their own handlers via .then()\n     * still receive the rejection on their derived promise.\n     */\n    this.#hydrationPromise.catch(() => undefined);\n    /**\n     * Kick off the initial hydrate eagerly when a caller-supplied\n     * thread id is present. Suspense consumers throw\n     * `hydrationPromise` on the very first render, which unmounts the\n     * subtree before any `useEffect` can run — so if we waited for an\n     * effect to drive the hydrate we'd deadlock. Firing here makes\n     * the promise settle independently of the component lifecycle.\n     */\n    if (this.#currentThreadId != null) {\n      void this.hydrate(this.#currentThreadId);\n    } else {\n      this.#resolveHydration();\n    }\n  }\n\n  /**\n   * Promise that settles the first time {@link hydrate} finishes on\n   * the current thread. Resolves on a clean hydration, rejects when\n   * the thread-state fetch errors. A fresh promise is installed on\n   * every thread swap so `<Suspense>` wrappers re-suspend on\n   * `switchThread`.\n   */\n  get hydrationPromise(): Promise<void> {\n    return this.#hydrationPromise;\n  }\n\n  /**\n   * Create the deferred promise backing the current hydration cycle.\n   */\n  #createHydrationPromise(): Promise<void> {\n    return new Promise<void>((resolve, reject) => {\n      this.#resolveHydration = resolve;\n      this.#rejectHydration = reject;\n    });\n  }\n\n  /**\n   * Replace the current hydration promise before a thread swap.\n   */\n  #resetHydrationPromise(): void {\n    /**\n     * Swallow rejection on the orphaned promise so Node doesn't\n     * flag it as unhandled; Suspense callers that still hold a\n     * reference see the rejection they subscribed to.\n     */\n    this.#hydrationPromise.catch(() => undefined);\n    this.#hydrationPromise = this.#createHydrationPromise();\n  }\n\n  // ---------- public imperatives ----------\n\n  /**\n   * Fetch the checkpointed thread state and seed the root snapshot.\n   * Re-calling with a different `threadId` swaps the underlying\n   * {@link ThreadStream}, rewires the registry to the new thread, and\n   * resets assemblers.\n   *\n   * @param threadId - Optional replacement thread id; `null` clears the active thread.\n   */\n  async hydrate(threadId?: string | null): Promise<void> {\n    if (this.#disposed) return;\n    const target = threadId === undefined ? this.#currentThreadId : threadId;\n    const changed = target !== this.#currentThreadId;\n    this.#currentThreadId = target ?? null;\n    this.rootStore.setState((s) => ({ ...s, threadId: this.#currentThreadId }));\n\n    if (changed) {\n      /**\n       * Swap to a new thread: re-arm the hydration promise so any\n       * Suspense boundary remounted against the new id suspends again.\n       */\n      this.#resetHydrationPromise();\n      await this.#teardownThread();\n      /**\n       * Reset UI-facing snapshot so stale messages/values/tool-calls\n       * from the previous thread don't bleed into the new one. The\n       * new thread's state (if any) is then populated below via\n       * `#applyValues`.\n       */\n      this.rootStore.setState(() => ({\n        ...this.#createInitialSnapshot(),\n        threadId: this.#currentThreadId,\n      }));\n      /**\n       * Drop queued submissions — they were targeted at the previous\n       * thread so dispatching them against the new thread would be\n       * surprising. Mirrors the legacy `StreamOrchestrator` behaviour.\n       */\n      this.queueStore.setState(\n        () => EMPTY_QUEUE as SubmissionQueueSnapshot<StateType>\n      );\n    }\n\n    if (this.#currentThreadId == null) {\n      this.rootStore.setState((s) => ({ ...s, isThreadLoading: false }));\n      this.#resolveHydration();\n      return;\n    }\n\n    /**\n     * Self-generated thread ids have nothing to fetch server-side yet\n     * — the thread is created lazily by the first `run.start`. Calling\n     * `threads.getState()` here would return a 404 and surface a\n     * spurious error to the UI.\n     */\n    if (this.#selfCreatedThreadIds.has(this.#currentThreadId)) {\n      this.rootStore.setState((s) => ({ ...s, isThreadLoading: false }));\n      this.#resolveHydration();\n      return;\n    }\n\n    this.rootStore.setState((s) => ({ ...s, isThreadLoading: true }));\n    let hydrationError: unknown;\n    let threadExists = false;\n    try {\n      const state = await this.#options.client.threads.getState<StateType>(\n        this.#currentThreadId\n      );\n      threadExists = state != null;\n      if (state?.values != null) {\n        /**\n         * `threads.getState()` returns the legacy `ThreadState` shape\n         * where `parent_checkpoint` is an object (`{ thread_id,\n         * checkpoint_id, checkpoint_ns }`). Synthesize the v2\n         * `Checkpoint` envelope (matching the `checkpoints` channel\n         * payload) so hydrated messages also get their\n         * `parentCheckpointId` populated for fork / edit flows.\n         */\n        const checkpointId = state.checkpoint?.checkpoint_id;\n        const parentCheckpointId =\n          state.parent_checkpoint?.checkpoint_id ?? undefined;\n        const syntheticCheckpoint =\n          typeof checkpointId === \"string\"\n            ? {\n                id: checkpointId,\n                ...(parentCheckpointId != null\n                  ? { parent_id: parentCheckpointId }\n                  : {}),\n              }\n            : undefined;\n        this.#applyValues(state.values as unknown, syntheticCheckpoint);\n      }\n      /**\n       * Sync the visible interrupt list to the server's authoritative\n       * `state.tasks[].interrupts`. Without this, SSE replay of past\n       * `input.requested` events would re-add resolved interrupts to\n       * the UI on every page navigation back to the thread.\n       *\n       * Only runs when `state.tasks` is an array — runtimes that don't\n       * surface tasks in `threads.getState()` are left untouched (an\n       * unconditional overwrite would wipe any in-flight interrupt\n       * state the wildcard watcher may already have recorded).\n       */\n      if (Array.isArray(state?.tasks)) {\n        const generationAtFetch = this.#submitGeneration;\n        const activeInterrupts: Interrupt<InterruptType>[] = [];\n        const activeIds = new Set<string>();\n        for (const task of state.tasks) {\n          if (!Array.isArray(task?.interrupts)) continue;\n          for (const interrupt of task.interrupts) {\n            const typed = interrupt as\n              | { id?: string; value?: unknown }\n              | null\n              | undefined;\n            const id = typed?.id;\n            if (typeof id !== \"string\" || activeIds.has(id)) continue;\n            activeIds.add(id);\n            activeInterrupts.push({\n              id,\n              value: typed?.value as InterruptType,\n            });\n          }\n        }\n        this.rootStore.setState((s) => ({\n          ...s,\n          interrupts: activeInterrupts,\n          interrupt: activeInterrupts[0],\n        }));\n        // Only seed the allowlist when no submit started while the\n        // state fetch was in flight. If one did, the cleared\n        // (null) allowlist must stay null so the new run's live\n        // interrupts are not filtered.\n        if (this.#submitGeneration === generationAtFetch) {\n          this.#hydratedActiveInterruptIds = activeIds;\n        }\n      }\n    } catch (error) {\n      /**\n       * A 404 on hydrate means the thread does not exist server-side\n       * yet (most commonly because the caller supplied a brand-new,\n       * externally-minted thread id and is about to create it via the\n       * first `submit()`). Treat it as \"empty state\" rather than a\n       * fatal hydration error so Suspense boundaries resolve cleanly\n       * and no spurious error renders in the UI.\n       */\n      const status = (error as { status?: number } | null)?.status;\n      if (status !== 404) {\n        hydrationError = error;\n        this.rootStore.setState((s) => ({ ...s, error }));\n      }\n    } finally {\n      this.rootStore.setState((s) => ({ ...s, isThreadLoading: false }));\n      if (hydrationError != null) {\n        this.#rejectHydration(hydrationError);\n      } else {\n        this.#resolveHydration();\n      }\n    }\n\n    /**\n     * P0 fix: open the shared subscription on mount so in-flight\n     * server-side runs are observed even when no local `submit()` is\n     * active. The transport replays the run from `seq=0` on a rotating\n     * subscribe, so late-joining is free once the subscription exists.\n     * `isLoading` transitions are driven by the persistent root\n     * lifecycle listener registered in `#startRootPump`.\n     */\n    const thread = this.#ensureThread(this.#currentThreadId);\n\n    /**\n     * Start the wildcard lifecycle watcher up-front for existing\n     * threads. The root content pump runs at `depth: 1`, which covers\n     * root-namespace and one-deep events but not arbitrarily-nested\n     * subagent / subgraph lifecycle — the dedicated watcher handles\n     * those.\n     *\n     * For self-created (new) threads we skip — the watcher would 404\n     * against a not-yet-existent thread. `submitRun` / `respondInput`\n     * call `startLifecycleWatcher` on first submission to cover that\n     * case.\n     */\n    if (threadExists) {\n      thread.startLifecycleWatcher();\n    }\n  }\n\n  /**\n   * Submit input or a resume command to the active thread.\n   *\n   * @param input - Input payload for a new run; `null`/`undefined` submits no input.\n   * @param options - Per-run config, metadata, multitask behavior, and callbacks.\n   */\n  async submit(\n    input: unknown,\n    options?: StreamSubmitOptions<StateType, ConfigurableType>\n  ): Promise<void> {\n    await this.#submitter.submit(input, options);\n  }\n\n  /**\n   * Abort the currently tracked run and mark the controller idle.\n   */\n  async stop(): Promise<void> {\n    await this.#submitter.stop();\n  }\n\n  #markLocalRunStart(): void {\n    this.#localRunDepth += 1;\n  }\n\n  #markLocalRunEnd(): void {\n    this.#localRunDepth = Math.max(0, this.#localRunDepth - 1);\n  }\n\n  #notifyCreated(runId: string): void {\n    this.#activeRunId = runId;\n    try {\n      this.#options.onCreated?.({ runId });\n    } catch {\n      /* caller-supplied callback errors must not crash the stream */\n    }\n  }\n\n  #notifyCompleted(\n    reason: RunExecutionReason,\n    runId = this.#activeRunId\n  ): void {\n    if (runId != null && runId === this.#activeRunId) {\n      this.#activeRunId = undefined;\n    }\n    setTimeout(() => {\n      if (this.#disposed) return;\n      try {\n        this.#options.onCompleted?.(\n          runId == null ? { reason } : { runId, reason }\n        );\n      } catch {\n        /* caller-supplied callback errors must not crash the stream */\n      }\n    }, 0);\n  }\n\n  readonly #runLifecycleListener = (event: Event): void => {\n    if (this.#localRunDepth > 0) return;\n    if (event.method !== \"lifecycle\") return;\n    if (!isRootNamespace(event.params.namespace)) return;\n    if (!this.rootStore.getSnapshot().isLoading) return;\n    const lifecycle = (event as LifecycleEvent).params.data as {\n      event?: string;\n    };\n    const reason = lifecycleReason(lifecycle?.event);\n    if (reason == null) return;\n    this.#notifyCompleted(reason);\n  };\n\n  /**\n   * Cancel a queued submission by id. Returns `true` when the entry\n   * was found and removed, `false` otherwise.\n   *\n   * Today this only removes the entry from the client-side mirror —\n   * once the server exposes queue cancel (roadmap A0.3) the\n   * controller will additionally issue a cancel call against the\n   * active transport.\n   *\n   * @param id - Client-side queue entry id to remove.\n   */\n  async cancelQueued(id: string): Promise<boolean> {\n    return this.#submitter.cancelQueued(id);\n  }\n\n  /**\n   * Drop every queued submission. Server-side cancel arrives with A0.3.\n   */\n  async clearQueue(): Promise<void> {\n    await this.#submitter.clearQueue();\n  }\n\n  /**\n   * Respond to a pending protocol interrupt.\n   *\n   * @param response - Payload to send back to the interrupted namespace.\n   * @param target - Optional explicit interrupt id and namespace; defaults to the latest unresolved interrupt.\n   */\n  async respond(\n    response: unknown,\n    target?: { interruptId: string; namespace?: string[] }\n  ): Promise<void> {\n    if (this.#disposed || this.#thread == null) {\n      throw new Error(\"No active thread to respond to.\");\n    }\n    const resolved =\n      target != null\n        ? {\n            interruptId: target.interruptId,\n            namespace: target.namespace ?? [...ROOT_NAMESPACE],\n          }\n        : this.#resolveInterruptForResume();\n    if (resolved == null) {\n      throw new Error(\"No pending interrupt to respond to.\");\n    }\n    try {\n      await this.#thread.respondInput({\n        namespace: resolved.namespace,\n        interrupt_id: resolved.interruptId,\n        response,\n      });\n      this.#resolvedInterrupts.add(resolved.interruptId);\n    } catch (error) {\n      if (this.#disposed && isAbortLikeError(error)) {\n        return;\n      }\n      throw error;\n    }\n  }\n\n  /**\n   * Dispose the active thread, subscriptions, registry entries, and listeners.\n   */\n  async dispose(): Promise<void> {\n    if (this.#disposed) return;\n    this.#cancelPendingDispose();\n    this.#disposed = true;\n    this.#submitter.abortActiveRun();\n    await this.#teardownThread();\n    await this.registry.dispose();\n    this.#threadListeners.clear();\n  }\n\n  /**\n   * StrictMode-safe lifecycle hook for framework bindings.\n   *\n   * React 18+ `StrictMode` intentionally mounts → unmounts → remounts\n   * components in dev to surface effect-cleanup bugs. A naive\n   * `useEffect(() => () => controller.dispose())` would permanently\n   * tear the controller down on that first synthetic unmount, leaving\n   * every subsequent `submit()` a silent no-op.\n   *\n   * Call {@link activate} from the bind site's effect and return the\n   * result as the effect's cleanup. The controller uses deferred\n   * disposal: a `release()` only schedules a dispose on the next\n   * microtask, which is cancelled if another `activate()` arrives\n   * before it fires (the normal StrictMode remount path).\n   */\n  activate(): () => void {\n    this.#cancelPendingDispose();\n    return () => {\n      if (this.#disposed) return;\n      this.#pendingDisposeTimer = setTimeout(() => {\n        this.#pendingDisposeTimer = null;\n        void this.dispose().catch(() => undefined);\n      }, 0);\n    };\n  }\n\n  /**\n   * Cancel a deferred dispose scheduled by {@link activate}.\n   */\n  #cancelPendingDispose(): void {\n    if (this.#pendingDisposeTimer != null) {\n      clearTimeout(this.#pendingDisposeTimer);\n      this.#pendingDisposeTimer = null;\n    }\n  }\n\n  // ---------- thread access ----------\n\n  /**\n   * Returns the bound {@link ThreadStream}, if one exists. Prefer\n   * {@link StreamController.rootStore} and selector projections for\n   * UI work; use this for low-level protocol access.\n   */\n  getThread(): ThreadStream | undefined {\n    return this.#thread;\n  }\n\n  /**\n   * Listen for `ThreadStream` lifecycle (swap on thread-id change,\n   * detach on dispose). The listener fires immediately with the\n   * current thread (may be `undefined`).\n   *\n   * @param listener - Callback invoked immediately and on every thread swap.\n   */\n  subscribeThread(\n    listener: (thread: ThreadStream | undefined) => void\n  ): () => void {\n    this.#threadListeners.add(listener);\n    listener(this.#thread);\n    return () => {\n      this.#threadListeners.delete(listener);\n    };\n  }\n\n  // ---------- internals ----------\n\n  /**\n   * Build the initial root snapshot from configured initial values.\n   */\n  #createInitialSnapshot(): RootSnapshot<StateType, InterruptType> {\n    const values = (this.#options.initialValues ??\n      ({} as StateType)) as StateType;\n    const messages = extractAndCoerceMessages(\n      values as unknown as Record<string, unknown>,\n      this.#messagesKey\n    );\n    /**\n     * Seed `isThreadLoading: true` synchronously when a caller-supplied\n     * threadId is on the controller at construction/swap time. Without\n     * this Suspense wrappers would render their fallback for a tick\n     * because `isThreadLoading` flips false → true → false once the\n     * deferred `hydrate()` starts, and the synchronous render observes\n     * the initial `false`.\n     */\n    const willHydrate =\n      this.#currentThreadId != null &&\n      !this.#selfCreatedThreadIds.has(this.#currentThreadId);\n    return {\n      values,\n      messages,\n      toolCalls: [],\n      interrupts: [],\n      interrupt: undefined,\n      isLoading: false,\n      isThreadLoading: willHydrate,\n      error: undefined,\n      threadId: this.#currentThreadId,\n    };\n  }\n\n  /**\n   * Return the active thread stream, creating and binding one when needed.\n   *\n   * @param threadId - Thread id used when constructing the stream.\n   * @param deferRootPump - When `true`, build the ThreadStream and bind\n   *   the registry but skip starting the persistent root SSE pump. Used\n   *   for client-self-created thread ids whose server-side thread row\n   *   doesn't exist yet — opening the pump's `subscription.subscribe`\n   *   against a not-yet-existent thread produces a `404: Thread not\n   *   found` protocol error that strands terminal lifecycle events and\n   *   leaves the UI showing nothing until the user reloads. The pump is\n   *   started later via {@link #startDeferredRootPump} after `submitRun`\n   *   / `respondInput` commits the thread server-side.\n   *\n   *   Note: PR 2381's `#runStartReady` gate covers the analogous race\n   *   for the in-flight `run.start` send, but only when that send is\n   *   already pending. `#ensureThread` runs *before* `submitRun` is\n   *   called (and thus before the gate is armed), so on transports\n   *   that subscribe synchronously (WebSocket) the deferred path is\n   *   still required.\n   */\n  #ensureThread(threadId: string, deferRootPump = false): ThreadStream {\n    if (this.#thread != null) return this.#thread;\n    this.#thread = this.#options.client.threads.stream(threadId, {\n      assistantId: this.#options.assistantId,\n      transport: this.#options.transport,\n      fetch: this.#options.fetch,\n      webSocketFactory: this.#options.webSocketFactory,\n    });\n    this.registry.bind(this.#thread);\n    if (deferRootPump) {\n      // Resolve `#rootPumpReady` immediately so `submit()`'s `await\n      // this.#rootPumpReady` doesn't block — the dispatch path only\n      // needs the ThreadStream wired up to call `submitRun`, not the\n      // persistent subscription.\n      this.#rootPumpReady = Promise.resolve();\n      this.#rootPumpDeferred = true;\n    } else {\n      this.#startRootPump(this.#thread);\n    }\n    this.#notifyThreadListeners();\n    return this.#thread;\n  }\n\n  /**\n   * Start the previously-deferred root SSE pump after the first\n   * `submitRun` / `respondInput` has committed the thread server-side.\n   *\n   * No-op when the pump was started eagerly in {@link #ensureThread}\n   * (i.e. for hydrated existing threads, or for any thread whose pump\n   * has already been brought up).\n   */\n  #startDeferredRootPump(): void {\n    if (!this.#rootPumpDeferred) return;\n    if (this.#thread == null) return;\n    this.#rootPumpDeferred = false;\n    this.#startRootPump(this.#thread);\n  }\n\n  /**\n   * Abandon a deferred root pump that never started because its\n   * triggering dispatch (`submitRun` / `respondInput`) failed.\n   *\n   * Without this, the controller would be wedged in a state where:\n   *   - `#thread` is wired but no content pump is open\n   *   - `#rootPumpDeferred` stays `true`\n   *   - `selfCreatedThreadIds` still holds the id\n   *\n   * A retry submit on the same controller would see\n   * `wasSelfCreated=false` (because `currentThreadId` is no longer\n   * null), `#ensureThread(id, false)` would early-return because\n   * `#thread != null`, and the pump would never start. The thread\n   * would have an id committed to the URL but no live subscription.\n   *\n   * Tearing down `#thread` so the next submit re-runs `#ensureThread`\n   * from scratch is the simplest recovery — the failed dispatch\n   * means there was nothing to subscribe to anyway.\n   */\n  #abandonDeferredRootPump(): void {\n    if (!this.#rootPumpDeferred) return;\n    this.#rootPumpDeferred = false;\n    void this.#teardownThread();\n  }\n\n  /**\n   * Close the current thread stream and reset per-thread assembly state.\n   */\n  async #teardownThread(): Promise<void> {\n    const thread = this.#thread;\n    this.#thread = undefined;\n    this.registry.bind(undefined);\n    this.#threadEventUnsubscribe?.();\n    this.#threadEventUnsubscribe = undefined;\n    /**\n     * Persistent lifecycle driver is scoped to the current thread\n     * stream. Remove it so a swap to a new thread starts with a clean\n     * listener set (a new one is installed in `#startRootPump`).\n     */\n    this.#rootEventListeners.delete(this.#lifecycleLoading.listener);\n    this.#rootEventListeners.delete(this.#runLifecycleListener);\n    try {\n      await this.#rootSubscription?.unsubscribe();\n    } catch {\n      /* already closed */\n    }\n    this.#rootSubscription = undefined;\n    this.#rootPumpReady = undefined;\n    // Reset so a swap to a new thread doesn't carry over a stale\n    // deferred flag — `#ensureThread` will set it again if the new\n    // thread is self-created.\n    this.#rootPumpDeferred = false;\n    try {\n      await this.#rootPump;\n    } catch {\n      /* ignore */\n    }\n    this.#rootPump = undefined;\n\n    // Reset per-thread assembly state.\n    this.#rootMessages.reset();\n    this.#rootToolAssembler = new ToolCallAssembler();\n    this.#lifecycleLoading.reset();\n    this.#subagents.reset();\n    this.#subgraphs.reset();\n    this.#activeRunId = undefined;\n    this.#localRunDepth = 0;\n    this.#messageMetadata.reset();\n    // Drop the hydrate-window allowlist — the next thread's hydrate\n    // will repopulate it from that thread's `state.tasks[].interrupts`.\n    this.#hydratedActiveInterruptIds = null;\n    this.queueStore.setState(\n      () => EMPTY_QUEUE as SubmissionQueueSnapshot<StateType>\n    );\n\n    if (thread != null) {\n      try {\n        await thread.close();\n      } catch {\n        /* already closed */\n      }\n      this.#notifyThreadListeners();\n    }\n  }\n\n  /**\n   * Determine whether the configured transport uses the resumable event-stream path.\n   */\n  #usesEventStreamTransport(): boolean {\n    const transport = this.#options.transport;\n    if (transport === \"websocket\") return false;\n    if (transport == null || transport === \"sse\") return true;\n    return typeof transport.openEventStream === \"function\";\n  }\n\n  /**\n   * Start the always-on root subscription pump for the provided thread.\n   *\n   * @param thread - Thread stream to subscribe to and fan out from.\n   */\n  #startRootPump(thread: ThreadStream): void {\n    if (this.#rootPump != null) return;\n    let resolveReady: (() => void) | undefined;\n    this.#rootPumpReady = new Promise<void>((resolve) => {\n      resolveReady = resolve;\n    });\n\n    /**\n     * Wildcard discovery + interrupt tracking is delivered via the\n     * thread's dedicated lifecycle watcher (see `ThreadStream.onEvent`).\n     * This callback fires once per globally-unique event across both\n     * the content pump AND the watcher, so we can drive discovery\n     * runners and nested HITL capture without widening the content\n     * pump's narrow filter.\n     */\n    this.#threadEventUnsubscribe = thread.onEvent((event) =>\n      this.#onWildcardEvent(event)\n    );\n\n    /**\n     * Persistent isLoading driver. Drives `isLoading` from\n     * root-namespace lifecycle events so that in-flight runs observed\n     * via `hydrate()` (not initiated by a local `submit()`) still flip\n     * the UI to loading. `running` → true; terminals → false. The\n     * optimistic `isLoading = true` inside `submit()` stays because\n     * that fires before any subscription event arrives.\n     */\n    this.#rootEventListeners.add(this.#lifecycleLoading.listener);\n    this.#rootEventListeners.add(this.#runLifecycleListener);\n\n    this.#rootPump = (async () => {\n      try {\n        /**\n         * Root content pump: depth 1 is required because the controller\n         * classifies tool events at namespace length ≤ 1 as root-level\n         * (see `#onWildcardEvent`'s `isRootLevelTool` check). The deep-\n         * agent `task` dispatcher fires `tools.tool-started` at\n         * `[\"tools:<id>\"]` (length 1), so a depth-0 filter would drop\n         * those events server-side before they reached `root.toolCalls`.\n         *\n         * Deeper content (subagent message tokens, values snapshots) is\n         * pulled in on demand by per-namespace selector projections\n         * (e.g. `useMessages(sub)`).\n         */\n        const subscriptionPromise = thread.subscribe({\n          channels: [...ROOT_PUMP_CHANNELS] as Channel[],\n          namespaces: [[] as string[]],\n          depth: 1,\n        });\n        if (this.#usesEventStreamTransport()) {\n          /**\n           * SSE streams can legitimately withhold response headers until\n           * the first event is available. Waiting for `subscribe()` here\n           * would deadlock new-thread submits: the run is not dispatched\n           * until the root pump is \"ready\", but the pump does not become\n           * ready until the run emits. `thread.subscribe()` has already\n           * registered the local subscription and scheduled the stream\n           * rotation by this point; waiting one microtask lets the fetch\n           * get kicked off without requiring headers to arrive.\n           */\n          queueMicrotask(() => {\n            resolveReady?.();\n            resolveReady = undefined;\n          });\n        }\n        const subscription = await subscriptionPromise;\n        resolveReady?.();\n        resolveReady = undefined;\n        this.#rootSubscription = subscription;\n        /**\n         * The SSE transport pauses the underlying subscription when\n         * a terminal root lifecycle event arrives (so `for await`\n         * loops observing a single run exit cleanly) and re-opens\n         * the next run's server stream on `#prepareForNextRun`,\n         * resuming the subscription handle. The root pump needs to\n         * survive that hand-off: we re-enter the inner `for await`\n         * for every resumed iteration until the subscription is\n         * permanently closed or the controller is disposed.\n         */\n        while (!this.#disposed) {\n          for await (const event of subscription) {\n            if (this.#disposed) {\n              break;\n            }\n            /**\n             * Resilience: isolate per-event dispatch from the pump loop.\n             *\n             * `#onRootEvent` runs synchronously and, transitively,\n             * invokes every root-bus listener (selector projections that\n             * opted into the shared stream) plus every `rootStore`\n             * subscriber. Some of those subscribers live in a React\n             * render tree — `useStream` drives\n             * `useSyncExternalStore`, so a misbehaving component can\n             * surface a render-phase error (\"Maximum update depth\n             * exceeded\", \"The result of getSnapshot should be cached\",\n             * etc.) that propagates out here.\n             *\n             * Without this guard, a single throw bubbles through the\n             * `for await` loop and terminates the root pump permanently.\n             * That is catastrophic: no more root events get processed —\n             * the terminal `lifecycle: completed` never lands, so\n             * `#awaitNextTerminal` never resolves, `isLoading` stays\n             * `true`, composers stay disabled, and the final assistant\n             * turn never commits to `stream.messages`. The UI looks\n             * hung even though the server is still emitting events\n             * (and `ThreadStream.onEvent` keeps firing).\n             *\n             * We therefore swallow the error and keep pumping. The\n             * the pump's correctness guarantees do not depend on any\n             * consumer behaving well.\n             */\n            try {\n              this.#onRootEvent(event);\n            } catch {\n              /**\n               * Best-effort — a consumer-facing store subscriber should not\n               * terminate the root pump. Store mutations happen before\n               * listeners are notified, so continuing with later events keeps\n               * the controller's authoritative state moving forward.\n               */\n            }\n          }\n          if (this.#disposed) break;\n          if (!subscription.isPaused) {\n            break;\n          }\n          await subscription.waitForResume();\n        }\n      } catch {\n        resolveReady?.();\n        resolveReady = undefined;\n        /* thread closed or errored */\n      }\n    })();\n  }\n\n  /**\n   * Handle an event delivered via {@link ThreadStream.onEvent}.\n   *\n   * `onEvent` fires once per globally-unique event across the content\n   * pump and the wildcard lifecycle watcher, so this is the single\n   * entry point for wildcard discovery / interrupt tracking. It does\n   * NOT fan events out to the root bus (that's driven by the content\n   * pump iterator so root-bus short-circuits stay depth-1 scoped) and\n   * it does NOT process root content — messages/tools/values at root\n   * are handled by `#onRootEvent` off the content pump.\n   *\n   * @param event - Raw protocol event observed by the thread-wide listener.\n   */\n  #onWildcardEvent(event: Event): void {\n    try {\n      this.#subagents.push(event);\n    } catch {\n      /**\n       * Discovery store subscribers are user/UI code. If one throws, still\n       * let the wildcard watcher update subgraphs, loading, and interrupts.\n       */\n    }\n    this.#subgraphs.push(event);\n    this.#lifecycleLoading.handle(event);\n\n    /**\n     * Nested `input.requested` events (HITL inside a subagent /\n     * subgraph) are not observable via the narrow content pump. The\n     * `ThreadStream` itself already records them into\n     * `thread.interrupts`, which `#latestUnresolvedInterrupt()`\n     * consults — so HITL respond() works for any depth. Root-level\n     * interrupts are also mirrored into `rootStore.interrupts` here so\n     * UI state does not depend on the narrower content pump being the\n     * first consumer to see the event.\n     */\n    this.#recordRootInterrupt(event);\n  }\n\n  /**\n   * Process one root-pump event and update all root projections.\n   *\n   * @param event - Event yielded by the root subscription.\n   */\n  #onRootEvent(event: Event): void {\n    try {\n      this.#subagents.push(event);\n    } catch {\n      /**\n       * Discovery store subscribers are user/UI code. If one throws, still\n       * process the root event below so orchestrator messages and terminal\n       * state continue to advance.\n       */\n    }\n\n    /**\n     * Fan root-pump events out to every root-bus listener (selector\n     * projections that opted into the shared stream,\n     * `#awaitTerminal`, etc.). The root bus mirrors the content\n     * pump's narrow scope (depth 1 at root) so projections that\n     * short-circuit via the bus stay bounded.\n     */\n    if (this.#rootEventListeners.size > 0) {\n      for (const listener of this.#rootEventListeners) {\n        try {\n          listener(event);\n        } catch {\n          /**\n           * Best-effort — a bad listener should not wedge other\n           * projections or the root pump itself.\n           */\n        }\n      }\n    }\n\n    /**\n     * `messages` and `tools` events are emitted under a node's\n     * namespace — for a typical StateGraph the LLM's token deltas\n     * land on `[\"model:<uuid>\"]`, tool executions on\n     * `[\"tools:<uuid>\"]`, etc. The orchestrator's own turns (root\n     * agent, or an orchestrator-scoped subgraph like `model:*` /\n     * `model_request:*`) belong in `root.messages` and\n     * `root.toolCalls`.\n     *\n     * Subagent / tool-internal branches do NOT:\n     *   - `task:*` segment — legacy subagent convention.\n     *   - `tools:*` segment — every tool execution is wrapped in a\n     *     `tools` subgraph. For simple tools its only content is\n     *     the eventual tool result (also echoed verbatim by\n     *     `values.messages` so we don't lose anything). For the\n     *     deep-agent `task` tool its content IS the spawned\n     *     subagent's full message + tool stream, which is surfaced\n     *     separately via `useMessages(stream, subagent)` /\n     *     `useToolCalls(stream, subagent)`.\n     *\n     * We therefore drop `messages` events from any namespace that\n     * contains a `task:*` or `tools:*` segment; the authoritative\n     * tool-result text lands in `root.messages` via the root\n     * `values.messages` snapshot merge in `#applyValues`.\n     */\n    const isInternalNamespace = isInternalWorkNamespace(event.params.namespace);\n    const hasLegacySubagentNamespace = isLegacySubagentNamespace(\n      event.params.namespace\n    );\n\n    if (event.method === \"messages\") {\n      if (!isInternalNamespace) {\n        this.#rootMessages.handleMessage(event as MessagesEvent);\n      }\n      return;\n    }\n\n    if (event.method === \"tools\") {\n      /**\n       * Root-level tool events (both for simple orchestrator tools\n       * and the deep-agent `task` dispatcher) fire at a\n       * single-segment `[\"tools:<id>\"]` namespace. Anything deeper\n       * (e.g. `[tools:<outer>, tools:<inner>]`) is a subagent's own\n       * tool call and belongs to that subagent's `useToolCalls`\n       * view, not the orchestrator's `root.toolCalls`.\n       */\n      const isRootLevelTool =\n        event.params.namespace.length <= 1 && !hasLegacySubagentNamespace;\n      if (isRootLevelTool) {\n        /**\n         * Record the `namespace → tool_call_id` association so that\n         * the ensuing `message-start` (role: \"tool\") at the same\n         * namespace can recover the `tool_call_id` (the `messages`\n         * channel's start event doesn't carry it directly).\n         */\n        const toolData = event.params.data as {\n          event?: string;\n          tool_call_id?: string;\n        };\n        if (\n          toolData.event === \"tool-started\" &&\n          typeof toolData.tool_call_id === \"string\"\n        ) {\n          this.#rootMessages.recordToolCallNamespace(\n            event.params.namespace,\n            toolData.tool_call_id\n          );\n        }\n        const tc = this.#rootToolAssembler.consume(event as ToolsEvent);\n        if (tc != null) {\n          this.rootStore.setState((s) => ({\n            ...s,\n            toolCalls: upsertToolCall(s.toolCalls, tc),\n          }));\n        }\n      }\n      return;\n    }\n\n    /**\n     * The `checkpoints` channel carries the lightweight envelope\n     * (`id`, `parent_id`, `step`, `source`) emitted immediately\n     * before its companion `values` event on the same superstep.\n     * Buffer the envelope per-namespace so the ensuing `values`\n     * event at the same namespace can pair with it in `#applyValues`.\n     * The buffer is read-and-cleared on consumption so a subsequent\n     * `values` event without a new checkpoint doesn't reuse stale\n     * metadata.\n     */\n    if (event.method === \"checkpoints\") {\n      const data = event.params.data as {\n        id?: unknown;\n        parent_id?: unknown;\n      } | null;\n      this.#messageMetadata.bufferCheckpoint(event.params.namespace, data);\n      return;\n    }\n\n    // Channels below are only meaningful at the root namespace.\n    const isRoot = isRootNamespace(event.params.namespace);\n    if (!isRoot) return;\n\n    if (event.method === \"values\") {\n      const valuesEvent = event as ValuesEvent;\n      const bufferedCheckpoint = this.#messageMetadata.consumeCheckpoint(\n        event.params.namespace\n      );\n      this.#applyValues(valuesEvent.params.data, bufferedCheckpoint);\n      return;\n    }\n\n    if (event.method === \"input.requested\") {\n      this.#recordRootInterrupt(event);\n      return;\n    }\n\n    if (event.method === \"lifecycle\") {\n      /**\n       * Root lifecycle transitions are observed elsewhere\n       * (#awaitTerminal) to unblock `submit`.\n       */\n      const lifecycle = (event as LifecycleEvent).params.data as {\n        event?: string;\n      };\n      void lifecycle;\n    }\n  }\n\n  /**\n   * Merge a `values` payload into root values and root messages.\n   *\n   * @param raw - Raw `values` channel payload.\n   * @param checkpoint - Optional checkpoint envelope paired with the values event.\n   */\n  #applyValues(raw: unknown, checkpoint?: CheckpointEnvelope): void {\n    if (raw == null || typeof raw !== \"object\" || Array.isArray(raw)) {\n      return;\n    }\n    const state = raw as Record<string, unknown>;\n    /**\n     * Surface parent_checkpoint per-message when the values event\n     * carries the lightweight checkpoint envelope (populated by\n     * `@langchain/langgraph-core`'s `_emitValuesWithCheckpointMeta` and\n     * forwarded through `convertToProtocolEvent`). Consumers surface\n     * this as `useMessageMetadata(stream, msg.id).parentCheckpointId`\n     * for fork / edit flows.\n     */\n    const parentCheckpointId = checkpoint?.parent_id;\n    if (parentCheckpointId != null && Array.isArray(state[this.#messagesKey])) {\n      this.#messageMetadata.recordMessages(\n        state[this.#messagesKey] as Array<{ id?: string }>,\n        { parentCheckpointId }\n      );\n    }\n    const maybeMessages = state[this.#messagesKey];\n    let nextValues: StateType;\n    let nextMessages: BaseMessage[];\n    if (Array.isArray(maybeMessages)) {\n      const coerced = ensureMessageInstances(\n        maybeMessages as (Message | BaseMessage)[]\n      );\n      nextValues = {\n        ...(state as StateType),\n        [this.#messagesKey]: coerced,\n      } as StateType;\n      nextMessages = coerced;\n    } else {\n      nextValues = state as StateType;\n      nextMessages = [];\n    }\n    this.#rootMessages.applyValues(nextValues, nextMessages);\n    if (nextMessages.length > 0) {\n      this.rootStore.setState((s) => {\n        const toolCalls = reconcileToolCallsFromMessages(\n          s.toolCalls,\n          nextMessages\n        );\n        if (toolCalls === s.toolCalls) return s;\n        return { ...s, toolCalls };\n      });\n    }\n  }\n\n  /**\n   * Mirror root protocol interrupts into the root snapshot.\n   *\n   * This can be called from both the wildcard lifecycle/input watcher and the\n   * root content pump. Store-level dedup keeps the user-facing list stable.\n   */\n  #recordRootInterrupt(event: Event): void {\n    if (event.method !== \"input.requested\") return;\n    if (!isRootNamespace(event.params.namespace)) return;\n    const data = event.params.data as {\n      interrupt_id?: string;\n      payload?: unknown;\n    };\n    const interruptId = data?.interrupt_id;\n    if (\n      typeof interruptId !== \"string\" ||\n      this.#resolvedInterrupts.has(interruptId)\n    ) {\n      return;\n    }\n    // Strict allowlist when populated by the most-recent hydrate: SSE\n    // replay of `input.requested` carries no signal distinguishing\n    // historical (already-resolved) interrupts from live ones, so we\n    // accept only ids the server reported as currently active in\n    // `state.tasks[].interrupts`. `null` (outside the hydrate window\n    // / after a submit clears it) disables filtering entirely so new\n    // live interrupts on an active run pass through.\n    if (\n      this.#hydratedActiveInterruptIds != null &&\n      !this.#hydratedActiveInterruptIds.has(interruptId)\n    ) {\n      return;\n    }\n    const interrupt: Interrupt<InterruptType> = {\n      id: interruptId,\n      value: data.payload as InterruptType,\n    };\n    this.rootStore.setState((s) => {\n      if (s.interrupts.some((entry) => entry.id === interruptId)) return s;\n      const interrupts = [...s.interrupts, interrupt];\n      return { ...s, interrupts, interrupt: interrupts[0] };\n    });\n  }\n\n  /**\n   * Resolve on the next root-namespace terminal lifecycle event\n   * (`completed` / `failed` / `interrupted`) or on abort.\n   *\n   * Attaches to the controller's root event bus instead of opening\n   * a second server subscription. Callers should register the\n   * returned promise **before** dispatching the command that\n   * triggers the run (`thread.run.start` / `thread.input.respond`)\n   * — the root pump fans events out synchronously on arrival, so a\n   * late registration would miss the terminal for fast runs.\n   *\n   * @param signal - Abort signal for the local submit lifecycle.\n   */\n  #awaitNextTerminal(signal: AbortSignal): Promise<{\n    event: \"completed\" | \"failed\" | \"interrupted\" | \"aborted\";\n    error?: string;\n  }> {\n    return new Promise((resolve) => {\n      let settled = false;\n      function finish(result: {\n        event: \"completed\" | \"failed\" | \"interrupted\" | \"aborted\";\n        error?: string;\n      }) {\n        if (settled) return;\n        settled = true;\n        unsubscribeRoot?.();\n        unsubscribeThread?.();\n        signal.removeEventListener(\"abort\", finishAborted);\n        resolve(result);\n      }\n      const finishAborted = () => finish({ event: \"aborted\" });\n      const onEvent = (event: Event) => {\n        if (settled) return;\n        if (event.method !== \"lifecycle\") return;\n        if (!isRootNamespace(event.params.namespace)) return;\n        const lifecycle = (event as LifecycleEvent).params.data as {\n          event?: string;\n          error?: string;\n        };\n        if (lifecycle?.event === \"completed\") {\n          setTimeout(() => finish({ event: \"completed\" }), 0);\n        } else if (lifecycle?.event === \"failed\") {\n          setTimeout(\n            () => finish({ event: \"failed\", error: lifecycle.error }),\n            0\n          );\n        } else if (lifecycle?.event === \"interrupted\") {\n          setTimeout(() => finish({ event: \"interrupted\" }), 0);\n        }\n      };\n      const unsubscribeRoot = this.#rootBus.subscribe(onEvent);\n      const unsubscribeThread = this.#thread?.onEvent(onEvent);\n      if (signal.aborted) {\n        finishAborted();\n      } else {\n        signal.addEventListener(\"abort\", finishAborted, { once: true });\n      }\n    });\n  }\n\n  /**\n   * Resolve which protocol interrupt a resume command should target.\n   * Headless-tool resumes are keyed by tool-call id; without matching\n   * on that id, parallel tool handlers would respond to the wrong\n   * interrupt (always the newest).\n   */\n  #resolveInterruptForResume(resume?: unknown): ResolvedInterrupt | null {\n    const thread = this.#thread;\n    if (thread == null) return null;\n    return resolveInterruptTargetForHeadlessResume(\n      resume,\n      thread.interrupts,\n      this.#resolvedInterrupts\n    );\n  }\n\n  /**\n   * Notify listeners that the underlying thread stream changed.\n   */\n  #notifyThreadListeners(): void {\n    for (const listener of this.#threadListeners) listener(this.#thread);\n  }\n}\n\n// ---------- helpers ----------\n\n/**\n * Extract and coerce the configured messages key from a values object.\n *\n * @param values - State values object to read from.\n * @param messagesKey - Key that contains the message array.\n */\nfunction extractAndCoerceMessages(\n  values: Record<string, unknown>,\n  messagesKey: string\n): BaseMessage[] {\n  const raw = values[messagesKey];\n  if (!Array.isArray(raw)) return [];\n  return ensureMessageInstances(\n    raw as (Message | BaseMessage)[]\n  ) as BaseMessage[];\n}\n\n// Unused import guard — `AIMessage` is only referenced by type tests.\nvoid AIMessage;\n"],"mappings":"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"}