@langchain/langgraph-sdk 1.9.23 → 1.9.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/_virtual/_rolldown/runtime.cjs +2 -0
  2. package/dist/_virtual/_rolldown/runtime.js +24 -0
  3. package/dist/node_modules/.pnpm/eventemitter3@5.0.4/node_modules/eventemitter3/index.cjs +244 -0
  4. package/dist/node_modules/.pnpm/eventemitter3@5.0.4/node_modules/eventemitter3/index.cjs.map +1 -0
  5. package/dist/node_modules/.pnpm/eventemitter3@5.0.4/node_modules/eventemitter3/index.js +240 -0
  6. package/dist/node_modules/.pnpm/eventemitter3@5.0.4/node_modules/eventemitter3/index.js.map +1 -0
  7. package/dist/node_modules/.pnpm/eventemitter3@5.0.4/node_modules/eventemitter3/index2.cjs +13 -0
  8. package/dist/node_modules/.pnpm/eventemitter3@5.0.4/node_modules/eventemitter3/index2.cjs.map +1 -0
  9. package/dist/node_modules/.pnpm/eventemitter3@5.0.4/node_modules/eventemitter3/index2.js +8 -0
  10. package/dist/node_modules/.pnpm/eventemitter3@5.0.4/node_modules/eventemitter3/index2.js.map +1 -0
  11. package/dist/node_modules/.pnpm/is-network-error@1.3.1/node_modules/is-network-error/index.cjs +25 -0
  12. package/dist/node_modules/.pnpm/is-network-error@1.3.1/node_modules/is-network-error/index.cjs.map +1 -0
  13. package/dist/node_modules/.pnpm/is-network-error@1.3.1/node_modules/is-network-error/index.js +25 -0
  14. package/dist/node_modules/.pnpm/is-network-error@1.3.1/node_modules/is-network-error/index.js.map +1 -0
  15. package/dist/node_modules/.pnpm/p-queue@9.1.0/node_modules/p-queue/dist/index.cjs +596 -0
  16. package/dist/node_modules/.pnpm/p-queue@9.1.0/node_modules/p-queue/dist/index.cjs.map +1 -0
  17. package/dist/node_modules/.pnpm/p-queue@9.1.0/node_modules/p-queue/dist/index.js +596 -0
  18. package/dist/node_modules/.pnpm/p-queue@9.1.0/node_modules/p-queue/dist/index.js.map +1 -0
  19. package/dist/node_modules/.pnpm/p-queue@9.1.0/node_modules/p-queue/dist/lower-bound.cjs +18 -0
  20. package/dist/node_modules/.pnpm/p-queue@9.1.0/node_modules/p-queue/dist/lower-bound.cjs.map +1 -0
  21. package/dist/node_modules/.pnpm/p-queue@9.1.0/node_modules/p-queue/dist/lower-bound.js +18 -0
  22. package/dist/node_modules/.pnpm/p-queue@9.1.0/node_modules/p-queue/dist/lower-bound.js.map +1 -0
  23. package/dist/node_modules/.pnpm/p-queue@9.1.0/node_modules/p-queue/dist/priority-queue.cjs +41 -0
  24. package/dist/node_modules/.pnpm/p-queue@9.1.0/node_modules/p-queue/dist/priority-queue.cjs.map +1 -0
  25. package/dist/node_modules/.pnpm/p-queue@9.1.0/node_modules/p-queue/dist/priority-queue.js +41 -0
  26. package/dist/node_modules/.pnpm/p-queue@9.1.0/node_modules/p-queue/dist/priority-queue.js.map +1 -0
  27. package/dist/node_modules/.pnpm/p-retry@7.1.1/node_modules/p-retry/index.cjs +141 -0
  28. package/dist/node_modules/.pnpm/p-retry@7.1.1/node_modules/p-retry/index.cjs.map +1 -0
  29. package/dist/node_modules/.pnpm/p-retry@7.1.1/node_modules/p-retry/index.js +141 -0
  30. package/dist/node_modules/.pnpm/p-retry@7.1.1/node_modules/p-retry/index.js.map +1 -0
  31. package/dist/node_modules/.pnpm/p-timeout@7.0.1/node_modules/p-timeout/index.cjs +63 -0
  32. package/dist/node_modules/.pnpm/p-timeout@7.0.1/node_modules/p-timeout/index.cjs.map +1 -0
  33. package/dist/node_modules/.pnpm/p-timeout@7.0.1/node_modules/p-timeout/index.js +62 -0
  34. package/dist/node_modules/.pnpm/p-timeout@7.0.1/node_modules/p-timeout/index.js.map +1 -0
  35. package/dist/stream/controller.cjs +29 -3
  36. package/dist/stream/controller.cjs.map +1 -1
  37. package/dist/stream/controller.d.cts.map +1 -1
  38. package/dist/stream/controller.d.ts.map +1 -1
  39. package/dist/stream/controller.js +30 -4
  40. package/dist/stream/controller.js.map +1 -1
  41. package/dist/stream/optimistic-input.cjs +32 -0
  42. package/dist/stream/optimistic-input.cjs.map +1 -1
  43. package/dist/stream/optimistic-input.js +32 -1
  44. package/dist/stream/optimistic-input.js.map +1 -1
  45. package/dist/stream/root-message-projection.cjs +14 -2
  46. package/dist/stream/root-message-projection.cjs.map +1 -1
  47. package/dist/stream/root-message-projection.js +14 -2
  48. package/dist/stream/root-message-projection.js.map +1 -1
  49. package/dist/stream/submit-coordinator.cjs +14 -1
  50. package/dist/stream/submit-coordinator.cjs.map +1 -1
  51. package/dist/stream/submit-coordinator.js +14 -1
  52. package/dist/stream/submit-coordinator.js.map +1 -1
  53. package/dist/stream/types.d.cts +27 -1
  54. package/dist/stream/types.d.cts.map +1 -1
  55. package/dist/stream/types.d.ts +27 -1
  56. package/dist/stream/types.d.ts.map +1 -1
  57. package/dist/ui/types.d.cts +13 -1
  58. package/dist/ui/types.d.cts.map +1 -1
  59. package/dist/ui/types.d.ts +13 -1
  60. package/dist/ui/types.d.ts.map +1 -1
  61. package/dist/utils/async_caller.cjs +14 -8
  62. package/dist/utils/async_caller.cjs.map +1 -1
  63. package/dist/utils/async_caller.d.cts.map +1 -1
  64. package/dist/utils/async_caller.d.ts.map +1 -1
  65. package/dist/utils/async_caller.js +13 -4
  66. package/dist/utils/async_caller.js.map +1 -1
  67. package/package.json +7 -6
@@ -1 +1 @@
1
- {"version":3,"file":"controller.js","names":["#options","#messagesKey","#subagents","#subgraphs","#messageMetadata","#resolvedInterrupts","#selfCreatedThreadIds","#namespaceResolves","#scopedHistorySeeds","#rootEventListeners","#rootBus","#threadListeners","#currentThreadId","#trySeedProjectionFromHistory","#createInitialSnapshot","#rootMessages","#lifecycleLoading","#disposed","#submitter","#ensureThread","#startDeferredRootPump","#abandonDeferredRootPump","#rootPumpReady","#awaitNextTerminal","#awaitResumedRunTerminal","#hydratedActiveInterruptIds","#submitGeneration","#markLocalRunStart","#notifyCreated","#notifyCompleted","#markLocalRunEnd","#beginOptimistic","#settleOptimistic","#hydrationPromise","#createHydrationPromise","#resolveHydration","#rejectHydration","#fetchHydrationState","#discoverySeedPromise","#resetHydrationPromise","#teardownThread","#applyValues","#seedDiscoveryFromHistory","#primeScopedHistorySeedsFromHistory","#rootPumpDeferred","#skipDefaultSubagentProjection","#getScopedHistorySeed","#activeRunId","#localRunDepth","#runLifecycleListener","#thread","#resolveInterruptForResume","#markInterruptResolvedInRootStore","#cancelPendingDispose","#pendingDisposeTimer","#startRootPump","#notifyThreadListeners","#threadEventUnsubscribe","#rootSubscription","#rootPump","#rootToolAssembler","#onWildcardEvent","#usesEventStreamTransport","#onRootEvent","#recordRootInterrupt","uuidv7","#sawValuesForRun","#awaitRootTerminal"],"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 { v7 as uuidv7 } from \"@langchain/core/utils/uuid\";\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, ThreadState } 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 type { AssembledToolCall } from \"../client/stream/handles/tools.js\";\nimport { normalizeInterruptForClient } from \"../ui/interrupts.js\";\nimport { normalizeHitlResponseForServer } from \"../ui/hitl-interrupt-payload.js\";\nimport type { Message } from \"../types.messages.js\";\nimport { NAMESPACE_SEPARATOR } from \"./constants.js\";\nimport { StreamStore } from \"./store.js\";\nimport { ChannelRegistry } from \"./channel-registry.js\";\nimport { ensureMessageInstances } from \"./message-coercion.js\";\nimport {\n SubagentDiscovery,\n type SubagentMap,\n SubgraphDiscovery,\n type SubgraphMap,\n type SubgraphByNodeMap,\n} from \"./discovery/index.js\";\nimport {\n collectSubgraphHostNamespaces,\n getHistoryPage,\n mapSubagentNamespaces,\n resolveSubagentNamespaces,\n} from \"./discovery/namespace-from-history.js\";\nimport type { SubagentDiscoverySnapshot } from \"./types.js\";\nimport {\n isInternalWorkNamespace,\n isLegacySubagentNamespace,\n isRootNamespace,\n namespaceKey,\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 prepareOptimisticInput,\n type OptimisticHandle,\n} from \"./optimistic-input.js\";\nimport {\n EMPTY_QUEUE,\n SubmitCoordinator,\n type SubmissionQueueEntry,\n type SubmissionQueueSnapshot,\n} from \"./submit-coordinator.js\";\nimport {\n reconcileToolCallsFromMessages,\n seedToolCallsFromMessages,\n upsertToolCall,\n} from \"./tool-calls.js\";\nimport { resolveInterruptTargetForHeadlessResume } from \"../headless-tools.js\";\nimport type {\n RootEventBus,\n RootSnapshot,\n RunExecutionReason,\n StreamControllerOptions,\n StreamRespondAllOptions,\n StreamRespondOptions,\n StreamStopOptions,\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\ninterface ScopedHistorySeed {\n readonly messages: BaseMessage[];\n readonly toolCalls: AssembledToolCall[];\n}\n\n/**\n * Decide whether a hydrated thread is *active* (a run is executing or\n * paused awaiting resume) from the `getState()` snapshot alone — no\n * extra request.\n *\n * Why this gate exists: a finished thread does not need either of the\n * always-on SSE pumps. Subagent/subgraph cards are already seeded from\n * the `getState()` messages and a single bounded `getHistory()` page, so\n * opening the depth-1 content pump + the wildcard lifecycle watcher only\n * to replay a completed run and then idle forever is pure waste. We open\n * the pumps eagerly only when the thread is active; otherwise they come\n * up on the first local `submit()` (the existing deferred-pump path) or\n * a thread swap that lands on an active thread.\n *\n * The gate is deliberately conservative: we only conclude *idle* when\n * the state proves it. A thread is treated as active unless `next` is a\n * present, empty array AND no task carries a pending interrupt:\n * - `next` missing / not an array: unknown shape (a server or custom\n * client may omit it). Treat as active so an already-running\n * server-side run is still observed on reconnect — never silently\n * disable streaming on an unfamiliar `getState` shape.\n * - `next.length > 0`: the checkpoint still has nodes to execute, i.e.\n * a run is mid-flight or paused at an interrupt.\n * - `next` is `[]` but a `tasks[].interrupts` is non-empty: the thread\n * is interrupted and a resume (which starts a run) must be observable.\n * - `next` is `[]` and no pending interrupts: a completed run → idle.\n */\nfunction isThreadStateActive(\n state: { next?: unknown; tasks?: unknown } | null | undefined\n): boolean {\n if (state == null) return true;\n // Only a present, empty `next` array proves \"no nodes pending\". A\n // missing/non-array `next` is an unknown shape → assume active.\n if (!Array.isArray(state.next)) return true;\n if (state.next.length > 0) return true;\n if (Array.isArray(state.tasks)) {\n for (const task of state.tasks) {\n const interrupts = (task as { interrupts?: unknown } | null)?.interrupts;\n if (Array.isArray(interrupts) && interrupts.length > 0) return true;\n }\n }\n return false;\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 /**\n * In-flight per-subagent namespace resolutions, keyed by tool-call\n * id. De-dupes concurrent {@link resolveSubagentNamespace} calls so\n * re-renders / multiple consumers of the same subagent don't issue\n * parallel `getHistory` walks.\n */\n readonly #namespaceResolves = new Map<string, Promise<void>>();\n /**\n * In-flight hydrate-time discovery seed ({@link #seedDiscoveryFromHistory}):\n * a single bounded `getHistory` page that bulk-promotes every\n * still-default subagent namespace and seeds subgraph hosts. Per-card\n * {@link resolveSubagentNamespace} calls await this shared promise\n * instead of each firing their own `getHistory` walk, so opening N\n * cards right after reconnect costs one history read, not N. Re-armed\n * per hydrate cycle and cleared once it settles.\n */\n #discoverySeedPromise: Promise<void> | undefined;\n readonly #scopedHistorySeeds = new Map<\n string,\n Promise<ScopedHistorySeed | null>\n >();\n readonly #rootEventListeners = new Set<(event: Event) => void>();\n readonly #rootBus: RootEventBus;\n #activeRunId: string | undefined;\n #localRunDepth = 0;\n /**\n * `true` once a root `values` event has been applied for the current\n * optimistic run. Reset to `false` in {@link #beginOptimistic} and\n * read in {@link #settleOptimistic}: when a run terminates without\n * the server ever echoing a `values` snapshot, optimistically-merged\n * non-message keys are rolled back to their pre-submit values.\n */\n #sawValuesForRun = false;\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 trySeedFromHistory: (params) =>\n this.#trySeedProjectionFromHistory(params),\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 awaitResumedRunTerminal: (signal) =>\n this.#awaitResumedRunTerminal(signal),\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 beginOptimistic: (input) => this.#beginOptimistic(input),\n settleOptimistic: (handle, event) =>\n this.#settleOptimistic(handle, event),\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 * Load thread state for hydration, preferring the active custom\n * adapter's `getState()` when present.\n */\n async #fetchHydrationState(): Promise<ThreadState<StateType> | null> {\n const threadId = this.#currentThreadId;\n if (threadId == null) return null;\n\n const transport = this.#options.transport;\n if (\n transport != null &&\n typeof transport === \"object\" &&\n typeof transport.getState === \"function\"\n ) {\n return (await transport.getState<StateType>()) as ThreadState<StateType> | null;\n }\n\n return this.#options.client.threads.getState<StateType>(threadId);\n }\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 // Re-arm per hydrate cycle: a stale seed from a previous thread must\n // not be awaited by this thread's lazy namespace resolves.\n this.#discoverySeedPromise = undefined;\n this.#scopedHistorySeeds.clear();\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 // Default active so a getState error / non-404 failure never\n // silently disables streaming — the pumps open eagerly as before.\n // Flipped to the real signal once we have the state in hand.\n let threadActive = true;\n try {\n const state = await this.#fetchHydrationState();\n threadExists = state != null;\n threadActive = isThreadStateActive(state);\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 /**\n * Carry the checkpoint `step` from `getState()` metadata so the\n * root message projection treats this seed as the authoritative\n * latest superstep. The content pump's reconnect replay emits\n * older checkpoints (lower step); marking the seed's step lets\n * the projection reject those as stale instead of letting them\n * remove the seeded message tail (the final assistant turn).\n */\n const seedStep = (state.metadata as { step?: unknown } | undefined)\n ?.step;\n const syntheticCheckpoint =\n typeof checkpointId === \"string\"\n ? {\n id: checkpointId,\n ...(parentCheckpointId != null\n ? { parent_id: parentCheckpointId }\n : {}),\n ...(typeof seedStep === \"number\" ? { step: seedStep } : {}),\n }\n : undefined;\n this.#applyValues(state.values as unknown, syntheticCheckpoint);\n\n /**\n * Seed subagent discovery from checkpoint messages so deep-agent\n * cards render on refresh without waiting for SSE replay. Zero\n * extra HTTP (reuses the `getState` payload); mirrors the\n * interrupt-seeding below. `#subagents` was cleared in\n * `#teardownThread`, and `seedFromCheckpointMessages` is\n * idempotent, so this is safe on re-hydrate.\n */\n const seedMessages = (state.values as Record<string, unknown>)[\n this.#messagesKey\n ];\n if (Array.isArray(seedMessages)) {\n this.#subagents.seedFromCheckpointMessages(seedMessages);\n\n /**\n * An idle (finished) thread defers its root SSE pump; the first\n * `submit()` brings it up and the transport replays the finished\n * run from `seq=0`. Seal the seeded message ids so that replay's\n * `messages` channel deltas can't downgrade the already-complete\n * tail to empty partials (a visible \"messages replay\"). Only safe\n * for idle threads, where every seeded message is final — an\n * active thread's tail may still be streaming and must keep\n * receiving deltas. New ids from the next run are never sealed,\n * and the seal lifts once a newer checkpoint advances the\n * timeline.\n */\n if (!threadActive) {\n const sealedIds = (seedMessages as Array<{ id?: unknown }>)\n .map((message) => message?.id)\n .filter((id): id is string => typeof id === \"string\");\n if (sealedIds.length > 0) {\n this.#rootMessages.sealMessageIds(sealedIds);\n }\n }\n }\n }\n /**\n * Converge to server truth: drop any optimistic messages the\n * server state does not contain (`pending` / `failed` that were\n * never persisted — e.g. a failed run's user message). Echoed\n * ids were flipped to `\"sent\"` by `#applyValues` above and so are\n * excluded from `unpersistedOptimisticIds()`.\n */\n const unpersisted = this.#messageMetadata.unpersistedOptimisticIds();\n if (unpersisted.size > 0) {\n this.#rootMessages.dropOptimisticMessages(unpersisted);\n this.#messageMetadata.forget(unpersisted);\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 normalizeInterruptForClient({\n id,\n value: typed?.value as InterruptType,\n })\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 * Open the shared subscription on mount so in-flight server-side\n * runs are observed even when no local `submit()` is active — BUT\n * only when the thread is actually active (see\n * {@link isThreadStateActive}). A finished thread's cards are seeded\n * from `getState()` + the bounded `getHistory()` below, so opening\n * the depth-1 content pump just to replay a completed run and idle\n * forever is pure waste. When idle we take the deferred path: the\n * pump (and watcher) come up on the first local `submit()` via\n * {@link #startDeferredRootPump}, exactly like a self-created thread.\n * The transport replays from `seq=0` on the deferred subscribe, so\n * nothing is missed.\n */\n const thread = this.#ensureThread(this.#currentThreadId, !threadActive);\n\n /**\n * Start the wildcard lifecycle watcher up-front for existing,\n * active threads. The root content pump runs at `depth: 1`, which\n * covers root-namespace and one-deep events but not arbitrarily-\n * nested subagent / subgraph lifecycle — the dedicated watcher\n * handles those.\n *\n * Skipped when:\n * - the thread is idle/finished — there are no live events to\n * watch; discovery is seeded from history below, and the watcher\n * starts with the deferred pump on the first `submit()`.\n * - the thread is self-created (new) — the watcher would 404\n * against a not-yet-existent thread; `submitRun` / `respondInput`\n * call `startLifecycleWatcher` on first submission instead.\n */\n if (threadExists && threadActive) {\n thread.startLifecycleWatcher();\n }\n if (threadExists) {\n /**\n * Seed subgraph discovery and promote subagent execution\n * namespaces from a single bounded `getHistory` page. Subgraph\n * structure is not present in the root checkpoint messages\n * (unlike subagents), so it can only be reconstructed from\n * history. Fire-and-forget — not awaited into the hydration\n * promise, so Suspense / first paint stay unblocked; cards fill\n * in progressively when it resolves.\n *\n * Held in `#discoverySeedPromise` so lazy per-card\n * {@link resolveSubagentNamespace} calls coalesce onto this single\n * read instead of each firing their own `getHistory` walk.\n */\n const seed: Promise<void> = this.#seedDiscoveryFromHistory(\n this.#currentThreadId\n ).finally(() => {\n // Only clear if a later hydrate cycle hasn't re-armed it.\n if (this.#discoverySeedPromise === seed) {\n this.#discoverySeedPromise = undefined;\n }\n });\n this.#discoverySeedPromise = seed;\n }\n }\n\n /**\n * One bounded, non-blocking `getHistory` read at hydrate that seeds\n * subgraph hosts and bulk-promotes still-default subagent execution\n * namespaces. O(1) in requests regardless of subagent/subgraph count.\n */\n async #seedDiscoveryFromHistory(threadId: string): Promise<void> {\n try {\n const history = await getHistoryPage(this.#options.client, threadId, {\n limit: 20,\n });\n // A thread swap (or dispose) during the fetch invalidates this seed.\n if (this.#disposed || this.#currentThreadId !== threadId) return;\n\n this.#primeScopedHistorySeedsFromHistory(threadId, history);\n\n const hosts = collectSubgraphHostNamespaces(history);\n this.#subgraphs.seedFromHistory(hosts);\n\n const defaultOnlyIds = [...this.#subagents.snapshot.values()]\n .filter(namespaceIsDefaultOnly)\n .map((entry) => entry.id);\n if (defaultOnlyIds.length > 0) {\n const map = mapSubagentNamespaces(\n history,\n defaultOnlyIds,\n this.#messagesKey\n );\n for (const [id, segment] of map) {\n this.#subagents.applyExecutionNamespace(id, segment);\n }\n }\n } catch {\n /* non-fatal: SSE replay still reconciles discovery */\n }\n }\n\n /**\n * Lazily resolve a single subagent's execution namespace from\n * checkpoint history. Intended call site: the first scoped\n * `useMessages` / `useToolCalls` mount for a subagent whose namespace\n * is still the default `tools:<toolCallId>`. A fallback for the\n * hydrate-time bulk seed ({@link #seedDiscoveryFromHistory}) — most\n * subagents are already promoted by the time a panel opens.\n *\n * Skips ids already promoted past default-only (SSE replay or a prior\n * resolve). Concurrent calls for the same id share one `getHistory`\n * walk via {@link #namespaceResolves}.\n *\n * @param toolCallId - Parent `task` tool-call id (the subagent's discovery key).\n */\n async resolveSubagentNamespace(toolCallId: string): Promise<void> {\n if (this.#disposed) return;\n const threadId = this.#currentThreadId;\n if (threadId == null) return;\n if (!namespaceIsDefaultOnly(this.#subagents.snapshot.get(toolCallId))) {\n return;\n }\n const inflight = this.#namespaceResolves.get(toolCallId);\n if (inflight != null) return inflight;\n\n const run = (async () => {\n try {\n /**\n * Coalesce onto the hydrate-time discovery seed. That single\n * bounded `getHistory` page bulk-promotes every default-only\n * subagent, so when many cards mount at once (the common\n * reconnect case) they all await this one read instead of each\n * firing their own walk. Re-check after it settles: usually the\n * bulk seed already promoted us and no further fetch is needed.\n */\n const seed = this.#discoverySeedPromise;\n if (seed != null) {\n await seed;\n if (this.#disposed || this.#currentThreadId !== threadId) return;\n if (\n !namespaceIsDefaultOnly(this.#subagents.snapshot.get(toolCallId))\n ) {\n return;\n }\n }\n const map = await resolveSubagentNamespaces(\n this.#options.client,\n threadId,\n [toolCallId],\n { messagesKey: this.#messagesKey }\n );\n if (this.#disposed || this.#currentThreadId !== threadId) return;\n const segment = map.get(toolCallId);\n if (segment != null) {\n this.#subagents.applyExecutionNamespace(toolCallId, segment);\n }\n } catch {\n /* non-fatal: SSE replay still reconciles the namespace */\n } finally {\n this.#namespaceResolves.delete(toolCallId);\n }\n })();\n this.#namespaceResolves.set(toolCallId, run);\n return run;\n }\n\n /**\n * Try to satisfy a scoped selector projection from checkpoint history\n * instead of opening a scoped `/events` replay.\n *\n * This is only valid while the root pump is deferred, which means hydrate\n * has classified the thread as idle/stale. Active and interrupted threads\n * must keep using SSE so ongoing work and resumes are observed. For an idle\n * thread, though, a late-mounted subagent card only needs the latest scoped\n * checkpoint snapshot; opening `/events` just asks the server to replay work\n * that already finished and can be slow for namespaces discovered from\n * history.\n *\n * Returns `true` when the projection was handled without `/events`. That can\n * mean either the store was seeded from namespace-specific history, or the\n * projection targeted a default subagent namespace that should be skipped\n * because hydrate promoted it to its execution namespace. Returns `false`\n * when the caller should fall back to the normal subscription path.\n */\n async #trySeedProjectionFromHistory<T>(params: {\n kind: \"messages\" | \"toolCalls\";\n namespace: readonly string[];\n store: StreamStore<T>;\n }): Promise<boolean> {\n const threadId = this.#currentThreadId;\n if (\n this.#disposed ||\n threadId == null ||\n params.namespace.length === 0 ||\n !this.#rootPumpDeferred ||\n this.#selfCreatedThreadIds.has(threadId)\n ) {\n return false;\n }\n\n if (await this.#skipDefaultSubagentProjection(params.namespace, threadId)) {\n return true;\n }\n if (\n this.#disposed ||\n this.#currentThreadId !== threadId ||\n !this.#rootPumpDeferred\n ) {\n return false;\n }\n\n const seed = await this.#getScopedHistorySeed(threadId, params.namespace);\n if (\n seed == null ||\n this.#disposed ||\n this.#currentThreadId !== threadId ||\n !this.#rootPumpDeferred\n ) {\n return false;\n }\n\n if (await this.#skipDefaultSubagentProjection(params.namespace, threadId)) {\n return true;\n }\n\n if (params.kind === \"messages\") {\n params.store.setValue(seed.messages as T);\n return true;\n }\n params.store.setValue(seed.toolCalls as T);\n return true;\n }\n\n /**\n * Suppress subscriptions for placeholder subagent namespaces once hydrate has\n * resolved the real execution namespace.\n *\n * Deep-agent discovery first creates cards at `tools:<toolCallId>`. The\n * actual worker history usually lives under a different checkpoint namespace\n * such as `tools:<uuid>`, and hydrate resolves that mapping from the bounded\n * root history seed. React/Vue/Svelte/Angular selector effects can mount\n * while that seed is still in flight, so this helper waits for it and then\n * returns `true` when the original placeholder namespace is stale. Returning\n * `true` tells the projection runtime not to open an `/events` subscription\n * for the wrong namespace; the framework will re-render with the promoted\n * card namespace and acquire the real projection.\n */\n async #skipDefaultSubagentProjection(\n namespace: readonly string[],\n threadId: string\n ): Promise<boolean> {\n const toolCallId = defaultSubagentToolCallId(namespace);\n if (toolCallId == null) return false;\n if (!namespaceIsDefaultOnly(this.#subagents.snapshot.get(toolCallId))) {\n return false;\n }\n const seed = this.#discoverySeedPromise;\n if (seed != null) {\n await seed;\n }\n if (this.#disposed || this.#currentThreadId !== threadId) return true;\n return !namespaceIsDefaultOnly(this.#subagents.snapshot.get(toolCallId));\n }\n\n /**\n * Load and cache the latest checkpoint snapshot for one scoped namespace.\n *\n * `useMessages(stream, subagent)` and `useToolCalls(stream, subagent)` often\n * mount together. Both need the same namespace-specific history page, so the\n * controller keeps an in-flight promise per `threadId + checkpoint_ns`.\n * The cache may already be primed by the hydrate-time discovery history page;\n * otherwise this method performs a narrow `checkpoint_ns` read and derives\n * both projection snapshots from that one response:\n *\n * - `messages` are coerced with the stream-local message coercion rules, so\n * serialized `content_blocks` and tool-call metadata hydrate correctly.\n * - `toolCalls` are reconstructed from AI tool calls plus matching\n * ToolMessages, enough for finished/stale card panels without replaying\n * the `tools` channel.\n *\n * Returns `null` when history does not contain usable values, or the request\n * fails. Callers treat that as a signal to fall back to `/events` so custom\n * servers or unusual state shapes still work.\n */\n #getScopedHistorySeed(\n threadId: string,\n namespace: readonly string[]\n ): Promise<ScopedHistorySeed | null> {\n const checkpointNs = namespaceKey(namespace);\n const key = `${threadId}|${checkpointNs}`;\n const existing = this.#scopedHistorySeeds.get(key);\n if (existing != null) return existing;\n\n const seed = (async (): Promise<ScopedHistorySeed | null> => {\n try {\n const history = await getHistoryPage(this.#options.client, threadId, {\n limit: 1,\n checkpoint: { checkpoint_ns: checkpointNs },\n });\n const values = history[0]?.values;\n if (values == null || typeof values !== \"object\") return null;\n const messages = extractAndCoerceMessagesWithFallback(\n values as Record<string, unknown>,\n this.#messagesKey\n );\n if (messages == null) return null;\n return {\n messages,\n toolCalls: seedToolCallsFromMessages(namespace, messages),\n };\n } catch {\n return null;\n }\n })();\n this.#scopedHistorySeeds.set(key, seed);\n return seed;\n }\n\n /**\n * Reuse the hydrate-time discovery history page as scoped projection data\n * when it already contains checkpoint values for a namespace.\n *\n * The discovery read is required to resolve subagent execution namespaces and\n * subgraph hosts. That same page often includes the latest values for those\n * namespaces, so priming `#scopedHistorySeeds` here lets later\n * `useMessages(stream, subagent)` / `useToolCalls(stream, subagent)` mounts\n * hydrate from memory instead of issuing an immediate second `getHistory`\n * request. If a namespace is not present in the bounded page,\n * `#getScopedHistorySeed` still falls back to a targeted `checkpoint_ns`\n * history read.\n */\n #primeScopedHistorySeedsFromHistory(\n threadId: string,\n history: Array<{\n checkpoint?: { checkpoint_ns?: unknown };\n values?: unknown;\n }>\n ): void {\n for (const state of history) {\n const checkpointNs = state.checkpoint?.checkpoint_ns;\n if (typeof checkpointNs !== \"string\" || checkpointNs.length === 0) {\n continue;\n }\n const namespace = checkpointNs\n .split(NAMESPACE_SEPARATOR)\n .filter((segment) => segment.length > 0);\n if (namespace.length === 0) continue;\n const key = `${threadId}|${namespaceKey(namespace)}`;\n if (this.#scopedHistorySeeds.has(key)) continue;\n const values = state.values;\n if (values == null || typeof values !== \"object\") continue;\n const messages = extractAndCoerceMessagesWithFallback(\n values as Record<string, unknown>,\n this.#messagesKey\n );\n if (messages == null) continue;\n this.#scopedHistorySeeds.set(\n key,\n Promise.resolve({\n messages,\n toolCalls: seedToolCallsFromMessages(namespace, messages),\n })\n );\n }\n }\n\n /**\n * Submit input to the active thread.\n *\n * To resume a pending interrupt, use {@link respond} instead.\n *\n * @param input - Input payload for a new run.\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 * Disconnect the client from the active run and mark the controller\n * idle. By default also cancels the run server-side; pass\n * `{ cancel: false }` or call {@link disconnect} to keep the agent\n * running (join/rejoin).\n */\n async stop(options?: StreamStopOptions): Promise<void> {\n const shouldCancel = options?.cancel ?? true;\n if (shouldCancel) {\n const threadId = this.#currentThreadId;\n const runId = this.#activeRunId;\n if (threadId != null && runId != null) {\n try {\n await this.#options.client.runs.cancel(threadId, runId);\n } catch {\n /* server cancel failures must not block client disconnect */\n }\n }\n }\n await this.#submitter.stop();\n }\n\n /**\n * Disconnect the client without cancelling the run server-side.\n * Alias for `stop({ cancel: false })`.\n */\n async disconnect(): Promise<void> {\n return this.stop({ cancel: false });\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 single pending protocol interrupt.\n *\n * When `options.interruptId` is omitted, resolution walks\n * {@link ThreadStream.interrupts `thread.interrupts`} from newest to\n * oldest and picks the first entry whose `interruptId` has not already\n * been resolved by a prior `respond()` call. That entry may be at the\n * root (`namespace: []`) or inside a subgraph (non-empty `namespace`).\n * This is **not** the same as {@link RootSnapshot.interrupts\n * `rootStore.interrupts[0]`} / framework `stream.interrupt`, which only\n * mirrors root-namespace interrupts for UI convenience.\n *\n * Omitting `interruptId` is fine when exactly one interrupt is pending.\n * When several can be active (parallel subagents, fan-out, nested\n * graphs), pass an explicit `interruptId` (and `namespace` for subgraph\n * interrupts) so you resume the interrupt the user acted on.\n *\n * To resume several interrupts pending at the same checkpoint in one\n * command, use {@link respondAll} — sequential single `respond()` calls\n * would not work, since the first resume starts a run, leaving the\n * others with no interrupted run to respond to.\n *\n * The server validates `namespace` against the pending interrupt. Root\n * interrupts use `namespace: []` (the default when `namespace` is\n * omitted). Subgraph interrupts require the exact tuple from\n * `getThread()?.interrupts` — see the example below.\n *\n * @param response - Payload sent back to the interrupted namespace.\n * @param options - Optional target (`interruptId` / `namespace`) and\n * run-level `config` / `metadata` folded into the run that services\n * the resume (model/user config, trigger source, test flags, …).\n * Equivalent to the same fields on {@link StreamSubmitOptions}.\n *\n * @example Single pending interrupt (safe to omit a target)\n * ```ts\n * await controller.respond({ approved: true });\n * ```\n *\n * @example Carry run config / metadata onto the resume\n * ```ts\n * await controller.respond(\n * { approved: true },\n * { config: { configurable: { model: \"gpt-4o\" } }, metadata: { source: \"ui\" } },\n * );\n * ```\n *\n * @example Multiple root interrupts — target by id\n * ```tsx\n * for (const intr of stream.interrupts) {\n * await stream.respond(decide(intr.value), { interruptId: intr.id! });\n * }\n * ```\n *\n * @example Subgraph interrupt — read `namespace` from the thread stream\n * ```tsx\n * const thread = stream.getThread();\n * for (const entry of thread?.interrupts ?? []) {\n * await stream.respond(buildResponse(entry.payload), {\n * interruptId: entry.interruptId,\n * namespace: entry.namespace,\n * });\n * }\n * ```\n *\n * Each {@link InterruptPayload} on `thread.interrupts` mirrors an\n * `input.requested` event: `{ interruptId, payload, namespace }`.\n * Nested interrupts may appear here but not on `stream.interrupts`.\n */\n async respond(\n response: unknown,\n options?: StreamRespondOptions<ConfigurableType>\n ): Promise<void> {\n if (this.#disposed || this.#thread == null) {\n throw new Error(\"No active thread to respond to.\");\n }\n\n const resolved =\n options?.interruptId != null\n ? {\n interruptId: options.interruptId,\n namespace: options.namespace ?? [...ROOT_NAMESPACE],\n }\n : this.#resolveInterruptForResume();\n if (resolved == null) {\n throw new Error(\"No pending interrupt to respond to.\");\n }\n const thread = this.#thread;\n try {\n // Route through the coordinator so a resumed run that fails (e.g. a\n // missing model key surfaced after the user answers) lands in the\n // reactive `rootStore.error` slot, exactly like a `submit()` failure.\n // The dispatch (`respondInput` + interrupt-resolved bookkeeping) is\n // what's awaited; the resumed run's terminal is watched in the\n // background (see {@link SubmitCoordinator.dispatchResume}).\n await this.#submitter.dispatchResume(async () => {\n await thread.respondInput({\n namespace: resolved.namespace,\n interrupt_id: resolved.interruptId,\n response: normalizeHitlResponseForServer(response),\n config: options?.config,\n metadata: options?.metadata,\n });\n this.#markInterruptResolvedInRootStore(resolved.interruptId);\n });\n } catch (error) {\n if (this.#disposed && isAbortLikeError(error)) {\n return;\n }\n throw error;\n }\n }\n\n /**\n * Resume several pending interrupts at the same checkpoint in a single\n * command.\n *\n * Required when a run pauses on multiple interrupts simultaneously\n * (e.g. parallel tool-authorization prompts): a single\n * `Command({ resume })` carrying every interrupt's payload resumes them\n * together. Sequential {@link respond} calls would fail because the\n * first resume starts a run, leaving the rest with no interrupted run to\n * respond to.\n *\n * `responsesById` maps each pending `interruptId` to the payload sent\n * back to it, so different interrupts can receive different responses\n * (approve one, deny another). To send the *same* payload to several\n * interrupts, build the map with that value for each id, e.g.\n * `Object.fromEntries(ids.map((id) => [id, response]))`.\n *\n * The server resumes by `interruptId`, so namespaces are resolved\n * internally from `getThread()?.interrupts` and need not be supplied.\n *\n * @param responsesById - Map of pending `interruptId` to its response\n * payload. Must contain at least one entry.\n * @param options - Optional run-level `config` / `metadata` folded into\n * the single run that services the batched resume. Equivalent to the\n * same fields on {@link StreamSubmitOptions}.\n *\n * @example Distinct payloads per interrupt\n * ```tsx\n * await stream.respondAll({\n * [interruptA.id]: { approved: true },\n * [interruptB.id]: { approved: false },\n * });\n * ```\n *\n * @example Same payload to every pending interrupt\n * ```tsx\n * await stream.respondAll(\n * Object.fromEntries(stream.interrupts.map((i) => [i.id!, { approved: true }])),\n * );\n * ```\n */\n async respondAll(\n responsesById: Record<string, unknown>,\n options?: StreamRespondAllOptions<ConfigurableType>\n ): Promise<void> {\n if (this.#disposed || this.#thread == null) {\n throw new Error(\"No active thread to respond to.\");\n }\n const entries = Object.entries(responsesById);\n if (entries.length === 0) {\n throw new Error(\"respondAll() requires at least one response.\");\n }\n const thread = this.#thread;\n const pending = thread.interrupts;\n const responses = entries.map(([interruptId, response]) => ({\n interrupt_id: interruptId,\n response: normalizeHitlResponseForServer(response),\n namespace: pending.find((entry) => entry.interruptId === interruptId)\n ?.namespace ?? [...ROOT_NAMESPACE],\n }));\n try {\n // See `respond()` — route through the coordinator so the single run\n // that services the batched resume surfaces failures on the reactive\n // `rootStore.error` slot.\n await this.#submitter.dispatchResume(async () => {\n await thread.respondInput({\n responses,\n config: options?.config,\n metadata: options?.metadata,\n });\n for (const { interrupt_id: interruptId } of responses) {\n this.#markInterruptResolvedInRootStore(interruptId);\n }\n });\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.#scopedHistorySeeds.clear();\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 step?: 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 /**\n * Apply a submit input optimistically to the root projection before\n * the server responds. Mints stable ids for id-less messages (so the\n * server echo reconciles by id), appends them to the projection, and\n * shallow-merges non-message input keys into `values`.\n *\n * Returns the dispatch payload (id-injected) for the coordinator to\n * send, plus an {@link OptimisticHandle} for terminal reconciliation.\n * Returns `undefined` when optimistic UI is disabled or there is\n * nothing to echo, in which case the coordinator dispatches the raw\n * input unchanged.\n *\n * @param input - Raw input passed to `submit()`.\n */\n #beginOptimistic(\n input: unknown\n ): { dispatchInput: unknown; handle: OptimisticHandle } | undefined {\n if (this.#options.optimistic === false) return undefined;\n if (input == null || typeof input !== \"object\" || Array.isArray(input)) {\n return undefined;\n }\n const prepared = prepareOptimisticInput(\n input as Record<string, unknown>,\n this.#messagesKey,\n () => uuidv7()\n );\n const extraKeys = Object.keys(prepared.extraValues);\n if (prepared.echoedIds.length === 0 && extraKeys.length === 0) {\n return undefined;\n }\n\n const currentValues = this.rootStore.getSnapshot().values as Record<\n string,\n unknown\n >;\n const restoreKeys = extraKeys.map((key) => ({\n key,\n hadKey: Object.prototype.hasOwnProperty.call(currentValues, key),\n prevValue: currentValues[key],\n }));\n\n this.#sawValuesForRun = false;\n this.#rootMessages.appendOptimistic(\n prepared.optimisticMessages,\n prepared.extraValues\n );\n if (prepared.echoedIds.length > 0) {\n this.#messageMetadata.markPending(prepared.echoedIds);\n }\n return {\n dispatchInput: prepared.dispatchInput,\n handle: { echoedIds: prepared.echoedIds, restoreKeys },\n };\n }\n\n /**\n * Reconcile optimistic state when a run terminates.\n *\n * - Messages: any echoed id still `\"pending\"` (never echoed by the\n * server) is flipped to `\"sent\"` on success/interrupt, or\n * `\"failed\"` on failure/abort. Ids the server already echoed were\n * flipped to `\"sent\"` in {@link #applyValues} and are untouched.\n * - Non-message keys: rolled back to their pre-submit values when no\n * server `values` event landed during the run (otherwise the\n * server snapshot already reconciled them). Skipped on abort,\n * where a superseding run (or `stop()`) owns subsequent state.\n *\n * @param handle - Handle returned by {@link #beginOptimistic}.\n * @param event - Terminal lifecycle event for the run.\n */\n #settleOptimistic(\n handle: OptimisticHandle,\n event: \"completed\" | \"failed\" | \"interrupted\" | \"aborted\"\n ): void {\n const failed = event === \"failed\" || event === \"aborted\";\n this.#messageMetadata.resolvePending(\n handle.echoedIds,\n failed ? \"failed\" : \"sent\"\n );\n if (event !== \"aborted\" && !this.#sawValuesForRun) {\n this.#rootMessages.restoreValueKeys(handle.restoreKeys);\n }\n }\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 // A root `values` snapshot landed: optimistic non-message keys are\n // now reconciled against server truth (see #settleOptimistic).\n this.#sawValuesForRun = true;\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 step: checkpoint?.step,\n });\n if (nextMessages.length > 0) {\n // Any optimistic message the server just echoed is now\n // server-authoritative: flip its status `pending` → `sent`.\n this.#messageMetadata.resolvePending(\n nextMessages\n .map((m) => m.id)\n .filter((id): id is string => typeof id === \"string\"),\n \"sent\"\n );\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> = normalizeInterruptForClient({\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 * Mark an interrupt resolved for replay filtering and mirror the\n * removal into the root snapshot the framework hooks read.\n */\n #markInterruptResolvedInRootStore(interruptId: string): void {\n this.#resolvedInterrupts.add(interruptId);\n this.rootStore.setState((s) => {\n const interrupts = s.interrupts.filter(\n (entry) => entry.id !== interruptId\n );\n if (\n interrupts.length === s.interrupts.length &&\n s.interrupt?.id !== interruptId\n ) {\n return s;\n }\n return {\n ...s,\n interrupts,\n interrupt: interrupts[0],\n };\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 this.#awaitRootTerminal(signal, {\n skipInterruptedUntilRunning: false,\n });\n }\n\n /**\n * Resolve on the resumed run's root terminal lifecycle.\n *\n * Unlike {@link #awaitNextTerminal}, ignores `interrupted` events until a\n * root `running` lifecycle has been observed. Headless-tool flows can emit\n * a stale `interrupted` for the run being resumed after `input.requested`\n * but before `respondInput` calls `#prepareForNextRun`; accepting that\n * terminal would unsubscribe the watcher before the resumed run's `failed`\n * terminal arrives.\n */\n #awaitResumedRunTerminal(signal: AbortSignal): Promise<{\n event: \"completed\" | \"failed\" | \"interrupted\" | \"aborted\";\n error?: string;\n }> {\n return this.#awaitRootTerminal(signal, {\n skipInterruptedUntilRunning: true,\n });\n }\n\n #awaitRootTerminal(\n signal: AbortSignal,\n options: { skipInterruptedUntilRunning: boolean }\n ): Promise<{\n event: \"completed\" | \"failed\" | \"interrupted\" | \"aborted\";\n error?: string;\n }> {\n return new Promise((resolve) => {\n let settled = false;\n let sawRunning = 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 === \"running\") {\n sawRunning = true;\n return;\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 if (options.skipInterruptedUntilRunning && !sawRunning) {\n return;\n }\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 * True when a subagent still sits on its default `tools:<toolCallId>`\n * namespace — i.e. no execution namespace has been observed (via SSE\n * replay) or resolved (via history) yet. Used to gate lazy namespace\n * resolution so already-promoted subagents aren't re-fetched.\n */\nfunction namespaceIsDefaultOnly(\n entry: SubagentDiscoverySnapshot | undefined\n): boolean {\n if (entry == null) return false;\n return (\n entry.namespace.length === 1 && entry.namespace[0] === `tools:${entry.id}`\n );\n}\n\nfunction defaultSubagentToolCallId(\n namespace: readonly string[]\n): string | undefined {\n if (namespace.length !== 1) return undefined;\n const segment = namespace[0];\n if (!segment.startsWith(\"tools:\")) return undefined;\n const id = segment.slice(\"tools:\".length);\n return id.length > 0 ? id : undefined;\n}\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\nfunction extractAndCoerceMessagesWithFallback(\n values: Record<string, unknown>,\n messagesKey: string\n): BaseMessage[] | null {\n let raw = values[messagesKey];\n if (!Array.isArray(raw) && messagesKey !== \"messages\") {\n raw = values.messages;\n }\n if (!Array.isArray(raw)) return null;\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmGA,SAAS,iBAAiB,OAAyB;AACjD,KAAI,SAAS,QAAQ,OAAO,UAAU,SAAU,QAAO;CACvD,MAAM,aAAa;AACnB,QACE,WAAW,SAAS,gBACnB,OAAO,WAAW,YAAY,YAC7B,WAAW,QAAQ,SAAS,UAAU;;AAI5C,SAAS,gBAAgB,OAAsD;AAC7E,KAAI,UAAU,YAAa,QAAO;AAClC,KAAI,UAAU,SAAU,QAAO;AAC/B,KAAI,UAAU,cAAe,QAAO;AACpC,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCT,SAAS,oBACP,OACS;AACT,KAAI,SAAS,KAAM,QAAO;AAG1B,KAAI,CAAC,MAAM,QAAQ,MAAM,KAAK,CAAE,QAAO;AACvC,KAAI,MAAM,KAAK,SAAS,EAAG,QAAO;AAClC,KAAI,MAAM,QAAQ,MAAM,MAAM,CAC5B,MAAK,MAAM,QAAQ,MAAM,OAAO;EAC9B,MAAM,aAAc,MAA0C;AAC9D,MAAI,MAAM,QAAQ,WAAW,IAAI,WAAW,SAAS,EAAG,QAAO;;AAGnE,QAAO;;AAGT,MAAM,iBAAoC,EAAE;;;;;;AAO5C,MAAa,qBAAyC;CACpD;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;;;;;;;;AA2BD,IAAa,mBAAb,MAIE;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA,aAAsB,IAAI,mBAAmB;CAC7C,aAAsB,IAAI,mBAAmB;CAC7C,mBAA4B,IAAI,wBAAwB;CAExD;CACA;CACA;CACA;CACA;;;;;;CAMA,oBAAoB;CACpB;CACA,YAAY;CACZ,uBAA6D;CAC7D,sCAA+B,IAAI,KAAa;;;;;;;;;;;;;;CAchD,8BAAkD;;;;;;;;;CASlD,oBAAoB;;;;;;;CAOpB,wCAAiC,IAAI,KAAa;;;;;;;CAOlD,qCAA8B,IAAI,KAA4B;;;;;;;;;;CAU9D;CACA,sCAA+B,IAAI,KAGhC;CACH,sCAA+B,IAAI,KAA6B;CAChE;CACA;CACA,iBAAiB;;;;;;;;CAQjB,mBAAmB;;;;;;;;CASnB;CACA;CACA;;;;;CAMA,qBAAqB,IAAI,mBAAmB;CAC5C;CACA;CAGA;CAEA,mCAA4B,IAAI,KAE7B;;;;;;CAOH,YAAY,SAA6C;AACvD,QAAA,UAAgB;AAChB,QAAA,cAAoB,QAAQ,eAAe;AAC3C,QAAA,kBAAwB,QAAQ,YAAY;AAC5C,QAAA,UAAgB;GACd,UAAU;GACV,YAAY,aAAa;AACvB,UAAA,mBAAyB,IAAI,SAAS;AACtC,iBAAa;AACX,WAAA,mBAAyB,OAAO,SAAS;;;GAG7C,qBAAqB,WACnB,MAAA,6BAAmC,OAAO;GAC7C;AACD,OAAK,WAAW,IAAI,gBAAgB,MAAA,QAAc;AAClD,OAAK,gBAAgB,MAAA,UAAgB;AACrC,OAAK,gBAAgB,MAAA,UAAgB;AACrC,OAAK,sBAAsB,MAAA,UAAgB;AAC3C,OAAK,YAAY,IAAI,YACnB,MAAA,uBAA6B,CAC9B;AACD,QAAA,eAAqB,IAAI,sBAAsB;GAC7C,aAAa,MAAA;GACb,OAAO,KAAK;GACb,CAAC;AACF,QAAA,mBAAyB,IAAI,wBAAwB;GACnD,OAAO,KAAK;GACZ,kBAAkB,MAAA;GACnB,CAAC;AACF,OAAK,uBAAuB,MAAA,gBAAsB;AAClD,OAAK,aAAa,IAAI,YACpB,YACD;AACD,QAAA,YAAkB,IAAI,kBAAkB;GACtC,SAAS,MAAA;GACT,WAAW,KAAK;GAChB,YAAY,KAAK;GACjB,mBAAmB,MAAA;GACnB,0BAA0B,MAAA;GAC1B,qBAAqB,aAAa;AAChC,UAAA,kBAAwB;;GAE1B,8BAA8B,aAAa;AACzC,UAAA,qBAA2B,IAAI,SAAS;;GAE1C,UAAU,aAAa,KAAK,QAAQ,SAAS;GAC7C,eAAe,UAAU,kBACvB,MAAA,aAAmB,UAAU,cAAc;GAC7C,6BAA6B,MAAA,uBAA6B;GAC1D,+BAA+B,MAAA,yBAA+B;GAC9D,4BAA4B,aAAa;AACvC,UAAA,qBAA2B,OAAO,SAAS;;GAE7C,4BAA4B,MAAA;GAC5B,oBAAoB,WAAW,MAAA,kBAAwB,OAAO;GAC9D,0BAA0B,WACxB,MAAA,wBAA8B,OAAO;GACvC,qBAAqB;AAKnB,UAAA,6BAAmC;AACnC,UAAA,oBAA0B;;GAE5B,kBAAkB,MAAA,mBAAyB;GAC3C,eAAe,UAAU,MAAA,cAAoB,MAAM;GACnD,iBAAiB,QAAQ,UAAU,MAAA,gBAAsB,QAAQ,MAAM;GACvE,gBAAgB,MAAA,iBAAuB;GACvC,kBAAkB,UAAU,MAAA,gBAAsB,MAAM;GACxD,mBAAmB,QAAQ,UACzB,MAAA,iBAAuB,QAAQ,MAAM;GACxC,CAAC;AACF,QAAA,mBAAyB,MAAA,wBAA8B;;;;;;;;;AASvD,QAAA,iBAAuB,YAAY,KAAA,EAAU;;;;;;;;;AAS7C,MAAI,MAAA,mBAAyB,KACtB,MAAK,QAAQ,MAAA,gBAAsB;MAExC,OAAA,kBAAwB;;;;;;;;;CAW5B,IAAI,mBAAkC;AACpC,SAAO,MAAA;;;;;CAMT,0BAAyC;AACvC,SAAO,IAAI,SAAe,SAAS,WAAW;AAC5C,SAAA,mBAAyB;AACzB,SAAA,kBAAwB;IACxB;;;;;CAMJ,yBAA+B;;;;;;AAM7B,QAAA,iBAAuB,YAAY,KAAA,EAAU;AAC7C,QAAA,mBAAyB,MAAA,wBAA8B;;;;;;CASzD,OAAA,sBAAqE;EACnE,MAAM,WAAW,MAAA;AACjB,MAAI,YAAY,KAAM,QAAO;EAE7B,MAAM,YAAY,MAAA,QAAc;AAChC,MACE,aAAa,QACb,OAAO,cAAc,YACrB,OAAO,UAAU,aAAa,WAE9B,QAAQ,MAAM,UAAU,UAAqB;AAG/C,SAAO,MAAA,QAAc,OAAO,QAAQ,SAAoB,SAAS;;;;;;;;;;CAWnE,MAAM,QAAQ,UAAyC;AACrD,MAAI,MAAA,SAAgB;EACpB,MAAM,SAAS,aAAa,KAAA,IAAY,MAAA,kBAAwB;EAChE,MAAM,UAAU,WAAW,MAAA;AAC3B,QAAA,kBAAwB,UAAU;AAGlC,QAAA,uBAA6B,KAAA;AAC7B,QAAA,mBAAyB,OAAO;AAChC,OAAK,UAAU,UAAU,OAAO;GAAE,GAAG;GAAG,UAAU,MAAA;GAAuB,EAAE;AAE3E,MAAI,SAAS;;;;;AAKX,SAAA,uBAA6B;AAC7B,SAAM,MAAA,gBAAsB;;;;;;;AAO5B,QAAK,UAAU,gBAAgB;IAC7B,GAAG,MAAA,uBAA6B;IAChC,UAAU,MAAA;IACX,EAAE;;;;;;AAMH,QAAK,WAAW,eACR,YACP;;AAGH,MAAI,MAAA,mBAAyB,MAAM;AACjC,QAAK,UAAU,UAAU,OAAO;IAAE,GAAG;IAAG,iBAAiB;IAAO,EAAE;AAClE,SAAA,kBAAwB;AACxB;;;;;;;;AASF,MAAI,MAAA,qBAA2B,IAAI,MAAA,gBAAsB,EAAE;AACzD,QAAK,UAAU,UAAU,OAAO;IAAE,GAAG;IAAG,iBAAiB;IAAO,EAAE;AAClE,SAAA,kBAAwB;AACxB;;AAGF,OAAK,UAAU,UAAU,OAAO;GAAE,GAAG;GAAG,iBAAiB;GAAM,EAAE;EACjE,IAAI;EACJ,IAAI,eAAe;EAInB,IAAI,eAAe;AACnB,MAAI;GACF,MAAM,QAAQ,MAAM,MAAA,qBAA2B;AAC/C,kBAAe,SAAS;AACxB,kBAAe,oBAAoB,MAAM;AACzC,OAAI,OAAO,UAAU,MAAM;;;;;;;;;IASzB,MAAM,eAAe,MAAM,YAAY;IACvC,MAAM,qBACJ,MAAM,mBAAmB,iBAAiB,KAAA;;;;;;;;;IAS5C,MAAM,WAAY,MAAM,UACpB;IACJ,MAAM,sBACJ,OAAO,iBAAiB,WACpB;KACE,IAAI;KACJ,GAAI,sBAAsB,OACtB,EAAE,WAAW,oBAAoB,GACjC,EAAE;KACN,GAAI,OAAO,aAAa,WAAW,EAAE,MAAM,UAAU,GAAG,EAAE;KAC3D,GACD,KAAA;AACN,UAAA,YAAkB,MAAM,QAAmB,oBAAoB;;;;;;;;;IAU/D,MAAM,eAAgB,MAAM,OAC1B,MAAA;AAEF,QAAI,MAAM,QAAQ,aAAa,EAAE;AAC/B,WAAA,UAAgB,2BAA2B,aAAa;;;;;;;;;;;;;AAcxD,SAAI,CAAC,cAAc;MACjB,MAAM,YAAa,aAChB,KAAK,YAAY,SAAS,GAAG,CAC7B,QAAQ,OAAqB,OAAO,OAAO,SAAS;AACvD,UAAI,UAAU,SAAS,EACrB,OAAA,aAAmB,eAAe,UAAU;;;;;;;;;;;GAYpD,MAAM,cAAc,MAAA,gBAAsB,0BAA0B;AACpE,OAAI,YAAY,OAAO,GAAG;AACxB,UAAA,aAAmB,uBAAuB,YAAY;AACtD,UAAA,gBAAsB,OAAO,YAAY;;;;;;;;;;;;;AAa3C,OAAI,MAAM,QAAQ,OAAO,MAAM,EAAE;IAC/B,MAAM,oBAAoB,MAAA;IAC1B,MAAM,mBAA+C,EAAE;IACvD,MAAM,4BAAY,IAAI,KAAa;AACnC,SAAK,MAAM,QAAQ,MAAM,OAAO;AAC9B,SAAI,CAAC,MAAM,QAAQ,MAAM,WAAW,CAAE;AACtC,UAAK,MAAM,aAAa,KAAK,YAAY;MACvC,MAAM,QAAQ;MAId,MAAM,KAAK,OAAO;AAClB,UAAI,OAAO,OAAO,YAAY,UAAU,IAAI,GAAG,CAAE;AACjD,gBAAU,IAAI,GAAG;AACjB,uBAAiB,KACf,4BAA4B;OAC1B;OACA,OAAO,OAAO;OACf,CAAC,CACH;;;AAGL,SAAK,UAAU,UAAU,OAAO;KAC9B,GAAG;KACH,YAAY;KACZ,WAAW,iBAAiB;KAC7B,EAAE;AAKH,QAAI,MAAA,qBAA2B,kBAC7B,OAAA,6BAAmC;;WAGhC,OAAO;AAUd,OADgB,OAAsC,WACvC,KAAK;AAClB,qBAAiB;AACjB,SAAK,UAAU,UAAU,OAAO;KAAE,GAAG;KAAG;KAAO,EAAE;;YAE3C;AACR,QAAK,UAAU,UAAU,OAAO;IAAE,GAAG;IAAG,iBAAiB;IAAO,EAAE;AAClE,OAAI,kBAAkB,KACpB,OAAA,gBAAsB,eAAe;OAErC,OAAA,kBAAwB;;;;;;;;;;;;;;;EAiB5B,MAAM,SAAS,MAAA,aAAmB,MAAA,iBAAuB,CAAC,aAAa;;;;;;;;;;;;;;;;AAiBvE,MAAI,gBAAgB,aAClB,QAAO,uBAAuB;AAEhC,MAAI,cAAc;;;;;;;;;;;;;;GAchB,MAAM,OAAsB,MAAA,yBAC1B,MAAA,gBACD,CAAC,cAAc;AAEd,QAAI,MAAA,yBAA+B,KACjC,OAAA,uBAA6B,KAAA;KAE/B;AACF,SAAA,uBAA6B;;;;;;;;CASjC,OAAA,yBAAgC,UAAiC;AAC/D,MAAI;GACF,MAAM,UAAU,MAAM,eAAe,MAAA,QAAc,QAAQ,UAAU,EACnE,OAAO,IACR,CAAC;AAEF,OAAI,MAAA,YAAkB,MAAA,oBAA0B,SAAU;AAE1D,SAAA,mCAAyC,UAAU,QAAQ;GAE3D,MAAM,QAAQ,8BAA8B,QAAQ;AACpD,SAAA,UAAgB,gBAAgB,MAAM;GAEtC,MAAM,iBAAiB,CAAC,GAAG,MAAA,UAAgB,SAAS,QAAQ,CAAC,CAC1D,OAAO,uBAAuB,CAC9B,KAAK,UAAU,MAAM,GAAG;AAC3B,OAAI,eAAe,SAAS,GAAG;IAC7B,MAAM,MAAM,sBACV,SACA,gBACA,MAAA,YACD;AACD,SAAK,MAAM,CAAC,IAAI,YAAY,IAC1B,OAAA,UAAgB,wBAAwB,IAAI,QAAQ;;UAGlD;;;;;;;;;;;;;;;;CAmBV,MAAM,yBAAyB,YAAmC;AAChE,MAAI,MAAA,SAAgB;EACpB,MAAM,WAAW,MAAA;AACjB,MAAI,YAAY,KAAM;AACtB,MAAI,CAAC,uBAAuB,MAAA,UAAgB,SAAS,IAAI,WAAW,CAAC,CACnE;EAEF,MAAM,WAAW,MAAA,kBAAwB,IAAI,WAAW;AACxD,MAAI,YAAY,KAAM,QAAO;EAE7B,MAAM,OAAO,YAAY;AACvB,OAAI;;;;;;;;;IASF,MAAM,OAAO,MAAA;AACb,QAAI,QAAQ,MAAM;AAChB,WAAM;AACN,SAAI,MAAA,YAAkB,MAAA,oBAA0B,SAAU;AAC1D,SACE,CAAC,uBAAuB,MAAA,UAAgB,SAAS,IAAI,WAAW,CAAC,CAEjE;;IAGJ,MAAM,MAAM,MAAM,0BAChB,MAAA,QAAc,QACd,UACA,CAAC,WAAW,EACZ,EAAE,aAAa,MAAA,aAAmB,CACnC;AACD,QAAI,MAAA,YAAkB,MAAA,oBAA0B,SAAU;IAC1D,MAAM,UAAU,IAAI,IAAI,WAAW;AACnC,QAAI,WAAW,KACb,OAAA,UAAgB,wBAAwB,YAAY,QAAQ;WAExD,WAEE;AACR,UAAA,kBAAwB,OAAO,WAAW;;MAE1C;AACJ,QAAA,kBAAwB,IAAI,YAAY,IAAI;AAC5C,SAAO;;;;;;;;;;;;;;;;;;;;CAqBT,OAAA,6BAAuC,QAIlB;EACnB,MAAM,WAAW,MAAA;AACjB,MACE,MAAA,YACA,YAAY,QACZ,OAAO,UAAU,WAAW,KAC5B,CAAC,MAAA,oBACD,MAAA,qBAA2B,IAAI,SAAS,CAExC,QAAO;AAGT,MAAI,MAAM,MAAA,8BAAoC,OAAO,WAAW,SAAS,CACvE,QAAO;AAET,MACE,MAAA,YACA,MAAA,oBAA0B,YAC1B,CAAC,MAAA,iBAED,QAAO;EAGT,MAAM,OAAO,MAAM,MAAA,qBAA2B,UAAU,OAAO,UAAU;AACzE,MACE,QAAQ,QACR,MAAA,YACA,MAAA,oBAA0B,YAC1B,CAAC,MAAA,iBAED,QAAO;AAGT,MAAI,MAAM,MAAA,8BAAoC,OAAO,WAAW,SAAS,CACvE,QAAO;AAGT,MAAI,OAAO,SAAS,YAAY;AAC9B,UAAO,MAAM,SAAS,KAAK,SAAc;AACzC,UAAO;;AAET,SAAO,MAAM,SAAS,KAAK,UAAe;AAC1C,SAAO;;;;;;;;;;;;;;;;CAiBT,OAAA,8BACE,WACA,UACkB;EAClB,MAAM,aAAa,0BAA0B,UAAU;AACvD,MAAI,cAAc,KAAM,QAAO;AAC/B,MAAI,CAAC,uBAAuB,MAAA,UAAgB,SAAS,IAAI,WAAW,CAAC,CACnE,QAAO;EAET,MAAM,OAAO,MAAA;AACb,MAAI,QAAQ,KACV,OAAM;AAER,MAAI,MAAA,YAAkB,MAAA,oBAA0B,SAAU,QAAO;AACjE,SAAO,CAAC,uBAAuB,MAAA,UAAgB,SAAS,IAAI,WAAW,CAAC;;;;;;;;;;;;;;;;;;;;;;CAuB1E,sBACE,UACA,WACmC;EACnC,MAAM,eAAe,aAAa,UAAU;EAC5C,MAAM,MAAM,GAAG,SAAS,GAAG;EAC3B,MAAM,WAAW,MAAA,mBAAyB,IAAI,IAAI;AAClD,MAAI,YAAY,KAAM,QAAO;EAE7B,MAAM,QAAQ,YAA+C;AAC3D,OAAI;IAKF,MAAM,UAJU,MAAM,eAAe,MAAA,QAAc,QAAQ,UAAU;KACnE,OAAO;KACP,YAAY,EAAE,eAAe,cAAc;KAC5C,CAAC,EACqB,IAAI;AAC3B,QAAI,UAAU,QAAQ,OAAO,WAAW,SAAU,QAAO;IACzD,MAAM,WAAW,qCACf,QACA,MAAA,YACD;AACD,QAAI,YAAY,KAAM,QAAO;AAC7B,WAAO;KACL;KACA,WAAW,0BAA0B,WAAW,SAAS;KAC1D;WACK;AACN,WAAO;;MAEP;AACJ,QAAA,mBAAyB,IAAI,KAAK,KAAK;AACvC,SAAO;;;;;;;;;;;;;;;CAgBT,oCACE,UACA,SAIM;AACN,OAAK,MAAM,SAAS,SAAS;GAC3B,MAAM,eAAe,MAAM,YAAY;AACvC,OAAI,OAAO,iBAAiB,YAAY,aAAa,WAAW,EAC9D;GAEF,MAAM,YAAY,aACf,MAAA,KAA0B,CAC1B,QAAQ,YAAY,QAAQ,SAAS,EAAE;AAC1C,OAAI,UAAU,WAAW,EAAG;GAC5B,MAAM,MAAM,GAAG,SAAS,GAAG,aAAa,UAAU;AAClD,OAAI,MAAA,mBAAyB,IAAI,IAAI,CAAE;GACvC,MAAM,SAAS,MAAM;AACrB,OAAI,UAAU,QAAQ,OAAO,WAAW,SAAU;GAClD,MAAM,WAAW,qCACf,QACA,MAAA,YACD;AACD,OAAI,YAAY,KAAM;AACtB,SAAA,mBAAyB,IACvB,KACA,QAAQ,QAAQ;IACd;IACA,WAAW,0BAA0B,WAAW,SAAS;IAC1D,CAAC,CACH;;;;;;;;;;;CAYL,MAAM,OACJ,OACA,SACe;AACf,QAAM,MAAA,UAAgB,OAAO,OAAO,QAAQ;;;;;;;;CAS9C,MAAM,KAAK,SAA4C;AAErD,MADqB,SAAS,UAAU,MACtB;GAChB,MAAM,WAAW,MAAA;GACjB,MAAM,QAAQ,MAAA;AACd,OAAI,YAAY,QAAQ,SAAS,KAC/B,KAAI;AACF,UAAM,MAAA,QAAc,OAAO,KAAK,OAAO,UAAU,MAAM;WACjD;;AAKZ,QAAM,MAAA,UAAgB,MAAM;;;;;;CAO9B,MAAM,aAA4B;AAChC,SAAO,KAAK,KAAK,EAAE,QAAQ,OAAO,CAAC;;CAGrC,qBAA2B;AACzB,QAAA,iBAAuB;;CAGzB,mBAAyB;AACvB,QAAA,gBAAsB,KAAK,IAAI,GAAG,MAAA,gBAAsB,EAAE;;CAG5D,eAAe,OAAqB;AAClC,QAAA,cAAoB;AACpB,MAAI;AACF,SAAA,QAAc,YAAY,EAAE,OAAO,CAAC;UAC9B;;CAKV,iBACE,QACA,QAAQ,MAAA,aACF;AACN,MAAI,SAAS,QAAQ,UAAU,MAAA,YAC7B,OAAA,cAAoB,KAAA;AAEtB,mBAAiB;AACf,OAAI,MAAA,SAAgB;AACpB,OAAI;AACF,UAAA,QAAc,cACZ,SAAS,OAAO,EAAE,QAAQ,GAAG;KAAE;KAAO;KAAQ,CAC/C;WACK;KAGP,EAAE;;CAGP,yBAAkC,UAAuB;AACvD,MAAI,MAAA,gBAAsB,EAAG;AAC7B,MAAI,MAAM,WAAW,YAAa;AAClC,MAAI,CAAC,gBAAgB,MAAM,OAAO,UAAU,CAAE;AAC9C,MAAI,CAAC,KAAK,UAAU,aAAa,CAAC,UAAW;EAC7C,MAAM,YAAa,MAAyB,OAAO;EAGnD,MAAM,SAAS,gBAAgB,WAAW,MAAM;AAChD,MAAI,UAAU,KAAM;AACpB,QAAA,gBAAsB,OAAO;;;;;;;;;;;;;CAc/B,MAAM,aAAa,IAA8B;AAC/C,SAAO,MAAA,UAAgB,aAAa,GAAG;;;;;CAMzC,MAAM,aAA4B;AAChC,QAAM,MAAA,UAAgB,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuEpC,MAAM,QACJ,UACA,SACe;AACf,MAAI,MAAA,YAAkB,MAAA,UAAgB,KACpC,OAAM,IAAI,MAAM,kCAAkC;EAGpD,MAAM,WACJ,SAAS,eAAe,OACpB;GACE,aAAa,QAAQ;GACrB,WAAW,QAAQ,aAAa,CAAC,GAAG,eAAe;GACpD,GACD,MAAA,2BAAiC;AACvC,MAAI,YAAY,KACd,OAAM,IAAI,MAAM,sCAAsC;EAExD,MAAM,SAAS,MAAA;AACf,MAAI;AAOF,SAAM,MAAA,UAAgB,eAAe,YAAY;AAC/C,UAAM,OAAO,aAAa;KACxB,WAAW,SAAS;KACpB,cAAc,SAAS;KACvB,UAAU,+BAA+B,SAAS;KAClD,QAAQ,SAAS;KACjB,UAAU,SAAS;KACpB,CAAC;AACF,UAAA,iCAAuC,SAAS,YAAY;KAC5D;WACK,OAAO;AACd,OAAI,MAAA,YAAkB,iBAAiB,MAAM,CAC3C;AAEF,SAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6CV,MAAM,WACJ,eACA,SACe;AACf,MAAI,MAAA,YAAkB,MAAA,UAAgB,KACpC,OAAM,IAAI,MAAM,kCAAkC;EAEpD,MAAM,UAAU,OAAO,QAAQ,cAAc;AAC7C,MAAI,QAAQ,WAAW,EACrB,OAAM,IAAI,MAAM,+CAA+C;EAEjE,MAAM,SAAS,MAAA;EACf,MAAM,UAAU,OAAO;EACvB,MAAM,YAAY,QAAQ,KAAK,CAAC,aAAa,eAAe;GAC1D,cAAc;GACd,UAAU,+BAA+B,SAAS;GAClD,WAAW,QAAQ,MAAM,UAAU,MAAM,gBAAgB,YAAY,EACjE,aAAa,CAAC,GAAG,eAAe;GACrC,EAAE;AACH,MAAI;AAIF,SAAM,MAAA,UAAgB,eAAe,YAAY;AAC/C,UAAM,OAAO,aAAa;KACxB;KACA,QAAQ,SAAS;KACjB,UAAU,SAAS;KACpB,CAAC;AACF,SAAK,MAAM,EAAE,cAAc,iBAAiB,UAC1C,OAAA,iCAAuC,YAAY;KAErD;WACK,OAAO;AACd,OAAI,MAAA,YAAkB,iBAAiB,MAAM,CAC3C;AAEF,SAAM;;;;;;CAOV,MAAM,UAAyB;AAC7B,MAAI,MAAA,SAAgB;AACpB,QAAA,sBAA4B;AAC5B,QAAA,WAAiB;AACjB,QAAA,UAAgB,gBAAgB;AAChC,QAAM,MAAA,gBAAsB;AAC5B,QAAM,KAAK,SAAS,SAAS;AAC7B,QAAA,gBAAsB,OAAO;;;;;;;;;;;;;;;;;CAkB/B,WAAuB;AACrB,QAAA,sBAA4B;AAC5B,eAAa;AACX,OAAI,MAAA,SAAgB;AACpB,SAAA,sBAA4B,iBAAiB;AAC3C,UAAA,sBAA4B;AACvB,SAAK,SAAS,CAAC,YAAY,KAAA,EAAU;MACzC,EAAE;;;;;;CAOT,wBAA8B;AAC5B,MAAI,MAAA,uBAA6B,MAAM;AACrC,gBAAa,MAAA,oBAA0B;AACvC,SAAA,sBAA4B;;;;;;;;CAWhC,YAAsC;AACpC,SAAO,MAAA;;;;;;;;;CAUT,gBACE,UACY;AACZ,QAAA,gBAAsB,IAAI,SAAS;AACnC,WAAS,MAAA,OAAa;AACtB,eAAa;AACX,SAAA,gBAAsB,OAAO,SAAS;;;;;;CAS1C,yBAAiE;EAC/D,MAAM,SAAU,MAAA,QAAc,iBAC3B,EAAE;AAgBL,SAAO;GACL;GACA,UAjBe,yBACf,QACA,MAAA,YACD;GAeC,WAAW,EAAE;GACb,YAAY,EAAE;GACd,WAAW,KAAA;GACX,WAAW;GACX,iBATA,MAAA,mBAAyB,QACzB,CAAC,MAAA,qBAA2B,IAAI,MAAA,gBAAsB;GAStD,OAAO,KAAA;GACP,UAAU,MAAA;GACX;;;;;;;;;;;;;;;;;;;;;;;CAwBH,cAAc,UAAkB,gBAAgB,OAAqB;AACnE,MAAI,MAAA,UAAgB,KAAM,QAAO,MAAA;AACjC,QAAA,SAAe,MAAA,QAAc,OAAO,QAAQ,OAAO,UAAU;GAC3D,aAAa,MAAA,QAAc;GAC3B,WAAW,MAAA,QAAc;GACzB,OAAO,MAAA,QAAc;GACrB,kBAAkB,MAAA,QAAc;GACjC,CAAC;AACF,OAAK,SAAS,KAAK,MAAA,OAAa;AAChC,MAAI,eAAe;AAKjB,SAAA,gBAAsB,QAAQ,SAAS;AACvC,SAAA,mBAAyB;QAEzB,OAAA,cAAoB,MAAA,OAAa;AAEnC,QAAA,uBAA6B;AAC7B,SAAO,MAAA;;;;;;;;;;CAWT,yBAA+B;AAC7B,MAAI,CAAC,MAAA,iBAAwB;AAC7B,MAAI,MAAA,UAAgB,KAAM;AAC1B,QAAA,mBAAyB;AACzB,QAAA,cAAoB,MAAA,OAAa;;;;;;;;;;;;;;;;;;;;;CAsBnC,2BAAiC;AAC/B,MAAI,CAAC,MAAA,iBAAwB;AAC7B,QAAA,mBAAyB;AACpB,QAAA,gBAAsB;;;;;CAM7B,OAAA,iBAAuC;EACrC,MAAM,SAAS,MAAA;AACf,QAAA,SAAe,KAAA;AACf,OAAK,SAAS,KAAK,KAAA,EAAU;AAC7B,QAAA,0BAAgC;AAChC,QAAA,yBAA+B,KAAA;;;;;;AAM/B,QAAA,mBAAyB,OAAO,MAAA,iBAAuB,SAAS;AAChE,QAAA,mBAAyB,OAAO,MAAA,qBAA2B;AAC3D,MAAI;AACF,SAAM,MAAA,kBAAwB,aAAa;UACrC;AAGR,QAAA,mBAAyB,KAAA;AACzB,QAAA,gBAAsB,KAAA;AAItB,QAAA,mBAAyB;AACzB,MAAI;AACF,SAAM,MAAA;UACA;AAGR,QAAA,WAAiB,KAAA;AAGjB,QAAA,aAAmB,OAAO;AAC1B,QAAA,oBAA0B,IAAI,mBAAmB;AACjD,QAAA,iBAAuB,OAAO;AAC9B,QAAA,UAAgB,OAAO;AACvB,QAAA,UAAgB,OAAO;AACvB,QAAA,mBAAyB,OAAO;AAChC,QAAA,cAAoB,KAAA;AACpB,QAAA,gBAAsB;AACtB,QAAA,gBAAsB,OAAO;AAG7B,QAAA,6BAAmC;AACnC,OAAK,WAAW,eACR,YACP;AAED,MAAI,UAAU,MAAM;AAClB,OAAI;AACF,UAAM,OAAO,OAAO;WACd;AAGR,SAAA,uBAA6B;;;;;;CAOjC,4BAAqC;EACnC,MAAM,YAAY,MAAA,QAAc;AAChC,MAAI,cAAc,YAAa,QAAO;AACtC,MAAI,aAAa,QAAQ,cAAc,MAAO,QAAO;AACrD,SAAO,OAAO,UAAU,oBAAoB;;;;;;;CAQ9C,eAAe,QAA4B;AACzC,MAAI,MAAA,YAAkB,KAAM;EAC5B,IAAI;AACJ,QAAA,gBAAsB,IAAI,SAAe,YAAY;AACnD,kBAAe;IACf;;;;;;;;;AAUF,QAAA,yBAA+B,OAAO,SAAS,UAC7C,MAAA,gBAAsB,MAAM,CAC7B;;;;;;;;;AAUD,QAAA,mBAAyB,IAAI,MAAA,iBAAuB,SAAS;AAC7D,QAAA,mBAAyB,IAAI,MAAA,qBAA2B;AAExD,QAAA,YAAkB,YAAY;AAC5B,OAAI;;;;;;;;;;;;;IAaF,MAAM,sBAAsB,OAAO,UAAU;KAC3C,UAAU,CAAC,GAAG,mBAAmB;KACjC,YAAY,CAAC,EAAE,CAAa;KAC5B,OAAO;KACR,CAAC;AACF,QAAI,MAAA,0BAAgC;;;;;;;;;;;AAWlC,yBAAqB;AACnB,qBAAgB;AAChB,oBAAe,KAAA;MACf;IAEJ,MAAM,eAAe,MAAM;AAC3B,oBAAgB;AAChB,mBAAe,KAAA;AACf,UAAA,mBAAyB;;;;;;;;;;;AAWzB,WAAO,CAAC,MAAA,UAAgB;AACtB,gBAAW,MAAM,SAAS,cAAc;AACtC,UAAI,MAAA,SACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BF,UAAI;AACF,aAAA,YAAkB,MAAM;cAClB;;AASV,SAAI,MAAA,SAAgB;AACpB,SAAI,CAAC,aAAa,SAChB;AAEF,WAAM,aAAa,eAAe;;WAE9B;AACN,oBAAgB;AAChB,mBAAe,KAAA;;MAGf;;;;;;;;;;;;;;;CAgBN,iBAAiB,OAAoB;AACnC,MAAI;AACF,SAAA,UAAgB,KAAK,MAAM;UACrB;AAMR,QAAA,UAAgB,KAAK,MAAM;AAC3B,QAAA,iBAAuB,OAAO,MAAM;;;;;;;;;;;AAYpC,QAAA,oBAA0B,MAAM;;;;;;;CAQlC,aAAa,OAAoB;AAC/B,MAAI;AACF,SAAA,UAAgB,KAAK,MAAM;UACrB;;;;;;;;AAeR,MAAI,MAAA,mBAAyB,OAAO,EAClC,MAAK,MAAM,YAAY,MAAA,mBACrB,KAAI;AACF,YAAS,MAAM;UACT;;;;;;;;;;;;;;;;;;;;;;;;;;EAkCZ,MAAM,sBAAsB,wBAAwB,MAAM,OAAO,UAAU;EAC3E,MAAM,6BAA6B,0BACjC,MAAM,OAAO,UACd;AAED,MAAI,MAAM,WAAW,YAAY;AAC/B,OAAI,CAAC,oBACH,OAAA,aAAmB,cAAc,MAAuB;AAE1D;;AAGF,MAAI,MAAM,WAAW,SAAS;AAW5B,OADE,MAAM,OAAO,UAAU,UAAU,KAAK,CAAC,4BACpB;;;;;;;IAOnB,MAAM,WAAW,MAAM,OAAO;AAI9B,QACE,SAAS,UAAU,kBACnB,OAAO,SAAS,iBAAiB,SAEjC,OAAA,aAAmB,wBACjB,MAAM,OAAO,WACb,SAAS,aACV;IAEH,MAAM,KAAK,MAAA,kBAAwB,QAAQ,MAAoB;AAC/D,QAAI,MAAM,KACR,MAAK,UAAU,UAAU,OAAO;KAC9B,GAAG;KACH,WAAW,eAAe,EAAE,WAAW,GAAG;KAC3C,EAAE;;AAGP;;;;;;;;;;;;AAaF,MAAI,MAAM,WAAW,eAAe;GAClC,MAAM,OAAO,MAAM,OAAO;AAK1B,SAAA,gBAAsB,iBAAiB,MAAM,OAAO,WAAW,KAAK;AACpE;;AAKF,MAAI,CADW,gBAAgB,MAAM,OAAO,UAAU,CACzC;AAEb,MAAI,MAAM,WAAW,UAAU;GAC7B,MAAM,cAAc;GACpB,MAAM,qBAAqB,MAAA,gBAAsB,kBAC/C,MAAM,OAAO,UACd;AACD,SAAA,YAAkB,YAAY,OAAO,MAAM,mBAAmB;AAC9D;;AAGF,MAAI,MAAM,WAAW,mBAAmB;AACtC,SAAA,oBAA0B,MAAM;AAChC;;AAGF,MAAI,MAAM,WAAW,YAKA,OAAyB,OAAO;;;;;;;;;;;;;;;;;;;;;;CA2BvD,iBACE,OACkE;AAClE,MAAI,MAAA,QAAc,eAAe,MAAO,QAAO,KAAA;AAC/C,MAAI,SAAS,QAAQ,OAAO,UAAU,YAAY,MAAM,QAAQ,MAAM,CACpE;EAEF,MAAM,WAAW,uBACf,OACA,MAAA,mBACMiE,IAAQ,CACf;EACD,MAAM,YAAY,OAAO,KAAK,SAAS,YAAY;AACnD,MAAI,SAAS,UAAU,WAAW,KAAK,UAAU,WAAW,EAC1D;EAGF,MAAM,gBAAgB,KAAK,UAAU,aAAa,CAAC;EAInD,MAAM,cAAc,UAAU,KAAK,SAAS;GAC1C;GACA,QAAQ,OAAO,UAAU,eAAe,KAAK,eAAe,IAAI;GAChE,WAAW,cAAc;GAC1B,EAAE;AAEH,QAAA,kBAAwB;AACxB,QAAA,aAAmB,iBACjB,SAAS,oBACT,SAAS,YACV;AACD,MAAI,SAAS,UAAU,SAAS,EAC9B,OAAA,gBAAsB,YAAY,SAAS,UAAU;AAEvD,SAAO;GACL,eAAe,SAAS;GACxB,QAAQ;IAAE,WAAW,SAAS;IAAW;IAAa;GACvD;;;;;;;;;;;;;;;;;CAkBH,kBACE,QACA,OACM;EACN,MAAM,SAAS,UAAU,YAAY,UAAU;AAC/C,QAAA,gBAAsB,eACpB,OAAO,WACP,SAAS,WAAW,OACrB;AACD,MAAI,UAAU,aAAa,CAAC,MAAA,gBAC1B,OAAA,aAAmB,iBAAiB,OAAO,YAAY;;CAI3D,aAAa,KAAc,YAAuC;AAChE,MAAI,OAAO,QAAQ,OAAO,QAAQ,YAAY,MAAM,QAAQ,IAAI,CAC9D;EAEF,MAAM,QAAQ;AAGd,QAAA,kBAAwB;;;;;;;;;EASxB,MAAM,qBAAqB,YAAY;AACvC,MAAI,sBAAsB,QAAQ,MAAM,QAAQ,MAAM,MAAA,aAAmB,CACvE,OAAA,gBAAsB,eACpB,MAAM,MAAA,cACN,EAAE,oBAAoB,CACvB;EAEH,MAAM,gBAAgB,MAAM,MAAA;EAC5B,IAAI;EACJ,IAAI;AACJ,MAAI,MAAM,QAAQ,cAAc,EAAE;GAChC,MAAM,UAAU,uBACd,cACD;AACD,gBAAa;IACX,GAAI;KACH,MAAA,cAAoB;IACtB;AACD,kBAAe;SACV;AACL,gBAAa;AACb,kBAAe,EAAE;;AAEnB,QAAA,aAAmB,YAAY,YAAY,cAAc,EACvD,MAAM,YAAY,MACnB,CAAC;AACF,MAAI,aAAa,SAAS,GAAG;AAG3B,SAAA,gBAAsB,eACpB,aACG,KAAK,MAAM,EAAE,GAAG,CAChB,QAAQ,OAAqB,OAAO,OAAO,SAAS,EACvD,OACD;AACD,QAAK,UAAU,UAAU,MAAM;IAC7B,MAAM,YAAY,+BAChB,EAAE,WACF,aACD;AACD,QAAI,cAAc,EAAE,UAAW,QAAO;AACtC,WAAO;KAAE,GAAG;KAAG;KAAW;KAC1B;;;;;;;;;CAUN,qBAAqB,OAAoB;AACvC,MAAI,MAAM,WAAW,kBAAmB;AACxC,MAAI,CAAC,gBAAgB,MAAM,OAAO,UAAU,CAAE;EAC9C,MAAM,OAAO,MAAM,OAAO;EAI1B,MAAM,cAAc,MAAM;AAC1B,MACE,OAAO,gBAAgB,YACvB,MAAA,mBAAyB,IAAI,YAAY,CAEzC;AASF,MACE,MAAA,8BAAoC,QACpC,CAAC,MAAA,2BAAiC,IAAI,YAAY,CAElD;EAEF,MAAM,YAAsC,4BAA4B;GACtE,IAAI;GACJ,OAAO,KAAK;GACb,CAAC;AACF,OAAK,UAAU,UAAU,MAAM;AAC7B,OAAI,EAAE,WAAW,MAAM,UAAU,MAAM,OAAO,YAAY,CAAE,QAAO;GACnE,MAAM,aAAa,CAAC,GAAG,EAAE,YAAY,UAAU;AAC/C,UAAO;IAAE,GAAG;IAAG;IAAY,WAAW,WAAW;IAAI;IACrD;;;;;;CAOJ,kCAAkC,aAA2B;AAC3D,QAAA,mBAAyB,IAAI,YAAY;AACzC,OAAK,UAAU,UAAU,MAAM;GAC7B,MAAM,aAAa,EAAE,WAAW,QAC7B,UAAU,MAAM,OAAO,YACzB;AACD,OACE,WAAW,WAAW,EAAE,WAAW,UACnC,EAAE,WAAW,OAAO,YAEpB,QAAO;AAET,UAAO;IACL,GAAG;IACH;IACA,WAAW,WAAW;IACvB;IACD;;;;;;;;;;;;;;;CAgBJ,mBAAmB,QAGhB;AACD,SAAO,MAAA,kBAAwB,QAAQ,EACrC,6BAA6B,OAC9B,CAAC;;;;;;;;;;;;CAaJ,yBAAyB,QAGtB;AACD,SAAO,MAAA,kBAAwB,QAAQ,EACrC,6BAA6B,MAC9B,CAAC;;CAGJ,mBACE,QACA,SAIC;AACD,SAAO,IAAI,SAAS,YAAY;GAC9B,IAAI,UAAU;GACd,IAAI,aAAa;GACjB,SAAS,OAAO,QAGb;AACD,QAAI,QAAS;AACb,cAAU;AACV,uBAAmB;AACnB,yBAAqB;AACrB,WAAO,oBAAoB,SAAS,cAAc;AAClD,YAAQ,OAAO;;GAEjB,MAAM,sBAAsB,OAAO,EAAE,OAAO,WAAW,CAAC;GACxD,MAAM,WAAW,UAAiB;AAChC,QAAI,QAAS;AACb,QAAI,MAAM,WAAW,YAAa;AAClC,QAAI,CAAC,gBAAgB,MAAM,OAAO,UAAU,CAAE;IAC9C,MAAM,YAAa,MAAyB,OAAO;AAInD,QAAI,WAAW,UAAU,WAAW;AAClC,kBAAa;AACb;;AAEF,QAAI,WAAW,UAAU,YACvB,kBAAiB,OAAO,EAAE,OAAO,aAAa,CAAC,EAAE,EAAE;aAC1C,WAAW,UAAU,SAC9B,kBACQ,OAAO;KAAE,OAAO;KAAU,OAAO,UAAU;KAAO,CAAC,EACzD,EACD;aACQ,WAAW,UAAU,eAAe;AAC7C,SAAI,QAAQ,+BAA+B,CAAC,WAC1C;AAEF,sBAAiB,OAAO,EAAE,OAAO,eAAe,CAAC,EAAE,EAAE;;;GAGzD,MAAM,kBAAkB,MAAA,QAAc,UAAU,QAAQ;GACxD,MAAM,oBAAoB,MAAA,QAAc,QAAQ,QAAQ;AACxD,OAAI,OAAO,QACT,gBAAe;OAEf,QAAO,iBAAiB,SAAS,eAAe,EAAE,MAAM,MAAM,CAAC;IAEjE;;;;;;;;CASJ,2BAA2B,QAA4C;EACrE,MAAM,SAAS,MAAA;AACf,MAAI,UAAU,KAAM,QAAO;AAC3B,SAAO,wCACL,QACA,OAAO,YACP,MAAA,mBACD;;;;;CAMH,yBAA+B;AAC7B,OAAK,MAAM,YAAY,MAAA,gBAAuB,UAAS,MAAA,OAAa;;;;;;;;;AAYxE,SAAS,uBACP,OACS;AACT,KAAI,SAAS,KAAM,QAAO;AAC1B,QACE,MAAM,UAAU,WAAW,KAAK,MAAM,UAAU,OAAO,SAAS,MAAM;;AAI1E,SAAS,0BACP,WACoB;AACpB,KAAI,UAAU,WAAW,EAAG,QAAO,KAAA;CACnC,MAAM,UAAU,UAAU;AAC1B,KAAI,CAAC,QAAQ,WAAW,SAAS,CAAE,QAAO,KAAA;CAC1C,MAAM,KAAK,QAAQ,MAAM,EAAgB;AACzC,QAAO,GAAG,SAAS,IAAI,KAAK,KAAA;;;;;;;;AAS9B,SAAS,yBACP,QACA,aACe;CACf,MAAM,MAAM,OAAO;AACnB,KAAI,CAAC,MAAM,QAAQ,IAAI,CAAE,QAAO,EAAE;AAClC,QAAO,uBACL,IACD;;AAGH,SAAS,qCACP,QACA,aACsB;CACtB,IAAI,MAAM,OAAO;AACjB,KAAI,CAAC,MAAM,QAAQ,IAAI,IAAI,gBAAgB,WACzC,OAAM,OAAO;AAEf,KAAI,CAAC,MAAM,QAAQ,IAAI,CAAE,QAAO;AAChC,QAAO,uBACL,IACD"}
1
+ {"version":3,"file":"controller.js","names":["#options","#messagesKey","#subagents","#subgraphs","#messageMetadata","#resolvedInterrupts","#selfCreatedThreadIds","#namespaceResolves","#scopedHistorySeeds","#rootEventListeners","#rootBus","#threadListeners","#currentThreadId","#trySeedProjectionFromHistory","#createInitialSnapshot","#rootMessages","#lifecycleLoading","#disposed","#submitter","#ensureThread","#startDeferredRootPump","#abandonDeferredRootPump","#rootPumpReady","#awaitNextTerminal","#awaitResumedRunTerminal","#hydratedActiveInterruptIds","#submitGeneration","#markLocalRunStart","#notifyCreated","#notifyCompleted","#markLocalRunEnd","#beginOptimistic","#settleOptimistic","#hydrationPromise","#createHydrationPromise","#resolveHydration","#rejectHydration","#fetchHydrationState","#discoverySeedPromise","#resetHydrationPromise","#teardownThread","#applyValues","#seedDiscoveryFromHistory","#primeScopedHistorySeedsFromHistory","#rootPumpDeferred","#skipDefaultSubagentProjection","#getScopedHistorySeed","#activeRunId","#localRunDepth","#runLifecycleListener","#thread","#resolveInterruptForResume","#resolveDispatchUpdate","#markInterruptResolvedInRootStore","#cancelPendingDispose","#pendingDisposeTimer","#startRootPump","#notifyThreadListeners","#threadEventUnsubscribe","#rootSubscription","#rootPump","#rootToolAssembler","#onWildcardEvent","#usesEventStreamTransport","#onRootEvent","#recordRootInterrupt","uuidv7","#sawValuesForRun","#awaitRootTerminal"],"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 { v7 as uuidv7 } from \"@langchain/core/utils/uuid\";\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, ThreadState } 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 type { AssembledToolCall } from \"../client/stream/handles/tools.js\";\nimport { normalizeInterruptForClient } from \"../ui/interrupts.js\";\nimport { normalizeHitlResponseForServer } from \"../ui/hitl-interrupt-payload.js\";\nimport type { Message } from \"../types.messages.js\";\nimport { NAMESPACE_SEPARATOR } from \"./constants.js\";\nimport { StreamStore } from \"./store.js\";\nimport { ChannelRegistry } from \"./channel-registry.js\";\nimport { ensureMessageInstances } from \"./message-coercion.js\";\nimport {\n SubagentDiscovery,\n type SubagentMap,\n SubgraphDiscovery,\n type SubgraphMap,\n type SubgraphByNodeMap,\n} from \"./discovery/index.js\";\nimport {\n collectSubgraphHostNamespaces,\n getHistoryPage,\n mapSubagentNamespaces,\n resolveSubagentNamespaces,\n} from \"./discovery/namespace-from-history.js\";\nimport type { SubagentDiscoverySnapshot } from \"./types.js\";\nimport {\n isInternalWorkNamespace,\n isLegacySubagentNamespace,\n isRootNamespace,\n namespaceKey,\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 prepareOptimisticInput,\n serializeUpdateMessages,\n type OptimisticHandle,\n} from \"./optimistic-input.js\";\nimport {\n EMPTY_QUEUE,\n SubmitCoordinator,\n type SubmissionQueueEntry,\n type SubmissionQueueSnapshot,\n} from \"./submit-coordinator.js\";\nimport {\n reconcileToolCallsFromMessages,\n seedToolCallsFromMessages,\n upsertToolCall,\n} from \"./tool-calls.js\";\nimport { resolveInterruptTargetForHeadlessResume } from \"../headless-tools.js\";\nimport type {\n RootEventBus,\n RootSnapshot,\n RunExecutionReason,\n StreamControllerOptions,\n StreamRespondAllOptions,\n StreamRespondOptions,\n StreamStopOptions,\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\ninterface ScopedHistorySeed {\n readonly messages: BaseMessage[];\n readonly toolCalls: AssembledToolCall[];\n}\n\n/**\n * Decide whether a hydrated thread is *active* (a run is executing or\n * paused awaiting resume) from the `getState()` snapshot alone — no\n * extra request.\n *\n * Why this gate exists: a finished thread does not need either of the\n * always-on SSE pumps. Subagent/subgraph cards are already seeded from\n * the `getState()` messages and a single bounded `getHistory()` page, so\n * opening the depth-1 content pump + the wildcard lifecycle watcher only\n * to replay a completed run and then idle forever is pure waste. We open\n * the pumps eagerly only when the thread is active; otherwise they come\n * up on the first local `submit()` (the existing deferred-pump path) or\n * a thread swap that lands on an active thread.\n *\n * The gate is deliberately conservative: we only conclude *idle* when\n * the state proves it. A thread is treated as active unless `next` is a\n * present, empty array AND no task carries a pending interrupt:\n * - `next` missing / not an array: unknown shape (a server or custom\n * client may omit it). Treat as active so an already-running\n * server-side run is still observed on reconnect — never silently\n * disable streaming on an unfamiliar `getState` shape.\n * - `next.length > 0`: the checkpoint still has nodes to execute, i.e.\n * a run is mid-flight or paused at an interrupt.\n * - `next` is `[]` but a `tasks[].interrupts` is non-empty: the thread\n * is interrupted and a resume (which starts a run) must be observable.\n * - `next` is `[]` and no pending interrupts: a completed run → idle.\n */\nfunction isThreadStateActive(\n state: { next?: unknown; tasks?: unknown } | null | undefined\n): boolean {\n if (state == null) return true;\n // Only a present, empty `next` array proves \"no nodes pending\". A\n // missing/non-array `next` is an unknown shape → assume active.\n if (!Array.isArray(state.next)) return true;\n if (state.next.length > 0) return true;\n if (Array.isArray(state.tasks)) {\n for (const task of state.tasks) {\n const interrupts = (task as { interrupts?: unknown } | null)?.interrupts;\n if (Array.isArray(interrupts) && interrupts.length > 0) return true;\n }\n }\n return false;\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 /**\n * In-flight per-subagent namespace resolutions, keyed by tool-call\n * id. De-dupes concurrent {@link resolveSubagentNamespace} calls so\n * re-renders / multiple consumers of the same subagent don't issue\n * parallel `getHistory` walks.\n */\n readonly #namespaceResolves = new Map<string, Promise<void>>();\n /**\n * In-flight hydrate-time discovery seed ({@link #seedDiscoveryFromHistory}):\n * a single bounded `getHistory` page that bulk-promotes every\n * still-default subagent namespace and seeds subgraph hosts. Per-card\n * {@link resolveSubagentNamespace} calls await this shared promise\n * instead of each firing their own `getHistory` walk, so opening N\n * cards right after reconnect costs one history read, not N. Re-armed\n * per hydrate cycle and cleared once it settles.\n */\n #discoverySeedPromise: Promise<void> | undefined;\n readonly #scopedHistorySeeds = new Map<\n string,\n Promise<ScopedHistorySeed | null>\n >();\n readonly #rootEventListeners = new Set<(event: Event) => void>();\n readonly #rootBus: RootEventBus;\n #activeRunId: string | undefined;\n #localRunDepth = 0;\n /**\n * `true` once a root `values` event has been applied for the current\n * optimistic run. Reset to `false` in {@link #beginOptimistic} and\n * read in {@link #settleOptimistic}: when a run terminates without\n * the server ever echoing a `values` snapshot, optimistically-merged\n * non-message keys are rolled back to their pre-submit values.\n */\n #sawValuesForRun = false;\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 trySeedFromHistory: (params) =>\n this.#trySeedProjectionFromHistory(params),\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 awaitResumedRunTerminal: (signal) =>\n this.#awaitResumedRunTerminal(signal),\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 beginOptimistic: (input) => this.#beginOptimistic(input),\n settleOptimistic: (handle, event) =>\n this.#settleOptimistic(handle, event),\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 * Load thread state for hydration, preferring the active custom\n * adapter's `getState()` when present.\n */\n async #fetchHydrationState(): Promise<ThreadState<StateType> | null> {\n const threadId = this.#currentThreadId;\n if (threadId == null) return null;\n\n const transport = this.#options.transport;\n if (\n transport != null &&\n typeof transport === \"object\" &&\n typeof transport.getState === \"function\"\n ) {\n return (await transport.getState<StateType>()) as ThreadState<StateType> | null;\n }\n\n return this.#options.client.threads.getState<StateType>(threadId);\n }\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 // Re-arm per hydrate cycle: a stale seed from a previous thread must\n // not be awaited by this thread's lazy namespace resolves.\n this.#discoverySeedPromise = undefined;\n this.#scopedHistorySeeds.clear();\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 // Default active so a getState error / non-404 failure never\n // silently disables streaming — the pumps open eagerly as before.\n // Flipped to the real signal once we have the state in hand.\n let threadActive = true;\n try {\n const state = await this.#fetchHydrationState();\n threadExists = state != null;\n threadActive = isThreadStateActive(state);\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 /**\n * Carry the checkpoint `step` from `getState()` metadata so the\n * root message projection treats this seed as the authoritative\n * latest superstep. The content pump's reconnect replay emits\n * older checkpoints (lower step); marking the seed's step lets\n * the projection reject those as stale instead of letting them\n * remove the seeded message tail (the final assistant turn).\n */\n const seedStep = (state.metadata as { step?: unknown } | undefined)\n ?.step;\n const syntheticCheckpoint =\n typeof checkpointId === \"string\"\n ? {\n id: checkpointId,\n ...(parentCheckpointId != null\n ? { parent_id: parentCheckpointId }\n : {}),\n ...(typeof seedStep === \"number\" ? { step: seedStep } : {}),\n }\n : undefined;\n this.#applyValues(state.values as unknown, syntheticCheckpoint);\n\n /**\n * Seed subagent discovery from checkpoint messages so deep-agent\n * cards render on refresh without waiting for SSE replay. Zero\n * extra HTTP (reuses the `getState` payload); mirrors the\n * interrupt-seeding below. `#subagents` was cleared in\n * `#teardownThread`, and `seedFromCheckpointMessages` is\n * idempotent, so this is safe on re-hydrate.\n */\n const seedMessages = (state.values as Record<string, unknown>)[\n this.#messagesKey\n ];\n if (Array.isArray(seedMessages)) {\n this.#subagents.seedFromCheckpointMessages(seedMessages);\n\n /**\n * An idle (finished) thread defers its root SSE pump; the first\n * `submit()` brings it up and the transport replays the finished\n * run from `seq=0`. Seal the seeded message ids so that replay's\n * `messages` channel deltas can't downgrade the already-complete\n * tail to empty partials (a visible \"messages replay\"). Only safe\n * for idle threads, where every seeded message is final — an\n * active thread's tail may still be streaming and must keep\n * receiving deltas. New ids from the next run are never sealed,\n * and the seal lifts once a newer checkpoint advances the\n * timeline.\n */\n if (!threadActive) {\n const sealedIds = (seedMessages as Array<{ id?: unknown }>)\n .map((message) => message?.id)\n .filter((id): id is string => typeof id === \"string\");\n if (sealedIds.length > 0) {\n this.#rootMessages.sealMessageIds(sealedIds);\n }\n }\n }\n }\n /**\n * Converge to server truth: drop any optimistic messages the\n * server state does not contain (`pending` / `failed` that were\n * never persisted — e.g. a failed run's user message). Echoed\n * ids were flipped to `\"sent\"` by `#applyValues` above and so are\n * excluded from `unpersistedOptimisticIds()`.\n */\n const unpersisted = this.#messageMetadata.unpersistedOptimisticIds();\n if (unpersisted.size > 0) {\n this.#rootMessages.dropOptimisticMessages(unpersisted);\n this.#messageMetadata.forget(unpersisted);\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 normalizeInterruptForClient({\n id,\n value: typed?.value as InterruptType,\n })\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 * Open the shared subscription on mount so in-flight server-side\n * runs are observed even when no local `submit()` is active — BUT\n * only when the thread is actually active (see\n * {@link isThreadStateActive}). A finished thread's cards are seeded\n * from `getState()` + the bounded `getHistory()` below, so opening\n * the depth-1 content pump just to replay a completed run and idle\n * forever is pure waste. When idle we take the deferred path: the\n * pump (and watcher) come up on the first local `submit()` via\n * {@link #startDeferredRootPump}, exactly like a self-created thread.\n * The transport replays from `seq=0` on the deferred subscribe, so\n * nothing is missed.\n */\n const thread = this.#ensureThread(this.#currentThreadId, !threadActive);\n\n /**\n * Start the wildcard lifecycle watcher up-front for existing,\n * active threads. The root content pump runs at `depth: 1`, which\n * covers root-namespace and one-deep events but not arbitrarily-\n * nested subagent / subgraph lifecycle — the dedicated watcher\n * handles those.\n *\n * Skipped when:\n * - the thread is idle/finished — there are no live events to\n * watch; discovery is seeded from history below, and the watcher\n * starts with the deferred pump on the first `submit()`.\n * - the thread is self-created (new) — the watcher would 404\n * against a not-yet-existent thread; `submitRun` / `respondInput`\n * call `startLifecycleWatcher` on first submission instead.\n */\n if (threadExists && threadActive) {\n thread.startLifecycleWatcher();\n }\n if (threadExists) {\n /**\n * Seed subgraph discovery and promote subagent execution\n * namespaces from a single bounded `getHistory` page. Subgraph\n * structure is not present in the root checkpoint messages\n * (unlike subagents), so it can only be reconstructed from\n * history. Fire-and-forget — not awaited into the hydration\n * promise, so Suspense / first paint stay unblocked; cards fill\n * in progressively when it resolves.\n *\n * Held in `#discoverySeedPromise` so lazy per-card\n * {@link resolveSubagentNamespace} calls coalesce onto this single\n * read instead of each firing their own `getHistory` walk.\n */\n const seed: Promise<void> = this.#seedDiscoveryFromHistory(\n this.#currentThreadId\n ).finally(() => {\n // Only clear if a later hydrate cycle hasn't re-armed it.\n if (this.#discoverySeedPromise === seed) {\n this.#discoverySeedPromise = undefined;\n }\n });\n this.#discoverySeedPromise = seed;\n }\n }\n\n /**\n * One bounded, non-blocking `getHistory` read at hydrate that seeds\n * subgraph hosts and bulk-promotes still-default subagent execution\n * namespaces. O(1) in requests regardless of subagent/subgraph count.\n */\n async #seedDiscoveryFromHistory(threadId: string): Promise<void> {\n try {\n const history = await getHistoryPage(this.#options.client, threadId, {\n limit: 20,\n });\n // A thread swap (or dispose) during the fetch invalidates this seed.\n if (this.#disposed || this.#currentThreadId !== threadId) return;\n\n this.#primeScopedHistorySeedsFromHistory(threadId, history);\n\n const hosts = collectSubgraphHostNamespaces(history);\n this.#subgraphs.seedFromHistory(hosts);\n\n const defaultOnlyIds = [...this.#subagents.snapshot.values()]\n .filter(namespaceIsDefaultOnly)\n .map((entry) => entry.id);\n if (defaultOnlyIds.length > 0) {\n const map = mapSubagentNamespaces(\n history,\n defaultOnlyIds,\n this.#messagesKey\n );\n for (const [id, segment] of map) {\n this.#subagents.applyExecutionNamespace(id, segment);\n }\n }\n } catch {\n /* non-fatal: SSE replay still reconciles discovery */\n }\n }\n\n /**\n * Lazily resolve a single subagent's execution namespace from\n * checkpoint history. Intended call site: the first scoped\n * `useMessages` / `useToolCalls` mount for a subagent whose namespace\n * is still the default `tools:<toolCallId>`. A fallback for the\n * hydrate-time bulk seed ({@link #seedDiscoveryFromHistory}) — most\n * subagents are already promoted by the time a panel opens.\n *\n * Skips ids already promoted past default-only (SSE replay or a prior\n * resolve). Concurrent calls for the same id share one `getHistory`\n * walk via {@link #namespaceResolves}.\n *\n * @param toolCallId - Parent `task` tool-call id (the subagent's discovery key).\n */\n async resolveSubagentNamespace(toolCallId: string): Promise<void> {\n if (this.#disposed) return;\n const threadId = this.#currentThreadId;\n if (threadId == null) return;\n if (!namespaceIsDefaultOnly(this.#subagents.snapshot.get(toolCallId))) {\n return;\n }\n const inflight = this.#namespaceResolves.get(toolCallId);\n if (inflight != null) return inflight;\n\n const run = (async () => {\n try {\n /**\n * Coalesce onto the hydrate-time discovery seed. That single\n * bounded `getHistory` page bulk-promotes every default-only\n * subagent, so when many cards mount at once (the common\n * reconnect case) they all await this one read instead of each\n * firing their own walk. Re-check after it settles: usually the\n * bulk seed already promoted us and no further fetch is needed.\n */\n const seed = this.#discoverySeedPromise;\n if (seed != null) {\n await seed;\n if (this.#disposed || this.#currentThreadId !== threadId) return;\n if (\n !namespaceIsDefaultOnly(this.#subagents.snapshot.get(toolCallId))\n ) {\n return;\n }\n }\n const map = await resolveSubagentNamespaces(\n this.#options.client,\n threadId,\n [toolCallId],\n { messagesKey: this.#messagesKey }\n );\n if (this.#disposed || this.#currentThreadId !== threadId) return;\n const segment = map.get(toolCallId);\n if (segment != null) {\n this.#subagents.applyExecutionNamespace(toolCallId, segment);\n }\n } catch {\n /* non-fatal: SSE replay still reconciles the namespace */\n } finally {\n this.#namespaceResolves.delete(toolCallId);\n }\n })();\n this.#namespaceResolves.set(toolCallId, run);\n return run;\n }\n\n /**\n * Try to satisfy a scoped selector projection from checkpoint history\n * instead of opening a scoped `/events` replay.\n *\n * This is only valid while the root pump is deferred, which means hydrate\n * has classified the thread as idle/stale. Active and interrupted threads\n * must keep using SSE so ongoing work and resumes are observed. For an idle\n * thread, though, a late-mounted subagent card only needs the latest scoped\n * checkpoint snapshot; opening `/events` just asks the server to replay work\n * that already finished and can be slow for namespaces discovered from\n * history.\n *\n * Returns `true` when the projection was handled without `/events`. That can\n * mean either the store was seeded from namespace-specific history, or the\n * projection targeted a default subagent namespace that should be skipped\n * because hydrate promoted it to its execution namespace. Returns `false`\n * when the caller should fall back to the normal subscription path.\n */\n async #trySeedProjectionFromHistory<T>(params: {\n kind: \"messages\" | \"toolCalls\";\n namespace: readonly string[];\n store: StreamStore<T>;\n }): Promise<boolean> {\n const threadId = this.#currentThreadId;\n if (\n this.#disposed ||\n threadId == null ||\n params.namespace.length === 0 ||\n !this.#rootPumpDeferred ||\n this.#selfCreatedThreadIds.has(threadId)\n ) {\n return false;\n }\n\n if (await this.#skipDefaultSubagentProjection(params.namespace, threadId)) {\n return true;\n }\n if (\n this.#disposed ||\n this.#currentThreadId !== threadId ||\n !this.#rootPumpDeferred\n ) {\n return false;\n }\n\n const seed = await this.#getScopedHistorySeed(threadId, params.namespace);\n if (\n seed == null ||\n this.#disposed ||\n this.#currentThreadId !== threadId ||\n !this.#rootPumpDeferred\n ) {\n return false;\n }\n\n if (await this.#skipDefaultSubagentProjection(params.namespace, threadId)) {\n return true;\n }\n\n if (params.kind === \"messages\") {\n params.store.setValue(seed.messages as T);\n return true;\n }\n params.store.setValue(seed.toolCalls as T);\n return true;\n }\n\n /**\n * Suppress subscriptions for placeholder subagent namespaces once hydrate has\n * resolved the real execution namespace.\n *\n * Deep-agent discovery first creates cards at `tools:<toolCallId>`. The\n * actual worker history usually lives under a different checkpoint namespace\n * such as `tools:<uuid>`, and hydrate resolves that mapping from the bounded\n * root history seed. React/Vue/Svelte/Angular selector effects can mount\n * while that seed is still in flight, so this helper waits for it and then\n * returns `true` when the original placeholder namespace is stale. Returning\n * `true` tells the projection runtime not to open an `/events` subscription\n * for the wrong namespace; the framework will re-render with the promoted\n * card namespace and acquire the real projection.\n */\n async #skipDefaultSubagentProjection(\n namespace: readonly string[],\n threadId: string\n ): Promise<boolean> {\n const toolCallId = defaultSubagentToolCallId(namespace);\n if (toolCallId == null) return false;\n if (!namespaceIsDefaultOnly(this.#subagents.snapshot.get(toolCallId))) {\n return false;\n }\n const seed = this.#discoverySeedPromise;\n if (seed != null) {\n await seed;\n }\n if (this.#disposed || this.#currentThreadId !== threadId) return true;\n return !namespaceIsDefaultOnly(this.#subagents.snapshot.get(toolCallId));\n }\n\n /**\n * Load and cache the latest checkpoint snapshot for one scoped namespace.\n *\n * `useMessages(stream, subagent)` and `useToolCalls(stream, subagent)` often\n * mount together. Both need the same namespace-specific history page, so the\n * controller keeps an in-flight promise per `threadId + checkpoint_ns`.\n * The cache may already be primed by the hydrate-time discovery history page;\n * otherwise this method performs a narrow `checkpoint_ns` read and derives\n * both projection snapshots from that one response:\n *\n * - `messages` are coerced with the stream-local message coercion rules, so\n * serialized `content_blocks` and tool-call metadata hydrate correctly.\n * - `toolCalls` are reconstructed from AI tool calls plus matching\n * ToolMessages, enough for finished/stale card panels without replaying\n * the `tools` channel.\n *\n * Returns `null` when history does not contain usable values, or the request\n * fails. Callers treat that as a signal to fall back to `/events` so custom\n * servers or unusual state shapes still work.\n */\n #getScopedHistorySeed(\n threadId: string,\n namespace: readonly string[]\n ): Promise<ScopedHistorySeed | null> {\n const checkpointNs = namespaceKey(namespace);\n const key = `${threadId}|${checkpointNs}`;\n const existing = this.#scopedHistorySeeds.get(key);\n if (existing != null) return existing;\n\n const seed = (async (): Promise<ScopedHistorySeed | null> => {\n try {\n const history = await getHistoryPage(this.#options.client, threadId, {\n limit: 1,\n checkpoint: { checkpoint_ns: checkpointNs },\n });\n const values = history[0]?.values;\n if (values == null || typeof values !== \"object\") return null;\n const messages = extractAndCoerceMessagesWithFallback(\n values as Record<string, unknown>,\n this.#messagesKey\n );\n if (messages == null) return null;\n return {\n messages,\n toolCalls: seedToolCallsFromMessages(namespace, messages),\n };\n } catch {\n return null;\n }\n })();\n this.#scopedHistorySeeds.set(key, seed);\n return seed;\n }\n\n /**\n * Reuse the hydrate-time discovery history page as scoped projection data\n * when it already contains checkpoint values for a namespace.\n *\n * The discovery read is required to resolve subagent execution namespaces and\n * subgraph hosts. That same page often includes the latest values for those\n * namespaces, so priming `#scopedHistorySeeds` here lets later\n * `useMessages(stream, subagent)` / `useToolCalls(stream, subagent)` mounts\n * hydrate from memory instead of issuing an immediate second `getHistory`\n * request. If a namespace is not present in the bounded page,\n * `#getScopedHistorySeed` still falls back to a targeted `checkpoint_ns`\n * history read.\n */\n #primeScopedHistorySeedsFromHistory(\n threadId: string,\n history: Array<{\n checkpoint?: { checkpoint_ns?: unknown };\n values?: unknown;\n }>\n ): void {\n for (const state of history) {\n const checkpointNs = state.checkpoint?.checkpoint_ns;\n if (typeof checkpointNs !== \"string\" || checkpointNs.length === 0) {\n continue;\n }\n const namespace = checkpointNs\n .split(NAMESPACE_SEPARATOR)\n .filter((segment) => segment.length > 0);\n if (namespace.length === 0) continue;\n const key = `${threadId}|${namespaceKey(namespace)}`;\n if (this.#scopedHistorySeeds.has(key)) continue;\n const values = state.values;\n if (values == null || typeof values !== \"object\") continue;\n const messages = extractAndCoerceMessagesWithFallback(\n values as Record<string, unknown>,\n this.#messagesKey\n );\n if (messages == null) continue;\n this.#scopedHistorySeeds.set(\n key,\n Promise.resolve({\n messages,\n toolCalls: seedToolCallsFromMessages(namespace, messages),\n })\n );\n }\n }\n\n /**\n * Submit input to the active thread.\n *\n * To resume a pending interrupt, use {@link respond} instead.\n *\n * @param input - Input payload for a new run.\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 * Disconnect the client from the active run and mark the controller\n * idle. By default also cancels the run server-side; pass\n * `{ cancel: false }` or call {@link disconnect} to keep the agent\n * running (join/rejoin).\n */\n async stop(options?: StreamStopOptions): Promise<void> {\n const shouldCancel = options?.cancel ?? true;\n if (shouldCancel) {\n const threadId = this.#currentThreadId;\n const runId = this.#activeRunId;\n if (threadId != null && runId != null) {\n try {\n await this.#options.client.runs.cancel(threadId, runId);\n } catch {\n /* server cancel failures must not block client disconnect */\n }\n }\n }\n await this.#submitter.stop();\n }\n\n /**\n * Disconnect the client without cancelling the run server-side.\n * Alias for `stop({ cancel: false })`.\n */\n async disconnect(): Promise<void> {\n return this.stop({ cancel: false });\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 single pending protocol interrupt.\n *\n * When `options.interruptId` is omitted, resolution walks\n * {@link ThreadStream.interrupts `thread.interrupts`} from newest to\n * oldest and picks the first entry whose `interruptId` has not already\n * been resolved by a prior `respond()` call. That entry may be at the\n * root (`namespace: []`) or inside a subgraph (non-empty `namespace`).\n * This is **not** the same as {@link RootSnapshot.interrupts\n * `rootStore.interrupts[0]`} / framework `stream.interrupt`, which only\n * mirrors root-namespace interrupts for UI convenience.\n *\n * Omitting `interruptId` is fine when exactly one interrupt is pending.\n * When several can be active (parallel subagents, fan-out, nested\n * graphs), pass an explicit `interruptId` (and `namespace` for subgraph\n * interrupts) so you resume the interrupt the user acted on.\n *\n * To resume several interrupts pending at the same checkpoint in one\n * command, use {@link respondAll} — sequential single `respond()` calls\n * would not work, since the first resume starts a run, leaving the\n * others with no interrupted run to respond to.\n *\n * The server validates `namespace` against the pending interrupt. Root\n * interrupts use `namespace: []` (the default when `namespace` is\n * omitted). Subgraph interrupts require the exact tuple from\n * `getThread()?.interrupts` — see the example below.\n *\n * @param response - Payload sent back to the interrupted namespace.\n * @param options - Optional target (`interruptId` / `namespace`) and\n * run-level `config` / `metadata` folded into the run that services\n * the resume (model/user config, trigger source, test flags, …).\n * Equivalent to the same fields on {@link StreamSubmitOptions}.\n *\n * @example Single pending interrupt (safe to omit a target)\n * ```ts\n * await controller.respond({ approved: true });\n * ```\n *\n * @example Carry run config / metadata onto the resume\n * ```ts\n * await controller.respond(\n * { approved: true },\n * { config: { configurable: { model: \"gpt-4o\" } }, metadata: { source: \"ui\" } },\n * );\n * ```\n *\n * @example Multiple root interrupts — target by id\n * ```tsx\n * for (const intr of stream.interrupts) {\n * await stream.respond(decide(intr.value), { interruptId: intr.id! });\n * }\n * ```\n *\n * @example Subgraph interrupt — read `namespace` from the thread stream\n * ```tsx\n * const thread = stream.getThread();\n * for (const entry of thread?.interrupts ?? []) {\n * await stream.respond(buildResponse(entry.payload), {\n * interruptId: entry.interruptId,\n * namespace: entry.namespace,\n * });\n * }\n * ```\n *\n * Each {@link InterruptPayload} on `thread.interrupts` mirrors an\n * `input.requested` event: `{ interruptId, payload, namespace }`.\n * Nested interrupts may appear here but not on `stream.interrupts`.\n */\n async respond(\n response: unknown,\n options?: StreamRespondOptions<ConfigurableType>\n ): Promise<void> {\n if (this.#disposed || this.#thread == null) {\n throw new Error(\"No active thread to respond to.\");\n }\n\n const resolved =\n options?.interruptId != null\n ? {\n interruptId: options.interruptId,\n namespace: options.namespace ?? [...ROOT_NAMESPACE],\n }\n : this.#resolveInterruptForResume();\n if (resolved == null) {\n throw new Error(\"No pending interrupt to respond to.\");\n }\n const thread = this.#thread;\n\n // Apply the state `update` optimistically, mirroring `submit()`: append its\n // messages to the root projection and mint stable ids so the resumed run's\n // echo reconciles by id. Without this the interrupt is cleared the instant\n // `respond()` dispatches while the pushed messages only reappear after a\n // server round-trip — so a HITL \"card\" pushed via `update` would vanish for\n // that window (the flicker). The id-injected payload is what we dispatch,\n // so the server echoes the same ids back and `#applyValues` flips them\n // `pending` → `sent` in place (no duplicate, no gap).\n const prepared =\n options?.update != null\n ? this.#beginOptimistic(options.update)\n : undefined;\n const dispatchUpdate = this.#resolveDispatchUpdate(\n options?.update,\n prepared\n );\n\n try {\n // Route through the coordinator so a resumed run that fails (e.g. a\n // missing model key surfaced after the user answers) lands in the\n // reactive `rootStore.error` slot, exactly like a `submit()` failure.\n // The dispatch (`respondInput` + interrupt-resolved bookkeeping) is\n // what's awaited; the resumed run's terminal is watched in the\n // background (see {@link SubmitCoordinator.dispatchResume}), which also\n // settles the optimistic handle (rolls back un-echoed keys on failure).\n await this.#submitter.dispatchResume(async () => {\n await thread.respondInput({\n namespace: resolved.namespace,\n interrupt_id: resolved.interruptId,\n response: normalizeHitlResponseForServer(response),\n // Fold an optional state update / directed jump into the same\n // superstep as the resume (HITL \"push card into state + resume\").\n // Omitted when absent so the server still sees a plain resume.\n // `BaseMessage` instances under the messages key are serialized to\n // plain dicts (like `submit()`) so they coerce server-side.\n ...(dispatchUpdate != null ? { update: dispatchUpdate } : {}),\n ...(options?.goto != null ? { goto: options.goto } : {}),\n config: options?.config,\n metadata: options?.metadata,\n });\n this.#markInterruptResolvedInRootStore(resolved.interruptId);\n }, prepared?.handle);\n } catch (error) {\n if (this.#disposed && isAbortLikeError(error)) {\n return;\n }\n throw error;\n }\n }\n\n /**\n * Resume several pending interrupts at the same checkpoint in a single\n * command.\n *\n * Required when a run pauses on multiple interrupts simultaneously\n * (e.g. parallel tool-authorization prompts): a single\n * `Command({ resume })` carrying every interrupt's payload resumes them\n * together. Sequential {@link respond} calls would fail because the\n * first resume starts a run, leaving the rest with no interrupted run to\n * respond to.\n *\n * `responsesById` maps each pending `interruptId` to the payload sent\n * back to it, so different interrupts can receive different responses\n * (approve one, deny another). To send the *same* payload to several\n * interrupts, build the map with that value for each id, e.g.\n * `Object.fromEntries(ids.map((id) => [id, response]))`.\n *\n * The server resumes by `interruptId`, so namespaces are resolved\n * internally from `getThread()?.interrupts` and need not be supplied.\n *\n * @param responsesById - Map of pending `interruptId` to its response\n * payload. Must contain at least one entry.\n * @param options - Optional run-level `config` / `metadata` folded into\n * the single run that services the batched resume. Equivalent to the\n * same fields on {@link StreamSubmitOptions}.\n *\n * @example Distinct payloads per interrupt\n * ```tsx\n * await stream.respondAll({\n * [interruptA.id]: { approved: true },\n * [interruptB.id]: { approved: false },\n * });\n * ```\n *\n * @example Same payload to every pending interrupt\n * ```tsx\n * await stream.respondAll(\n * Object.fromEntries(stream.interrupts.map((i) => [i.id!, { approved: true }])),\n * );\n * ```\n */\n async respondAll(\n responsesById: Record<string, unknown>,\n options?: StreamRespondAllOptions<ConfigurableType>\n ): Promise<void> {\n if (this.#disposed || this.#thread == null) {\n throw new Error(\"No active thread to respond to.\");\n }\n const entries = Object.entries(responsesById);\n if (entries.length === 0) {\n throw new Error(\"respondAll() requires at least one response.\");\n }\n const thread = this.#thread;\n const pending = thread.interrupts;\n const responses = entries.map(([interruptId, response]) => ({\n interrupt_id: interruptId,\n response: normalizeHitlResponseForServer(response),\n namespace: pending.find((entry) => entry.interruptId === interruptId)\n ?.namespace ?? [...ROOT_NAMESPACE],\n }));\n // Apply the run-level `update` optimistically (see `respond()` for the\n // rationale): the batched resume's pushed messages paint immediately and\n // reconcile by id when the single servicing run echoes them back.\n const prepared =\n options?.update != null\n ? this.#beginOptimistic(options.update)\n : undefined;\n const dispatchUpdate = this.#resolveDispatchUpdate(\n options?.update,\n prepared\n );\n\n try {\n // See `respond()` — route through the coordinator so the single run\n // that services the batched resume surfaces failures on the reactive\n // `rootStore.error` slot and settles the optimistic handle.\n await this.#submitter.dispatchResume(async () => {\n await thread.respondInput({\n responses,\n // A batched resume services every targeted interrupt in one run, so\n // the update / jump are run-level (not per-entry) — applied once in\n // that run's superstep alongside all the resumes. `BaseMessage`\n // instances under the messages key are serialized to plain dicts\n // (like `submit()`) so they coerce server-side.\n ...(dispatchUpdate != null ? { update: dispatchUpdate } : {}),\n ...(options?.goto != null ? { goto: options.goto } : {}),\n config: options?.config,\n metadata: options?.metadata,\n });\n for (const { interrupt_id: interruptId } of responses) {\n this.#markInterruptResolvedInRootStore(interruptId);\n }\n }, prepared?.handle);\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.#scopedHistorySeeds.clear();\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 step?: 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 /**\n * Apply a submit input optimistically to the root projection before\n * the server responds. Mints stable ids for id-less messages (so the\n * server echo reconciles by id), appends them to the projection, and\n * shallow-merges non-message input keys into `values`.\n *\n * Returns the dispatch payload (id-injected) for the coordinator to\n * send, plus an {@link OptimisticHandle} for terminal reconciliation.\n * Returns `undefined` when optimistic UI is disabled or there is\n * nothing to echo, in which case the coordinator dispatches the raw\n * input unchanged.\n *\n * @param input - Raw input passed to `submit()`.\n */\n #beginOptimistic(\n input: unknown\n ): { dispatchInput: unknown; handle: OptimisticHandle } | undefined {\n if (this.#options.optimistic === false) return undefined;\n if (input == null || typeof input !== \"object\" || Array.isArray(input)) {\n return undefined;\n }\n const prepared = prepareOptimisticInput(\n input as Record<string, unknown>,\n this.#messagesKey,\n () => uuidv7()\n );\n const extraKeys = Object.keys(prepared.extraValues);\n if (prepared.echoedIds.length === 0 && extraKeys.length === 0) {\n return undefined;\n }\n\n const currentValues = this.rootStore.getSnapshot().values as Record<\n string,\n unknown\n >;\n const restoreKeys = extraKeys.map((key) => ({\n key,\n hadKey: Object.prototype.hasOwnProperty.call(currentValues, key),\n prevValue: currentValues[key],\n }));\n\n this.#sawValuesForRun = false;\n // Commit synchronously: this runs inside the user's `submit()` /\n // `respond()` call (before the first await), so the optimistic\n // message lands in the same tick — and therefore the same React /\n // framework commit — as any local UI state the caller flips\n // alongside it. A macrotask-deferred flush would paint the message\n // one tick late, leaving a blink (e.g. a HITL card vanishing between\n // \"form hidden\" and \"resolved card shown\").\n this.#rootMessages.appendOptimistic(\n prepared.optimisticMessages,\n prepared.extraValues,\n { sync: true }\n );\n if (prepared.echoedIds.length > 0) {\n this.#messageMetadata.markPending(prepared.echoedIds);\n }\n return {\n dispatchInput: prepared.dispatchInput,\n handle: { echoedIds: prepared.echoedIds, restoreKeys },\n };\n }\n\n /**\n * Pick the `update` payload to dispatch on a resume (`respond` /\n * `respondAll`).\n *\n * When the optimistic path ran ({@link #beginOptimistic} returned a handle),\n * its `dispatchInput` already carries the minted message ids the server must\n * echo back, so dispatch that — the echo reconciles the optimistic messages\n * by id (no duplicate). Otherwise (optimistic UI disabled, or an `update`\n * with no echoable messages — e.g. the tuple-entry form) fall back to\n * serializing `BaseMessage` instances to dicts, exactly as before. Returns\n * `undefined` when there is no `update`, so the server still sees a plain\n * resume.\n */\n #resolveDispatchUpdate(\n update: Record<string, unknown> | [string, unknown][] | undefined,\n prepared: { dispatchInput: unknown; handle: OptimisticHandle } | undefined\n ): Record<string, unknown> | [string, unknown][] | undefined {\n if (prepared != null) {\n return prepared.dispatchInput as Record<string, unknown>;\n }\n if (update == null) return undefined;\n return serializeUpdateMessages(update, this.#messagesKey);\n }\n\n /**\n * Reconcile optimistic state when a run terminates.\n *\n * - Messages: any echoed id still `\"pending\"` (never echoed by the\n * server) is flipped to `\"sent\"` on success/interrupt, or\n * `\"failed\"` on failure/abort. Ids the server already echoed were\n * flipped to `\"sent\"` in {@link #applyValues} and are untouched.\n * - Non-message keys: rolled back to their pre-submit values when no\n * server `values` event landed during the run (otherwise the\n * server snapshot already reconciled them). Skipped on abort,\n * where a superseding run (or `stop()`) owns subsequent state.\n *\n * @param handle - Handle returned by {@link #beginOptimistic}.\n * @param event - Terminal lifecycle event for the run.\n */\n #settleOptimistic(\n handle: OptimisticHandle,\n event: \"completed\" | \"failed\" | \"interrupted\" | \"aborted\"\n ): void {\n const failed = event === \"failed\" || event === \"aborted\";\n this.#messageMetadata.resolvePending(\n handle.echoedIds,\n failed ? \"failed\" : \"sent\"\n );\n if (event !== \"aborted\" && !this.#sawValuesForRun) {\n this.#rootMessages.restoreValueKeys(handle.restoreKeys);\n }\n }\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 // A root `values` snapshot landed: optimistic non-message keys are\n // now reconciled against server truth (see #settleOptimistic).\n this.#sawValuesForRun = true;\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 step: checkpoint?.step,\n });\n if (nextMessages.length > 0) {\n // Any optimistic message the server just echoed is now\n // server-authoritative: flip its status `pending` → `sent`.\n this.#messageMetadata.resolvePending(\n nextMessages\n .map((m) => m.id)\n .filter((id): id is string => typeof id === \"string\"),\n \"sent\"\n );\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> = normalizeInterruptForClient({\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 * Mark an interrupt resolved for replay filtering and mirror the\n * removal into the root snapshot the framework hooks read.\n */\n #markInterruptResolvedInRootStore(interruptId: string): void {\n this.#resolvedInterrupts.add(interruptId);\n this.rootStore.setState((s) => {\n const interrupts = s.interrupts.filter(\n (entry) => entry.id !== interruptId\n );\n if (\n interrupts.length === s.interrupts.length &&\n s.interrupt?.id !== interruptId\n ) {\n return s;\n }\n return {\n ...s,\n interrupts,\n interrupt: interrupts[0],\n };\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 this.#awaitRootTerminal(signal, {\n skipInterruptedUntilRunning: false,\n });\n }\n\n /**\n * Resolve on the resumed run's root terminal lifecycle.\n *\n * Unlike {@link #awaitNextTerminal}, ignores `interrupted` events until a\n * root `running` lifecycle has been observed. Headless-tool flows can emit\n * a stale `interrupted` for the run being resumed after `input.requested`\n * but before `respondInput` calls `#prepareForNextRun`; accepting that\n * terminal would unsubscribe the watcher before the resumed run's `failed`\n * terminal arrives.\n */\n #awaitResumedRunTerminal(signal: AbortSignal): Promise<{\n event: \"completed\" | \"failed\" | \"interrupted\" | \"aborted\";\n error?: string;\n }> {\n return this.#awaitRootTerminal(signal, {\n skipInterruptedUntilRunning: true,\n });\n }\n\n #awaitRootTerminal(\n signal: AbortSignal,\n options: { skipInterruptedUntilRunning: boolean }\n ): Promise<{\n event: \"completed\" | \"failed\" | \"interrupted\" | \"aborted\";\n error?: string;\n }> {\n return new Promise((resolve) => {\n let settled = false;\n let sawRunning = 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 === \"running\") {\n sawRunning = true;\n return;\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 if (options.skipInterruptedUntilRunning && !sawRunning) {\n return;\n }\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 * True when a subagent still sits on its default `tools:<toolCallId>`\n * namespace — i.e. no execution namespace has been observed (via SSE\n * replay) or resolved (via history) yet. Used to gate lazy namespace\n * resolution so already-promoted subagents aren't re-fetched.\n */\nfunction namespaceIsDefaultOnly(\n entry: SubagentDiscoverySnapshot | undefined\n): boolean {\n if (entry == null) return false;\n return (\n entry.namespace.length === 1 && entry.namespace[0] === `tools:${entry.id}`\n );\n}\n\nfunction defaultSubagentToolCallId(\n namespace: readonly string[]\n): string | undefined {\n if (namespace.length !== 1) return undefined;\n const segment = namespace[0];\n if (!segment.startsWith(\"tools:\")) return undefined;\n const id = segment.slice(\"tools:\".length);\n return id.length > 0 ? id : undefined;\n}\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\nfunction extractAndCoerceMessagesWithFallback(\n values: Record<string, unknown>,\n messagesKey: string\n): BaseMessage[] | null {\n let raw = values[messagesKey];\n if (!Array.isArray(raw) && messagesKey !== \"messages\") {\n raw = values.messages;\n }\n if (!Array.isArray(raw)) return null;\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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"}