@athenaintel/react 0.42.1 → 0.42.2

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 (96) hide show
  1. package/README.md +36 -7
  2. package/dist/agent-runtime/src/index.d.ts +2 -2
  3. package/dist/agent-runtime/src/messages.cjs +38 -7
  4. package/dist/agent-runtime/src/messages.cjs.map +1 -1
  5. package/dist/agent-runtime/src/messages.d.ts +26 -8
  6. package/dist/agent-runtime/src/messages.js +38 -7
  7. package/dist/agent-runtime/src/messages.js.map +1 -1
  8. package/dist/agent-runtime/src/quota-safe-local-storage-session.cjs +10 -0
  9. package/dist/agent-runtime/src/quota-safe-local-storage-session.cjs.map +1 -1
  10. package/dist/agent-runtime/src/quota-safe-local-storage-session.d.ts +7 -0
  11. package/dist/agent-runtime/src/quota-safe-local-storage-session.js +10 -0
  12. package/dist/agent-runtime/src/quota-safe-local-storage-session.js.map +1 -1
  13. package/dist/agent-runtime/src/session-lane-scope.cjs +25 -0
  14. package/dist/agent-runtime/src/session-lane-scope.cjs.map +1 -0
  15. package/dist/agent-runtime/src/session-lane-scope.d.ts +59 -0
  16. package/dist/agent-runtime/src/session-lane-scope.js +25 -0
  17. package/dist/agent-runtime/src/session-lane-scope.js.map +1 -0
  18. package/dist/agent-runtime/src/subagent-transcripts.cjs +2 -2
  19. package/dist/agent-runtime/src/subagent-transcripts.cjs.map +1 -1
  20. package/dist/agent-runtime/src/subagent-transcripts.d.ts +2 -2
  21. package/dist/agent-runtime/src/subagent-transcripts.js +3 -3
  22. package/dist/agent-runtime/src/subagent-transcripts.js.map +1 -1
  23. package/dist/agent-runtime/src/tool-names.cjs +4 -0
  24. package/dist/agent-runtime/src/tool-names.cjs.map +1 -1
  25. package/dist/agent-runtime/src/tool-names.d.ts +7 -0
  26. package/dist/agent-runtime/src/tool-names.js +4 -0
  27. package/dist/agent-runtime/src/tool-names.js.map +1 -1
  28. package/dist/agent-runtime/src/use-deep-agent-runtime.cjs +18 -1
  29. package/dist/agent-runtime/src/use-deep-agent-runtime.cjs.map +1 -1
  30. package/dist/agent-runtime/src/use-deep-agent-runtime.d.ts +19 -10
  31. package/dist/agent-runtime/src/use-deep-agent-runtime.js +19 -2
  32. package/dist/agent-runtime/src/use-deep-agent-runtime.js.map +1 -1
  33. package/dist/agent-runtime/src/use-deep-agent-thread.cjs +28 -10
  34. package/dist/agent-runtime/src/use-deep-agent-thread.cjs.map +1 -1
  35. package/dist/agent-runtime/src/use-deep-agent-thread.d.ts +35 -3
  36. package/dist/agent-runtime/src/use-deep-agent-thread.js +28 -10
  37. package/dist/agent-runtime/src/use-deep-agent-thread.js.map +1 -1
  38. package/dist/agent-runtime/src/use-deep-agent-voice.cjs +20 -0
  39. package/dist/agent-runtime/src/use-deep-agent-voice.cjs.map +1 -1
  40. package/dist/agent-runtime/src/use-deep-agent-voice.js +20 -0
  41. package/dist/agent-runtime/src/use-deep-agent-voice.js.map +1 -1
  42. package/dist/agent-runtime/src/voice-presentation.cjs +2 -0
  43. package/dist/agent-runtime/src/voice-presentation.cjs.map +1 -1
  44. package/dist/agent-runtime/src/voice-presentation.js +2 -0
  45. package/dist/agent-runtime/src/voice-presentation.js.map +1 -1
  46. package/dist/catalog/runtime-catalog.js +1 -1
  47. package/dist/chat/AthenaChat.cjs +125 -103
  48. package/dist/chat/AthenaChat.cjs.map +1 -1
  49. package/dist/chat/AthenaChat.d.ts +9 -0
  50. package/dist/chat/AthenaChat.js +126 -104
  51. package/dist/chat/AthenaChat.js.map +1 -1
  52. package/dist/chat/chat-voice-controls.cjs +128 -0
  53. package/dist/chat/chat-voice-controls.cjs.map +1 -0
  54. package/dist/chat/chat-voice-controls.d.ts +19 -0
  55. package/dist/chat/chat-voice-controls.js +128 -0
  56. package/dist/chat/chat-voice-controls.js.map +1 -0
  57. package/dist/chat/use-statewire-approval-request.js +1 -1
  58. package/dist/chat-ui/composer/tiptap-composer.cjs +42 -22
  59. package/dist/chat-ui/composer/tiptap-composer.cjs.map +1 -1
  60. package/dist/chat-ui/composer/tiptap-composer.d.ts +1 -0
  61. package/dist/chat-ui/composer/tiptap-composer.js +43 -23
  62. package/dist/chat-ui/composer/tiptap-composer.js.map +1 -1
  63. package/dist/chat-ui/mentions/suggestions/data/api-client.cjs +2 -1
  64. package/dist/chat-ui/mentions/suggestions/data/api-client.cjs.map +1 -1
  65. package/dist/chat-ui/mentions/suggestions/data/api-client.d.ts +3 -1
  66. package/dist/chat-ui/mentions/suggestions/data/api-client.js +2 -1
  67. package/dist/chat-ui/mentions/suggestions/data/api-client.js.map +1 -1
  68. package/dist/chat-ui/mentions/use-account-workspace-id.cjs +29 -51
  69. package/dist/chat-ui/mentions/use-account-workspace-id.cjs.map +1 -1
  70. package/dist/chat-ui/mentions/use-account-workspace-id.d.ts +2 -2
  71. package/dist/chat-ui/mentions/use-account-workspace-id.js +30 -52
  72. package/dist/chat-ui/mentions/use-account-workspace-id.js.map +1 -1
  73. package/dist/chat-ui/thread/composer-send-or-stop.cjs +2 -1
  74. package/dist/chat-ui/thread/composer-send-or-stop.cjs.map +1 -1
  75. package/dist/chat-ui/thread/composer-send-or-stop.d.ts +2 -0
  76. package/dist/chat-ui/thread/composer-send-or-stop.js +2 -1
  77. package/dist/chat-ui/thread/composer-send-or-stop.js.map +1 -1
  78. package/dist/diagnostics/athena-diagnostics.cjs.map +1 -1
  79. package/dist/diagnostics/athena-diagnostics.d.ts +1 -1
  80. package/dist/diagnostics/athena-diagnostics.js.map +1 -1
  81. package/dist/index.d.ts +1 -1
  82. package/dist/react/package.json.cjs +1 -1
  83. package/dist/react/package.json.js +1 -1
  84. package/dist/runtime/statewire-session-key.cjs.map +1 -1
  85. package/dist/runtime/statewire-session-key.d.ts +8 -5
  86. package/dist/runtime/statewire-session-key.js.map +1 -1
  87. package/dist/runtime/useAthenaStatewireRuntime.cjs +5 -2
  88. package/dist/runtime/useAthenaStatewireRuntime.cjs.map +1 -1
  89. package/dist/runtime/useAthenaStatewireRuntime.js +5 -2
  90. package/dist/runtime/useAthenaStatewireRuntime.js.map +1 -1
  91. package/dist/voice/use-chat-voice-enabled.cjs +43 -0
  92. package/dist/voice/use-chat-voice-enabled.cjs.map +1 -0
  93. package/dist/voice/use-chat-voice-enabled.d.ts +9 -0
  94. package/dist/voice/use-chat-voice-enabled.js +43 -0
  95. package/dist/voice/use-chat-voice-enabled.js.map +1 -0
  96. package/package.json +4 -8
@@ -31,15 +31,24 @@ export type DeepAgentThreadElement = ReturnType<typeof useDeepAgentThread>;
31
31
  * host's signed-in user is still loading). See {@link DeepAgentPendingSession}.
32
32
  * - `false`: process-local session (a reload mints a new client).
33
33
  *
34
- * A `local-storage` `key` must name the same lane for the whole life of the
35
- * thread's mount. The statewire session that mounts on a lane keeps the client
36
- * id, lane clocks and journal it loaded there; moved to another key, it carries
37
- * them along, and the record it left keeps the same client id with a stale
38
- * clock and whatever it had journaled. Every later mount that loads that record
39
- * resends its journal and stamps early commands from its clock, at or below the
40
- * watermark the server keeps for the id, so the server absorbs each one as a
41
- * replay and answers `expired`. A host whose principal arrives after mount
42
- * passes `pending` until it does, never a stand-in key.
34
+ * A `local-storage` `key` names one principal's lanes (and one backend's). The
35
+ * statewire session that mounts on a lane keeps the client id, lane clocks and
36
+ * journal it loaded there, and everything in that journal is sent under
37
+ * whatever credential the transport holds at its next attach. So a mounted
38
+ * session never moves to another key: when the key starts naming another
39
+ * principal's lanes for the same thread (another account signed in under a
40
+ * mounted chat), the runtime starts a new lane epoch
41
+ * (`UseDeepAgentThreadOptions['laneEpoch']`). The whole statewire thread
42
+ * remounts on the new principal's lane and attaches afresh, as that lane's own
43
+ * client, with nothing remembered or pooled from before. The previous
44
+ * principal's record stays where it is, with its client id and whatever it had
45
+ * journaled and not yet delivered: that principal's next mount on this browser
46
+ * resends it as them, and the retention sweep retires it otherwise. It is never
47
+ * sent as anyone else. A key for the same principal (a token refresh, a fresh
48
+ * `key` function) and a thread switch keep the lane as it is. A host whose
49
+ * principal arrives after mount passes `pending` until it does, never a
50
+ * stand-in key: moving off a stand-in would be a principal change, a second
51
+ * attach.
43
52
  */
44
53
  export type DeepAgentSessionPersistence = {
45
54
  kind: 'local-storage';
@@ -128,7 +137,7 @@ export interface DeepAgentRunConfigRegistration {
128
137
  */
129
138
  key?: unknown;
130
139
  }
131
- export interface UseDeepAgentRuntimeOptions<TClient extends DeepAgentModelContextHost & DeepAgentThreadStateClientHost> extends Omit<UseDeepAgentThreadOptions, 'storage' | 'sessionStore'> {
140
+ export interface UseDeepAgentRuntimeOptions<TClient extends DeepAgentModelContextHost & DeepAgentThreadStateClientHost> extends Omit<UseDeepAgentThreadOptions, 'storage' | 'sessionStore' | 'laneEpoch'> {
132
141
  /** Host surface tag for the shared fallback error logging (e.g.
133
142
  * `'AthenaDeepAgent'`, `'AthenaSDK'`, `'ChromeStatewire'`). */
134
143
  surface: string;
@@ -3,9 +3,10 @@ import { isStatewireChannelTeardown } from "./errors.js";
3
3
  import { registerDeepAgentRunConfig } from "./model-context.js";
4
4
  import { readPersistedFailedSendStamps, readFailedSendStampsFromRecord, QuotaSafeLocalStorageSession, isStorageQuotaError } from "./quota-safe-local-storage-session.js";
5
5
  import { createDeepAgentSendRecovery } from "./send-recovery.js";
6
+ import { nextSessionLaneEpoch, INITIAL_SESSION_LANE_EPOCH } from "./session-lane-scope.js";
6
7
  import { readStatewireFailedSendLane } from "./thread-extras.js";
7
8
  import { isConfigSyncUnavailableRejection } from "./use-deep-agent-config.js";
8
- import { useDeepAgentThread } from "./use-deep-agent-thread.js";
9
+ import { deepAgentAttachId, useDeepAgentThread } from "./use-deep-agent-thread.js";
9
10
  function pendingSessionAttachKey(attachKey) {
10
11
  return `session-pending:${attachKey ?? ""}`;
11
12
  }
@@ -75,7 +76,22 @@ function useDeepAgentRuntime(options) {
75
76
  failureStampsRef.current = session === false || session.kind === "pending" ? void 0 : session.kind === "local-storage" ? canPersistLocalSession ? () => readPersistedFailedSendStamps(session.key(threadOptions.threadId)) : void 0 : () => readFailedSendStampsFromRecord(loadSessionRecord(session.store));
76
77
  const sessionPending = session !== false && session.kind === "pending";
77
78
  const attachKey = sessionPending ? pendingSessionAttachKey(threadOptions.attachKey) : threadOptions.attachKey;
78
- const attachId = attachKey === void 0 ? threadOptions.threadId : `${threadOptions.threadId}:${attachKey}`;
79
+ const laneKey = session !== false && session.kind === "local-storage" ? session.key : null;
80
+ const [laneEpochState, setLaneEpochState] = useState(
81
+ () => nextSessionLaneEpoch({
82
+ previous: INITIAL_SESSION_LANE_EPOCH,
83
+ threadId: threadOptions.threadId,
84
+ key: laneKey
85
+ })
86
+ );
87
+ const nextLaneEpochState = nextSessionLaneEpoch({
88
+ previous: laneEpochState,
89
+ threadId: threadOptions.threadId,
90
+ key: laneKey
91
+ });
92
+ if (nextLaneEpochState !== laneEpochState) setLaneEpochState(nextLaneEpochState);
93
+ const laneEpoch = nextLaneEpochState.epoch;
94
+ const attachId = deepAgentAttachId({ threadId: threadOptions.threadId, attachKey, laneEpoch });
79
95
  const sendRecovery = useMemo(
80
96
  () => createDeepAgentSendRecovery({
81
97
  lane: () => {
@@ -97,6 +113,7 @@ function useDeepAgentRuntime(options) {
97
113
  const thread = useDeepAgentThread({
98
114
  ...threadOptions,
99
115
  ...attachKey !== void 0 && { attachKey },
116
+ laneEpoch,
100
117
  // Parked without a message (a host suspension, when one applies, wins):
101
118
  // nothing dials and nothing journals until the lane is known. What paints
102
119
  // the first frame is the real attach's to report, not the park's.
@@ -1 +1 @@
1
- {"version":3,"file":"use-deep-agent-runtime.js","sources":["../../../../agent-runtime/src/use-deep-agent-runtime.ts"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react';\nimport { isStatewireChannelTeardown } from './errors';\nimport { type DeepAgentModelContextHost, registerDeepAgentRunConfig } from './model-context';\nimport {\n isStorageQuotaError,\n QuotaSafeLocalStorageSession,\n readFailedSendStampsFromRecord,\n readPersistedFailedSendStamps,\n type SessionStorageDegradation,\n} from './quota-safe-local-storage-session';\nimport { createDeepAgentSendRecovery } from './send-recovery';\nimport type { StatewireTransportOptions } from './statewire-transport';\nimport { readStatewireFailedSendLane } from './thread-extras';\nimport { isConfigSyncUnavailableRejection } from './use-deep-agent-config';\nimport { useDeepAgentThread, type UseDeepAgentThreadOptions } from './use-deep-agent-thread';\n\n/** The thread element `useDeepAgentThread` produces — what the host's `useAui`\n * consumes (`useAui({ thread })`). */\nexport type DeepAgentThreadElement = ReturnType<typeof useDeepAgentThread>;\n\n/**\n * Durable command-lane persistence for the thread — where the lane identity and\n * the commands still awaiting a durable verdict live, so a reload resumes the\n * lane instead of minting a new client.\n *\n * - `local-storage`: browser hosts. Backed by {@link QuotaSafeLocalStorageSession}\n * (the upstream `LocalStorageSession` contract with Athena keys, legacy\n * metadata, and retention). A storage failure after mounting — the origin\n * out of quota after statewire's own sweep, a lockdown-mode write refusal —\n * degrades the session to memory-only persistence and is reported through\n * `onStorageDegraded` (default: one console warning); it never stops the\n * connection. Guarded by a shared availability probe: SSR, storage-blocked\n * iframes, and locked-down browsers keep the session process-local, exactly\n * as before the seam existed, while an origin that is merely full mounts the\n * session and lets it degrade. `key` scopes the stored lane — a thread id is\n * only meaningful against the backend (and, where relevant, principal) that\n * minted it, so include the sync-host URL.\n * - `session-store`: React Native hosts pass a hydrated library store backed\n * by AsyncStorage. Its synchronous `load()` is\n * also the send-recovery freshness gate's view of the persisted failed lane,\n * so a store that persists `failed` gets the same 24 h staleness rule as the\n * browser hosts.\n * - `pending`: the principal the lane is scoped by is not known yet (the\n * host's signed-in user is still loading). See {@link DeepAgentPendingSession}.\n * - `false`: process-local session (a reload mints a new client).\n *\n * A `local-storage` `key` must name the same lane for the whole life of the\n * thread's mount. The statewire session that mounts on a lane keeps the client\n * id, lane clocks and journal it loaded there; moved to another key, it carries\n * them along, and the record it left keeps the same client id with a stale\n * clock and whatever it had journaled. Every later mount that loads that record\n * resends its journal and stamps early commands from its clock, at or below the\n * watermark the server keeps for the id, so the server absorbs each one as a\n * replay and answers `expired`. A host whose principal arrives after mount\n * passes `pending` until it does, never a stand-in key.\n */\nexport type DeepAgentSessionPersistence =\n | {\n kind: 'local-storage';\n key: (threadId: string) => string;\n onStorageDegraded?: (degradation: SessionStorageDegradation) => void;\n }\n | { kind: 'session-store'; store: NonNullable<StatewireTransportOptions['sessionStore']> }\n | DeepAgentPendingSession\n | false;\n\n/**\n * The durable lane's principal is not known yet. A lane belongs to one\n * principal and a mounted session cannot move to another lane (see\n * {@link DeepAgentSessionPersistence}), so nothing is mounted, nothing is\n * journaled and the transport stays parked — no dial — until the host passes\n * the real persistence. The park is an attach identity of its own: the attach\n * that follows makes every per-attach decision afresh (the remembered-snapshot\n * seed, the pooled hello, which the park leaves in the pool) and dials the real\n * lane once, as the thread's first attach. The park carries no connection\n * message, so it reads like a never-started chat rather than a paused one; a\n * host suspension, when one applies, wins.\n */\nexport type DeepAgentPendingSession = { kind: 'pending' };\n\n/**\n * The attach key a pending-lane park runs under. It differs from every key the\n * host passes, so the real attach after it is a new attach identity.\n */\nexport function pendingSessionAttachKey(attachKey: string | number | undefined): string {\n return `session-pending:${attachKey ?? ''}`;\n}\n\n/** Structural view of the browser storage global — this package compiles\n * without the DOM lib (React Native honesty; see `globals.d.ts`). */\ntype StorageGlobals = {\n localStorage?: {\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n };\n navigator?: { locks?: unknown };\n};\n\n/**\n * Whether a localStorage-backed session can persist here. A write probe, not a\n * `typeof` check: storage-blocked iframes (SDK embeds under third-party-storage\n * blocking) throw from the `localStorage` accessor itself, and lockdown-mode\n * origins throw from `setItem` — each of those must mean \"stay process-local\",\n * never a crash at mount. An origin that is merely out of quota still reads\n * and takes locks, so it mounts the session: {@link QuotaSafeLocalStorageSession}\n * degrades that to memory-only persistence and recovers when room frees,\n * where staying process-local would give up cross-tab sharing for the whole\n * page load over a condition that clears.\n *\n * The Web Locks API is probed too: the session storage serializes the shared\n * record across tabs through `navigator.locks` and refuses to mount without it\n * (the same precondition as the vendor `LocalStorageSession`), so an origin\n * with localStorage but no Web Locks — older WebViews, Safari before 15.4 —\n * must also stay process-local instead of throwing at mount.\n */\nexport function canPersistDeepAgentSession(): boolean {\n try {\n const globals = globalThis as StorageGlobals;\n if (!globals.navigator?.locks) return false;\n const storage = globals.localStorage;\n if (!storage) return false;\n const probeKey = '__athena_statewire_session_probe__';\n try {\n storage.setItem(probeKey, probeKey);\n storage.removeItem(probeKey);\n } catch (error) {\n return isStorageQuotaError(error);\n }\n return true;\n } catch {\n return false;\n }\n}\n\n/** A host session store's current record, or `null` when the read throws —\n * the recovery gate must never take the runtime down over a storage fault. */\nfunction loadSessionRecord(\n store: NonNullable<StatewireTransportOptions['sessionStore']>\n): ReturnType<NonNullable<StatewireTransportOptions['sessionStore']>['load']> | null {\n try {\n return store.load();\n } catch {\n return null;\n }\n}\n\n/**\n * The shared tail of every surface's transport error policy: channel teardown\n * is lifecycle noise (logged at debug), as is `config_sync_unavailable` — only\n * `config/*` writes emit it, and the host's config sync already handles it\n * through its own command promise (regionally-pinned workspaces run on\n * local-only config); everything else is a real transport error. Hosts with their own error chain\n * (paywall, telemetry, send-failure stores) end it with this so the fallback\n * logging stays uniform per surface.\n */\nexport function logUnhandledStatewireError(surface: string, error: Error): void {\n if (isStatewireChannelTeardown(error)) {\n console.debug(`[${surface}] statewire channel teardown:`, error);\n return;\n }\n if (isConfigSyncUnavailableRejection(error)) {\n console.debug(`[${surface}] config sync unavailable (handled by config sync):`, error);\n return;\n }\n console.error(`[${surface}] statewire transport error:`, error);\n}\n\n/**\n * The imperative thread-state slice of an assistant-ui client — structural so\n * web and native `useAui` results both satisfy it. The send-recovery layer\n * reads the statewire thread extras through it (the sanctioned imperative\n * pattern: `aui.thread().getState()` inside callbacks/effects, never render).\n */\nexport interface DeepAgentThreadStateClientHost {\n thread: () => { getState: () => { extras?: unknown } };\n subscribe: (listener: () => void) => () => void;\n}\n\n/** Deep-agent runConfig registration on the ambient model context (see\n * {@link registerDeepAgentRunConfig}). */\nexport interface DeepAgentRunConfigRegistration {\n /**\n * Builds the `runConfig` that drives the run. The core pulls it when the\n * model context notifies (mount and each key change); the server reads that\n * synced value at command admission. The per-render ref makes each pull use\n * the latest closure but does not trigger a pull itself.\n */\n get: () => Record<string, unknown>;\n /**\n * Re-registers the provider and notifies model-context sync when this value\n * changes (reference equality). Include getter inputs that can change while\n * mounted; they reach the server only after a key change triggers a pull.\n */\n key?: unknown;\n}\n\nexport interface UseDeepAgentRuntimeOptions<\n TClient extends DeepAgentModelContextHost & DeepAgentThreadStateClientHost,\n> extends Omit<UseDeepAgentThreadOptions, 'storage' | 'sessionStore'> {\n /** Host surface tag for the shared fallback error logging (e.g.\n * `'AthenaDeepAgent'`, `'AthenaSDK'`, `'ChromeStatewire'`). */\n surface: string;\n /** Durable command-lane persistence; defaults to process-local. */\n session?: DeepAgentSessionPersistence;\n /**\n * Builds the host's assistant-ui client from the thread element — pass the\n * platform `useAui` (web hosts from `@assistant-ui/react`, React Native\n * hosts from `@assistant-ui/react-native`; this package imports neither).\n * Called unconditionally on every render, so it MUST be (or call) a hook\n * following the rules of hooks — define it at module scope with a `use*`\n * name: `const useHostClient = (thread: DeepAgentThreadElement) =>\n * useAui({ thread })`.\n */\n useClient: (thread: DeepAgentThreadElement) => TClient;\n /** Registers the deep-agent runConfig on the client's model context. Hosts\n * that register elsewhere (e.g. a bridge component mounted under the\n * provider) omit it. */\n runConfig?: DeepAgentRunConfigRegistration;\n}\n\n/**\n * The configurable deep-agent statewire runtime — the one hook every Athena\n * chat surface (Olympus, `@athenaintel/react` SDK, Chrome extension, Office\n * add-in, mobile) mounts its assistant-ui client through.\n *\n * Composes the pieces every surface previously wired by hand:\n * `useDeepAgentThread` (attach lifecycle, auth parking, lane projection,\n * connection/running observers), durable session persistence with the shared\n * availability guard, transport-level send recovery (failed sends the server\n * never judged auto-retry under their original message id — `send-recovery.ts`),\n * the platform client via `useClient`, and the\n * runConfig model-context registration on provider notifications. Everything\n * host-specific — auth headers, run-config building, error policy, adapters,\n * diagnostics recorders — stays an option, so per-surface behavior is\n * configuration, not a fork.\n */\nexport function useDeepAgentRuntime<\n TClient extends DeepAgentModelContextHost & DeepAgentThreadStateClientHost,\n>(options: UseDeepAgentRuntimeOptions<TClient>): TClient {\n const {\n surface,\n session = false,\n useClient,\n runConfig,\n onError,\n onStateChange,\n onCommandChange,\n ...threadOptions\n } = options;\n\n // Probed once per mount, like every browser host did individually: an origin\n // that cannot persist stays process-local for the mount's lifetime.\n const [canPersistLocalSession] = useState(canPersistDeepAgentSession);\n\n // Per-render refs so the transport never remounts on a new closure identity\n // and always reads the latest policy.\n const onErrorRef = useRef(onError);\n onErrorRef.current = onError;\n const surfaceRef = useRef(surface);\n surfaceRef.current = surface;\n const onStateChangeRef = useRef(onStateChange);\n onStateChangeRef.current = onStateChange;\n const onCommandChangeRef = useRef(onCommandChange);\n onCommandChangeRef.current = onCommandChange;\n const diagnosticsRef = useRef(threadOptions.diagnostics);\n diagnosticsRef.current = threadOptions.diagnostics;\n const clientRef = useRef<TClient | null>(null);\n\n // Where the persisted session record lives, for the recovery freshness gate\n // (read per sweep — undefined means \"no persistence, everything is fresh\").\n const failureStampsRef = useRef<(() => ReadonlyMap<string, number> | null) | undefined>(\n undefined\n );\n failureStampsRef.current =\n session === false || session.kind === 'pending'\n ? undefined\n : session.kind === 'local-storage'\n ? canPersistLocalSession\n ? () => readPersistedFailedSendStamps(session.key(threadOptions.threadId))\n : undefined\n : () => readFailedSendStampsFromRecord(loadSessionRecord(session.store));\n\n // A pending lane parks the thread under an attach identity of its own (see\n // `DeepAgentPendingSession`), so the attach once the lane is known is new.\n const sessionPending = session !== false && session.kind === 'pending';\n const attachKey = sessionPending\n ? pendingSessionAttachKey(threadOptions.attachKey)\n : threadOptions.attachKey;\n\n // Send recovery, keyed by attach identity exactly like the thread's session\n // (a switch or re-attach gets fresh hydration gating and retry budgets). The\n // controller re-dispatches failed sends the server never judged; see\n // `send-recovery.ts` for the contract.\n const attachId =\n attachKey === undefined ? threadOptions.threadId : `${threadOptions.threadId}:${attachKey}`;\n // biome-ignore lint/correctness/useExhaustiveDependencies: keyed by attach identity on purpose; every other input is read through a per-render ref.\n const sendRecovery = useMemo(\n () =>\n createDeepAgentSendRecovery({\n lane: () => {\n const client = clientRef.current;\n if (!client) return null;\n return readStatewireFailedSendLane(client.thread().getState().extras);\n },\n failureStamps: () => failureStampsRef.current?.() ?? null,\n diagnostics: () => diagnosticsRef.current,\n }),\n [attachId]\n );\n useEffect(() => () => sendRecovery.dispose(), [sendRecovery]);\n const sendRecoveryRef = useRef(sendRecovery);\n sendRecoveryRef.current = sendRecovery;\n\n const thread = useDeepAgentThread({\n ...threadOptions,\n ...(attachKey !== undefined && { attachKey }),\n // Parked without a message (a host suspension, when one applies, wins):\n // nothing dials and nothing journals until the lane is known. What paints\n // the first frame is the real attach's to report, not the park's.\n ...(sessionPending && {\n suspended: threadOptions.suspended || {},\n onAttachSource: undefined,\n }),\n // A pooled dial for the thread is the real attach's to take: a park that\n // only waits for the lane neither takes nor discards it. A host suspension\n // still discards it, as it does without a pending lane.\n ...(sessionPending && !threadOptions.suspended && { prewarmPool: undefined }),\n ...(session !== false &&\n session.kind === 'local-storage' &&\n canPersistLocalSession && {\n storage: {\n session: QuotaSafeLocalStorageSession({\n threadId: threadOptions.threadId,\n key: session.key,\n ...(session.onStorageDegraded === undefined\n ? {}\n : { onStorageDegraded: session.onStorageDegraded }),\n }),\n },\n }),\n ...(session !== false && session.kind === 'session-store' && { sessionStore: session.store }),\n onStateChange: (state, ctx) => {\n sendRecoveryRef.current.observeStateChange(state);\n onStateChangeRef.current?.(state, ctx);\n },\n onCommandChange: (update, ctx) => {\n sendRecoveryRef.current.observeCommand(update);\n onCommandChangeRef.current?.(update, ctx);\n },\n onError: (error) => {\n const handler = onErrorRef.current;\n if (handler) {\n handler(error);\n return;\n }\n logUnhandledStatewireError(surfaceRef.current, error);\n },\n });\n\n const client = useClient(thread);\n clientRef.current = client;\n\n // A locked shared journal can publish failed history after onCommandChange,\n // with no canonical state change to wake recovery. Observe the client that\n // owns the visible failed lane; the controller guards in-flight retries.\n useEffect(() => {\n const unsubscribe = client.subscribe(() => sendRecovery.observeFailedLaneChange());\n sendRecovery.observeFailedLaneChange();\n return unsubscribe;\n }, [client, sendRecovery]);\n\n // Commands travel bare, so the runConfig that drives the run rides the\n // ambient model context: `contextSync: 'command-send'` mirrors it to the\n // server, which reads `config.runConfig` at command admission. The core pulls\n // on model-context notifications; the per-render ref ensures each pull uses\n // the latest closure but does not trigger a pull itself.\n const getRunConfigRef = useRef(runConfig?.get);\n getRunConfigRef.current = runConfig?.get;\n const hasRunConfig = runConfig !== undefined;\n const runConfigKey = runConfig?.key;\n useEffect(() => {\n if (!hasRunConfig) return undefined;\n return registerDeepAgentRunConfig(client, () => getRunConfigRef.current?.() ?? {});\n }, [client, hasRunConfig, runConfigKey]);\n\n return client;\n}\n\n"],"names":["client"],"mappings":"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"}
1
+ {"version":3,"file":"use-deep-agent-runtime.js","sources":["../../../../agent-runtime/src/use-deep-agent-runtime.ts"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react';\nimport { isStatewireChannelTeardown } from './errors';\nimport { type DeepAgentModelContextHost, registerDeepAgentRunConfig } from './model-context';\nimport {\n isStorageQuotaError,\n QuotaSafeLocalStorageSession,\n readFailedSendStampsFromRecord,\n readPersistedFailedSendStamps,\n type SessionStorageDegradation,\n} from './quota-safe-local-storage-session';\nimport { createDeepAgentSendRecovery } from './send-recovery';\nimport { INITIAL_SESSION_LANE_EPOCH, nextSessionLaneEpoch } from './session-lane-scope';\nimport type { StatewireTransportOptions } from './statewire-transport';\nimport { readStatewireFailedSendLane } from './thread-extras';\nimport { isConfigSyncUnavailableRejection } from './use-deep-agent-config';\nimport {\n deepAgentAttachId,\n useDeepAgentThread,\n type UseDeepAgentThreadOptions,\n} from './use-deep-agent-thread';\n\n/** The thread element `useDeepAgentThread` produces — what the host's `useAui`\n * consumes (`useAui({ thread })`). */\nexport type DeepAgentThreadElement = ReturnType<typeof useDeepAgentThread>;\n\n/**\n * Durable command-lane persistence for the thread — where the lane identity and\n * the commands still awaiting a durable verdict live, so a reload resumes the\n * lane instead of minting a new client.\n *\n * - `local-storage`: browser hosts. Backed by {@link QuotaSafeLocalStorageSession}\n * (the upstream `LocalStorageSession` contract with Athena keys, legacy\n * metadata, and retention). A storage failure after mounting — the origin\n * out of quota after statewire's own sweep, a lockdown-mode write refusal —\n * degrades the session to memory-only persistence and is reported through\n * `onStorageDegraded` (default: one console warning); it never stops the\n * connection. Guarded by a shared availability probe: SSR, storage-blocked\n * iframes, and locked-down browsers keep the session process-local, exactly\n * as before the seam existed, while an origin that is merely full mounts the\n * session and lets it degrade. `key` scopes the stored lane — a thread id is\n * only meaningful against the backend (and, where relevant, principal) that\n * minted it, so include the sync-host URL.\n * - `session-store`: React Native hosts pass a hydrated library store backed\n * by AsyncStorage. Its synchronous `load()` is\n * also the send-recovery freshness gate's view of the persisted failed lane,\n * so a store that persists `failed` gets the same 24 h staleness rule as the\n * browser hosts.\n * - `pending`: the principal the lane is scoped by is not known yet (the\n * host's signed-in user is still loading). See {@link DeepAgentPendingSession}.\n * - `false`: process-local session (a reload mints a new client).\n *\n * A `local-storage` `key` names one principal's lanes (and one backend's). The\n * statewire session that mounts on a lane keeps the client id, lane clocks and\n * journal it loaded there, and everything in that journal is sent under\n * whatever credential the transport holds at its next attach. So a mounted\n * session never moves to another key: when the key starts naming another\n * principal's lanes for the same thread (another account signed in under a\n * mounted chat), the runtime starts a new lane epoch\n * (`UseDeepAgentThreadOptions['laneEpoch']`). The whole statewire thread\n * remounts on the new principal's lane and attaches afresh, as that lane's own\n * client, with nothing remembered or pooled from before. The previous\n * principal's record stays where it is, with its client id and whatever it had\n * journaled and not yet delivered: that principal's next mount on this browser\n * resends it as them, and the retention sweep retires it otherwise. It is never\n * sent as anyone else. A key for the same principal (a token refresh, a fresh\n * `key` function) and a thread switch keep the lane as it is. A host whose\n * principal arrives after mount passes `pending` until it does, never a\n * stand-in key: moving off a stand-in would be a principal change, a second\n * attach.\n */\nexport type DeepAgentSessionPersistence =\n | {\n kind: 'local-storage';\n key: (threadId: string) => string;\n onStorageDegraded?: (degradation: SessionStorageDegradation) => void;\n }\n | { kind: 'session-store'; store: NonNullable<StatewireTransportOptions['sessionStore']> }\n | DeepAgentPendingSession\n | false;\n\n/**\n * The durable lane's principal is not known yet. A lane belongs to one\n * principal and a mounted session cannot move to another lane (see\n * {@link DeepAgentSessionPersistence}), so nothing is mounted, nothing is\n * journaled and the transport stays parked — no dial — until the host passes\n * the real persistence. The park is an attach identity of its own: the attach\n * that follows makes every per-attach decision afresh (the remembered-snapshot\n * seed, the pooled hello, which the park leaves in the pool) and dials the real\n * lane once, as the thread's first attach. The park carries no connection\n * message, so it reads like a never-started chat rather than a paused one; a\n * host suspension, when one applies, wins.\n */\nexport type DeepAgentPendingSession = { kind: 'pending' };\n\n/**\n * The attach key a pending-lane park runs under. It differs from every key the\n * host passes, so the real attach after it is a new attach identity.\n */\nexport function pendingSessionAttachKey(attachKey: string | number | undefined): string {\n return `session-pending:${attachKey ?? ''}`;\n}\n\n/** Structural view of the browser storage global — this package compiles\n * without the DOM lib (React Native honesty; see `globals.d.ts`). */\ntype StorageGlobals = {\n localStorage?: {\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n };\n navigator?: { locks?: unknown };\n};\n\n/**\n * Whether a localStorage-backed session can persist here. A write probe, not a\n * `typeof` check: storage-blocked iframes (SDK embeds under third-party-storage\n * blocking) throw from the `localStorage` accessor itself, and lockdown-mode\n * origins throw from `setItem` — each of those must mean \"stay process-local\",\n * never a crash at mount. An origin that is merely out of quota still reads\n * and takes locks, so it mounts the session: {@link QuotaSafeLocalStorageSession}\n * degrades that to memory-only persistence and recovers when room frees,\n * where staying process-local would give up cross-tab sharing for the whole\n * page load over a condition that clears.\n *\n * The Web Locks API is probed too: the session storage serializes the shared\n * record across tabs through `navigator.locks` and refuses to mount without it\n * (the same precondition as the vendor `LocalStorageSession`), so an origin\n * with localStorage but no Web Locks — older WebViews, Safari before 15.4 —\n * must also stay process-local instead of throwing at mount.\n */\nexport function canPersistDeepAgentSession(): boolean {\n try {\n const globals = globalThis as StorageGlobals;\n if (!globals.navigator?.locks) return false;\n const storage = globals.localStorage;\n if (!storage) return false;\n const probeKey = '__athena_statewire_session_probe__';\n try {\n storage.setItem(probeKey, probeKey);\n storage.removeItem(probeKey);\n } catch (error) {\n return isStorageQuotaError(error);\n }\n return true;\n } catch {\n return false;\n }\n}\n\n/** A host session store's current record, or `null` when the read throws —\n * the recovery gate must never take the runtime down over a storage fault. */\nfunction loadSessionRecord(\n store: NonNullable<StatewireTransportOptions['sessionStore']>\n): ReturnType<NonNullable<StatewireTransportOptions['sessionStore']>['load']> | null {\n try {\n return store.load();\n } catch {\n return null;\n }\n}\n\n/**\n * The shared tail of every surface's transport error policy: channel teardown\n * is lifecycle noise (logged at debug), as is `config_sync_unavailable` — only\n * `config/*` writes emit it, and the host's config sync already handles it\n * through its own command promise (regionally-pinned workspaces run on\n * local-only config); everything else is a real transport error. Hosts with their own error chain\n * (paywall, telemetry, send-failure stores) end it with this so the fallback\n * logging stays uniform per surface.\n */\nexport function logUnhandledStatewireError(surface: string, error: Error): void {\n if (isStatewireChannelTeardown(error)) {\n console.debug(`[${surface}] statewire channel teardown:`, error);\n return;\n }\n if (isConfigSyncUnavailableRejection(error)) {\n console.debug(`[${surface}] config sync unavailable (handled by config sync):`, error);\n return;\n }\n console.error(`[${surface}] statewire transport error:`, error);\n}\n\n/**\n * The imperative thread-state slice of an assistant-ui client — structural so\n * web and native `useAui` results both satisfy it. The send-recovery layer\n * reads the statewire thread extras through it (the sanctioned imperative\n * pattern: `aui.thread().getState()` inside callbacks/effects, never render).\n */\nexport interface DeepAgentThreadStateClientHost {\n thread: () => { getState: () => { extras?: unknown } };\n subscribe: (listener: () => void) => () => void;\n}\n\n/** Deep-agent runConfig registration on the ambient model context (see\n * {@link registerDeepAgentRunConfig}). */\nexport interface DeepAgentRunConfigRegistration {\n /**\n * Builds the `runConfig` that drives the run. The core pulls it when the\n * model context notifies (mount and each key change); the server reads that\n * synced value at command admission. The per-render ref makes each pull use\n * the latest closure but does not trigger a pull itself.\n */\n get: () => Record<string, unknown>;\n /**\n * Re-registers the provider and notifies model-context sync when this value\n * changes (reference equality). Include getter inputs that can change while\n * mounted; they reach the server only after a key change triggers a pull.\n */\n key?: unknown;\n}\n\nexport interface UseDeepAgentRuntimeOptions<\n TClient extends DeepAgentModelContextHost & DeepAgentThreadStateClientHost,\n> extends Omit<UseDeepAgentThreadOptions, 'storage' | 'sessionStore' | 'laneEpoch'> {\n /** Host surface tag for the shared fallback error logging (e.g.\n * `'AthenaDeepAgent'`, `'AthenaSDK'`, `'ChromeStatewire'`). */\n surface: string;\n /** Durable command-lane persistence; defaults to process-local. */\n session?: DeepAgentSessionPersistence;\n /**\n * Builds the host's assistant-ui client from the thread element — pass the\n * platform `useAui` (web hosts from `@assistant-ui/react`, React Native\n * hosts from `@assistant-ui/react-native`; this package imports neither).\n * Called unconditionally on every render, so it MUST be (or call) a hook\n * following the rules of hooks — define it at module scope with a `use*`\n * name: `const useHostClient = (thread: DeepAgentThreadElement) =>\n * useAui({ thread })`.\n */\n useClient: (thread: DeepAgentThreadElement) => TClient;\n /** Registers the deep-agent runConfig on the client's model context. Hosts\n * that register elsewhere (e.g. a bridge component mounted under the\n * provider) omit it. */\n runConfig?: DeepAgentRunConfigRegistration;\n}\n\n/**\n * The configurable deep-agent statewire runtime — the one hook every Athena\n * chat surface (Olympus, `@athenaintel/react` SDK, Chrome extension, Office\n * add-in, mobile) mounts its assistant-ui client through.\n *\n * Composes the pieces every surface previously wired by hand:\n * `useDeepAgentThread` (attach lifecycle, auth parking, lane projection,\n * connection/running observers), durable session persistence with the shared\n * availability guard, transport-level send recovery (failed sends the server\n * never judged auto-retry under their original message id — `send-recovery.ts`),\n * the platform client via `useClient`, and the\n * runConfig model-context registration on provider notifications. Everything\n * host-specific — auth headers, run-config building, error policy, adapters,\n * diagnostics recorders — stays an option, so per-surface behavior is\n * configuration, not a fork.\n */\nexport function useDeepAgentRuntime<\n TClient extends DeepAgentModelContextHost & DeepAgentThreadStateClientHost,\n>(options: UseDeepAgentRuntimeOptions<TClient>): TClient {\n const {\n surface,\n session = false,\n useClient,\n runConfig,\n onError,\n onStateChange,\n onCommandChange,\n ...threadOptions\n } = options;\n\n // Probed once per mount, like every browser host did individually: an origin\n // that cannot persist stays process-local for the mount's lifetime.\n const [canPersistLocalSession] = useState(canPersistDeepAgentSession);\n\n // Per-render refs so the transport never remounts on a new closure identity\n // and always reads the latest policy.\n const onErrorRef = useRef(onError);\n onErrorRef.current = onError;\n const surfaceRef = useRef(surface);\n surfaceRef.current = surface;\n const onStateChangeRef = useRef(onStateChange);\n onStateChangeRef.current = onStateChange;\n const onCommandChangeRef = useRef(onCommandChange);\n onCommandChangeRef.current = onCommandChange;\n const diagnosticsRef = useRef(threadOptions.diagnostics);\n diagnosticsRef.current = threadOptions.diagnostics;\n const clientRef = useRef<TClient | null>(null);\n\n // Where the persisted session record lives, for the recovery freshness gate\n // (read per sweep — undefined means \"no persistence, everything is fresh\").\n const failureStampsRef = useRef<(() => ReadonlyMap<string, number> | null) | undefined>(\n undefined\n );\n failureStampsRef.current =\n session === false || session.kind === 'pending'\n ? undefined\n : session.kind === 'local-storage'\n ? canPersistLocalSession\n ? () => readPersistedFailedSendStamps(session.key(threadOptions.threadId))\n : undefined\n : () => readFailedSendStampsFromRecord(loadSessionRecord(session.store));\n\n // A pending lane parks the thread under an attach identity of its own (see\n // `DeepAgentPendingSession`), so the attach once the lane is known is new.\n const sessionPending = session !== false && session.kind === 'pending';\n const attachKey = sessionPending\n ? pendingSessionAttachKey(threadOptions.attachKey)\n : threadOptions.attachKey;\n\n // The lane's scope, as the host's `key` names it: whose lanes this mount\n // runs on. A key that starts naming another principal's lanes starts a new\n // lane epoch, which remounts the whole thread on the new lane instead of\n // moving the mounted session there (see `DeepAgentSessionPersistence`).\n // Tracked whether or not this origin can persist: a process-local session\n // holds the previous principal's journal in memory just the same. A pending\n // lane keeps the last known scope, so a sign-out and a sign-in as someone\n // else with a gap between them is still a change.\n const laneKey = session !== false && session.kind === 'local-storage' ? session.key : null;\n const [laneEpochState, setLaneEpochState] = useState(() =>\n nextSessionLaneEpoch({\n previous: INITIAL_SESSION_LANE_EPOCH,\n threadId: threadOptions.threadId,\n key: laneKey,\n })\n );\n const nextLaneEpochState = nextSessionLaneEpoch({\n previous: laneEpochState,\n threadId: threadOptions.threadId,\n key: laneKey,\n });\n // React's derived-state update: re-renders at once with the new epoch, and\n // this render already uses it, so no render pairs the new key with the\n // mounted thread.\n if (nextLaneEpochState !== laneEpochState) setLaneEpochState(nextLaneEpochState);\n const laneEpoch = nextLaneEpochState.epoch;\n\n // Send recovery, keyed by attach identity exactly like the thread's session\n // (a switch, re-attach or new lane epoch gets fresh hydration gating and\n // retry budgets, so it never re-dispatches a previous principal's failed\n // sends). The controller re-dispatches failed sends the server never judged;\n // see `send-recovery.ts` for the contract.\n const attachId = deepAgentAttachId({ threadId: threadOptions.threadId, attachKey, laneEpoch });\n // biome-ignore lint/correctness/useExhaustiveDependencies: keyed by attach identity on purpose; every other input is read through a per-render ref.\n const sendRecovery = useMemo(\n () =>\n createDeepAgentSendRecovery({\n lane: () => {\n const client = clientRef.current;\n if (!client) return null;\n return readStatewireFailedSendLane(client.thread().getState().extras);\n },\n failureStamps: () => failureStampsRef.current?.() ?? null,\n diagnostics: () => diagnosticsRef.current,\n }),\n [attachId]\n );\n useEffect(() => () => sendRecovery.dispose(), [sendRecovery]);\n const sendRecoveryRef = useRef(sendRecovery);\n sendRecoveryRef.current = sendRecovery;\n\n const thread = useDeepAgentThread({\n ...threadOptions,\n ...(attachKey !== undefined && { attachKey }),\n laneEpoch,\n // Parked without a message (a host suspension, when one applies, wins):\n // nothing dials and nothing journals until the lane is known. What paints\n // the first frame is the real attach's to report, not the park's.\n ...(sessionPending && {\n suspended: threadOptions.suspended || {},\n onAttachSource: undefined,\n }),\n // A pooled dial for the thread is the real attach's to take: a park that\n // only waits for the lane neither takes nor discards it. A host suspension\n // still discards it, as it does without a pending lane.\n ...(sessionPending && !threadOptions.suspended && { prewarmPool: undefined }),\n ...(session !== false &&\n session.kind === 'local-storage' &&\n canPersistLocalSession && {\n storage: {\n session: QuotaSafeLocalStorageSession({\n threadId: threadOptions.threadId,\n key: session.key,\n ...(session.onStorageDegraded === undefined\n ? {}\n : { onStorageDegraded: session.onStorageDegraded }),\n }),\n },\n }),\n ...(session !== false && session.kind === 'session-store' && { sessionStore: session.store }),\n onStateChange: (state, ctx) => {\n sendRecoveryRef.current.observeStateChange(state);\n onStateChangeRef.current?.(state, ctx);\n },\n onCommandChange: (update, ctx) => {\n sendRecoveryRef.current.observeCommand(update);\n onCommandChangeRef.current?.(update, ctx);\n },\n onError: (error) => {\n const handler = onErrorRef.current;\n if (handler) {\n handler(error);\n return;\n }\n logUnhandledStatewireError(surfaceRef.current, error);\n },\n });\n\n const client = useClient(thread);\n clientRef.current = client;\n\n // A locked shared journal can publish failed history after onCommandChange,\n // with no canonical state change to wake recovery. Observe the client that\n // owns the visible failed lane; the controller guards in-flight retries.\n useEffect(() => {\n const unsubscribe = client.subscribe(() => sendRecovery.observeFailedLaneChange());\n sendRecovery.observeFailedLaneChange();\n return unsubscribe;\n }, [client, sendRecovery]);\n\n // Commands travel bare, so the runConfig that drives the run rides the\n // ambient model context: `contextSync: 'command-send'` mirrors it to the\n // server, which reads `config.runConfig` at command admission. The core pulls\n // on model-context notifications; the per-render ref ensures each pull uses\n // the latest closure but does not trigger a pull itself.\n const getRunConfigRef = useRef(runConfig?.get);\n getRunConfigRef.current = runConfig?.get;\n const hasRunConfig = runConfig !== undefined;\n const runConfigKey = runConfig?.key;\n useEffect(() => {\n if (!hasRunConfig) return undefined;\n return registerDeepAgentRunConfig(client, () => getRunConfigRef.current?.() ?? {});\n }, [client, hasRunConfig, runConfigKey]);\n\n return client;\n}\n\n"],"names":["client"],"mappings":"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"}
@@ -15,6 +15,14 @@ const statewireState = require("./statewire-state.cjs");
15
15
  const statewireSuspended = require("./statewire-suspended.cjs");
16
16
  const statewireTransport = require("./statewire-transport.cjs");
17
17
  const threadSnapshotCache = require("./thread-snapshot-cache.cjs");
18
+ function deepAgentAttachId({
19
+ threadId,
20
+ attachKey,
21
+ laneEpoch = 0
22
+ }) {
23
+ const attach = attachKey === void 0 ? threadId : `${threadId}:${attachKey}`;
24
+ return laneEpoch === 0 ? attach : `${attach}#lane-epoch:${laneEpoch}`;
25
+ }
18
26
  function readSessionPersisted(state) {
19
27
  const flag = state == null ? void 0 : state.sessionPersisted;
20
28
  return typeof flag === "boolean" ? flag : null;
@@ -35,9 +43,11 @@ function useDeepAgentThread({
35
43
  storage,
36
44
  isNewChat = false,
37
45
  attachKey,
46
+ laneEpoch = 0,
38
47
  suspended = false,
39
48
  capabilities,
40
49
  laneProjection = "both",
50
+ showQuietTools = false,
41
51
  selfAuthor = null,
42
52
  getMessageMetadata,
43
53
  adapters,
@@ -116,7 +126,7 @@ function useDeepAgentThread({
116
126
  const absentStateRunConfigAttachRef = react.useRef(null);
117
127
  const threadIdRef = react.useRef(threadId);
118
128
  threadIdRef.current = threadId;
119
- const attachId = attachKey === void 0 ? threadId : `${threadId}:${attachKey}`;
129
+ const attachId = deepAgentAttachId({ threadId, attachKey, laneEpoch });
120
130
  const attachIdRef = react.useRef(attachId);
121
131
  attachIdRef.current = attachId;
122
132
  const [authParked, setAuthParked] = react.useState(null);
@@ -218,32 +228,38 @@ function useDeepAgentThread({
218
228
  },
219
229
  []
220
230
  );
221
- const preloadRef = react.useRef({ threadId, preload: isNewChat });
222
- if (preloadRef.current.threadId !== threadId)
223
- preloadRef.current = { threadId, preload: isNewChat };
231
+ const preloadRef = react.useRef({ threadId, laneEpoch, preload: isNewChat });
232
+ if (preloadRef.current.threadId !== threadId || preloadRef.current.laneEpoch !== laneEpoch)
233
+ preloadRef.current = { threadId, laneEpoch, preload: isNewChat };
224
234
  const snapshotCacheRef = react.useRef(snapshotCache);
225
235
  snapshotCacheRef.current = snapshotCache;
226
236
  const snapshotSeedRef = react.useRef(null);
227
237
  if (snapshotSeedRef.current === null || snapshotSeedRef.current.attachId !== attachId) {
228
238
  const preload = preloadRef.current.preload;
239
+ const laneReplaced = snapshotSeedRef.current !== null && snapshotSeedRef.current.laneEpoch !== laneEpoch;
240
+ if (laneReplaced) snapshotCache == null ? void 0 : snapshotCache.forget(threadId);
229
241
  let pooledState;
230
242
  if (prewarmPool == null ? void 0 : prewarmPool.has(threadId)) {
231
- if (preload || suspended) {
243
+ if (preload || suspended || laneReplaced) {
232
244
  prewarmPool.discard(threadId);
233
245
  } else {
234
246
  pooledState = (_a = prewarmPool.take(threadId)) == null ? void 0 : _a.state;
235
247
  }
236
248
  }
237
- const remembered = preload || suspended ? void 0 : pooledState ?? (snapshotCache == null ? void 0 : snapshotCache.take(threadId));
249
+ const remembered = preload || suspended || laneReplaced ? void 0 : pooledState ?? (snapshotCache == null ? void 0 : snapshotCache.take(threadId));
238
250
  const seed = remembered ? threadSnapshotCache.toThreadSnapshotSeed(remembered) : void 0;
239
- snapshotSeedRef.current = { attachId, seed };
251
+ snapshotSeedRef.current = { attachId, laneEpoch, seed };
240
252
  const source = pooledState !== void 0 ? "prewarm" : seed ? "snapshot" : "none";
241
253
  const notifySource = onAttachSourceRef.current;
242
254
  if (notifySource) queueMicrotask(() => notifySource({ threadId, source }));
243
255
  }
244
256
  const conversionCacheRef = react.useRef(null);
245
- if (conversionCacheRef.current === null || conversionCacheRef.current.attachId !== attachId) {
246
- conversionCacheRef.current = { attachId, cache: createConversionCache() };
257
+ if (conversionCacheRef.current === null || conversionCacheRef.current.attachId !== attachId || conversionCacheRef.current.showQuietTools !== showQuietTools) {
258
+ conversionCacheRef.current = {
259
+ attachId,
260
+ showQuietTools,
261
+ cache: createConversionCache({ showQuietTools })
262
+ };
247
263
  }
248
264
  const conversionCache = conversionCacheRef.current.cache;
249
265
  const lastStateMessagesRef = react.useRef(
@@ -346,7 +362,7 @@ function useDeepAgentThread({
346
362
  },
347
363
  [attachId, conversionCache, converter, materialize, readMessageWindow, threadId]
348
364
  );
349
- return reactStatewire.StatewireThread({
365
+ const thread = reactStatewire.StatewireThread({
350
366
  // The transport stamps this protocol on every command; the thread client
351
367
  // must declare it too or the core skips the optimistic folds and a send
352
368
  // stays invisible until the server frame lands.
@@ -459,7 +475,9 @@ function useDeepAgentThread({
459
475
  })
460
476
  }
461
477
  });
478
+ return laneEpoch === 0 ? thread : tap.withKey(`lane-epoch:${laneEpoch}`, thread);
462
479
  }
480
+ exports.deepAgentAttachId = deepAgentAttachId;
463
481
  exports.readSessionPersisted = readSessionPersisted;
464
482
  exports.shouldNotifyRunningChange = shouldNotifyRunningChange;
465
483
  exports.useDeepAgentThread = useDeepAgentThread;
@@ -1 +1 @@
1
- {"version":3,"file":"use-deep-agent-thread.cjs","sources":["../../../../agent-runtime/src/use-deep-agent-thread.ts"],"sourcesContent":["import type { LangChainMessage } from '@assistant-ui/react-langgraph';\nimport { StatewireThread } from '@assistant-ui/react-statewire';\nimport { withKey } from '@assistant-ui/tap';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport {\n type AuthDenialTrackerState,\n initialAuthDenialTrackerState,\n observeAuthDenial,\n} from './auth-denial';\nimport {\n createDeepAgentAttachTransport,\n type DeepAgentHeadersResolver,\n} from './deep-agent-transport-recipe';\nimport { projectDeepAgentConnection } from './errors';\nimport { type DeepAgentMessageAuthor, isSameMessageAuthor } from './message-author';\nimport { DEEP_AGENT_INTEREST_PROTOCOL } from './generated/runconfig-contract';\nimport {\n type DeepAgentMessageWindowDeclaration,\n headInterestRequest,\n readDeepAgentMessageWindow,\n} from './message-window';\nimport {\n convertDeepAgentState,\n createDeepAgentMaterialize,\n createDeepAgentMessageConversionCache,\n type DeepAgentConversionMemory,\n} from './messages';\nimport { type DeepAgentProtocolStatus, projectDeepAgentProtocol } from './protocol';\nimport type { DeepAgentAttachTimingEvent } from './statewire-attach-observer';\nimport type { StatewireDiagnosticsRecorder } from './statewire-diagnostics';\nimport {\n isDeepAgentStateHydrated,\n pairDeepAgentStateWithThread,\n readDeepAgentStreamValues,\n} from './statewire-state';\nimport { StatewireSuspended } from './statewire-suspended';\nimport {\n DEEP_AGENT_STATEWIRE_PROTOCOL,\n type StatewireTransportOptions,\n} from './statewire-transport';\nimport type { DeepAgentThreadPrewarmPool } from './thread-prewarm';\nimport { type DeepAgentThreadSnapshotCache, toThreadSnapshotSeed } from './thread-snapshot-cache';\nimport type { DeepAgentToolOutputStore } from './tool-outputs';\nimport type { DeepAgentWithdrawnTurns } from './withdrawn-turns';\nimport type {\n DeepAgentConnectionState,\n DeepAgentLaneProjection,\n DeepAgentStreamValues,\n} from './types';\n\ntype ThreadOptions = StatewireThread.Options<DeepAgentStreamValues>;\n\nexport type { DeepAgentHeadersResolver } from './deep-agent-transport-recipe';\n\n/**\n * How an attach's FIRST frame is sourced, decided once per attach at mount: the\n * hello a pooled pre-dialed attach already replicated (`prewarm`), a remembered\n * snapshot seed (`snapshot`), or nothing (`none` — the thread loads until its\n * hello).\n */\nexport type DeepAgentAttachSource = 'prewarm' | 'snapshot' | 'none';\n\nexport interface UseDeepAgentThreadOptions {\n threadId: string;\n /** Base URL of the deep-agent statewire host (the `…/v2/threads` mount). */\n baseUrl: string;\n /** Transport selection: SSE when true, WebSocket otherwise. */\n sse?: boolean;\n /**\n * Auth headers for the transport. Statewire passes `{ staleAuth: true }`\n * when the previous credential was refused, so resolvers that can\n * force-renew should honor it.\n */\n headers: DeepAgentHeadersResolver;\n /**\n * Statewire session store for reconnect command-session recovery (e.g. the\n * mobile AsyncStorage-backed store). Host-resolved; omitted, sessions are\n * process-local.\n */\n sessionStore?: StatewireTransportOptions['sessionStore'];\n /**\n * Mounted storage seams for the thread: `session` holds the lane identity and\n * the commands still awaiting a durable verdict, so a reload resumes the lane\n * instead of minting a new client (browser hosts pass `LocalStorageSession`\n * from `@assistant-ui/react-statewire/local-storage`); `composer` persists the\n * draft. Mutually exclusive with `sessionStore` — statewire refuses a session\n * mounted twice.\n */\n storage?: ThreadOptions['storage'];\n /** True only for a never-started local chat; existing empty threads still load until their snapshot. */\n isNewChat?: boolean;\n /**\n * Extra transport-key discriminator for the SAME thread. The session is keyed\n * by `threadId` alone by default; hosts that must re-read the server snapshot\n * without a thread switch (a run dispatched outside this client, e.g. a\n * backgrounded AOP run writing the thread's first messages) bump this to\n * remount the session against the same URL.\n */\n attachKey?: string | number;\n /**\n * Host gate: park the transport instead of attaching — no socket, no retry\n * loop. The thread element stays mounted so assistant-ui consumers keep\n * rendering. Pass it when the host already knows every attach would be denied (e.g.\n * the signed-in user has no workspace yet), so a locked-out tab does not\n * hammer the attach endpoint on every backoff interval. `reason` is the\n * connection message; a park with none (a `pending` session lane) reads\n * like a never-started chat.\n */\n suspended?: { reason?: string | undefined } | false;\n /**\n * Adapter capabilities the host UI exposes. Defaults preserve each client's\n * pre-extraction surface — pass what the product mounts.\n */\n capabilities?: {\n edit?: boolean;\n continue?: boolean;\n reload?: boolean;\n unstable_switchBranch?: boolean;\n };\n /** Which run lanes project into the message tail; see {@link DeepAgentLaneProjection}. */\n laneProjection?: DeepAgentLaneProjection;\n /**\n * The viewer, as the backend would stamp them (`metadata.custom.author`).\n * Stamped onto this client's own optimistic bubbles — a send still in flight\n * has no host `meta` yet — so a just-sent turn is attributed, and the\n * multi-author avatar gate flips for a second poster, without waiting for\n * the acknowledgment. Replicated entries carry the host's own stamp instead.\n * Compared field-wise, so callers need not memoize the object.\n */\n selfAuthor?: DeepAgentMessageAuthor | null;\n /**\n * Snapshot host context when each user message is submitted, before it is\n * journaled. Includes programmatic appends, keyboard sends, queued messages,\n * steering, and edits. The async getter starts immediately at submission;\n * sends retain their order while it resolves. Retries, queue text edits,\n * lane moves, reload, and continue preserve the original association.\n * Return JSON-serializable metadata; optional capture failures can\n * return an explicit null field rather than blocking the message.\n *\n * Declared structurally rather than as `ThreadOptions['getMessageMetadata']`:\n * the option is what the monorepo patches into\n * `@assistant-ui/react-statewire@0.31.0`, and the published sources must also\n * compile against the unpatched upstream typings, where the library does not\n * know the option and ignores it.\n */\n getMessageMetadata?:\n | (() => Record<string, unknown> | undefined | Promise<Record<string, unknown> | undefined>)\n | undefined;\n /** Host-owned adapters: attachments (upload/mention choreography), speech, feedback. */\n adapters?: ThreadOptions['adapters'];\n /**\n * Transport lifecycle recorder (attach latency, reconnect loops, command\n * settle fates, stale-auth renewals) fed from the hook's own seams. Pass one\n * only when the host's diagnostics channel is enabled — the recorder is\n * observed on hot paths, and an absent recorder costs a single optional call.\n */\n diagnostics?: StatewireDiagnosticsRecorder;\n /**\n * Per-tab LRU of the last wire state seen per thread. When set, every\n * hydrated state the transport publishes is remembered, and a mount of a\n * remembered thread seeds `initialState` from the cache (through\n * {@link toThreadSnapshotSeed}) — the thread renders its last-known\n * messages on the first frame instead of holding a loading treatment for\n * the attach round-trip, and projects \"running\" when the run was live at\n * capture (else \"ready\"). The attach snapshot then\n * REPLACES the seeded state (statewire's `initialState` contract), so a\n * thread that advanced elsewhere reconciles in one round-trip. New chats\n * keep their preload and never read the cache.\n */\n snapshotCache?: DeepAgentThreadSnapshotCache;\n /**\n * The head window this surface wants: declared on every attach\n * (`x-athena-deep-agent-window`, part of the transport recipe) so the hello\n * of a client the host does not remember carries the newest `size`\n * messages, and stated as `athena/interest` after the hello only when the\n * hello's head is narrower — never to shrink it (the host holds what a\n * viewer has loaded for the life of its connection). The resolved chain is\n * joined with the mounted message documents into the converted transcript\n * (see `message-window.ts`); older pages load through\n * `useDeepAgentWindowSync`. Read once per attach.\n */\n messageWindow?: DeepAgentMessageWindowDeclaration;\n /**\n * Set only by a surface that pages a cut lane's earlier steps wherever it\n * shows a lane's steps (`useDeepAgentLaneHistory`). Its attaches then\n * declare the lane-cut contract version, and the host sends a finished\n * `task` card with a deep transcript cut to its newest steps (see\n * `DeepAgentProtocolDeclaration`). Omitted, every attach declares the\n * default version and finished cards ride whole. Part of the transport\n * recipe: a prewarm controller for this thread must be given the same\n * value. Read once per attach.\n */\n laneHistory?: boolean;\n /**\n * Joins the interest window off the mounted documents; the default is\n * {@link readDeepAgentMessageWindow}. Olympus passes\n * `readDeepAgentFollowedWindow`, which also reads the lanes it follows onto\n * their `task` cards.\n */\n readMessageWindow?: typeof readDeepAgentMessageWindow;\n /**\n * Creates each attach's conversion memory; the default is\n * {@link createDeepAgentMessageConversionCache}. Olympus passes\n * `createSubagentTranscriptConversionCache`, which also turns each `task`\n * card's nested transcript into its tool-call part's `messages`.\n */\n createConversionCache?: () => DeepAgentConversionMemory;\n /**\n * Full tool outputs fetched back for the wire's cut tool messages\n * (`history/tool_output`); the thread re-converts when one lands.\n */\n toolOutputs?: DeepAgentToolOutputStore;\n /**\n * User turns this client took back ahead of the wire (a user stop before\n * any assistant output); the thread re-converts when one opens or closes.\n */\n withdrawnTurns?: DeepAgentWithdrawnTurns;\n /**\n * Pre-dialed attaches (see `thread-prewarm.ts`). When the pool holds a root\n * for this thread at mount, the thread takes the hello it replicated as its\n * seed — the transcript is the first frame — and dials its own attach\n * against the instance the pool warmed; the pooled root is disposed in the\n * same step, so one socket is live per thread after mount. Never seeded for\n * a preloaded new chat or a suspended attach; such a root is discarded.\n */\n prewarmPool?: DeepAgentThreadPrewarmPool<DeepAgentStreamValues>;\n /**\n * Attach-path timing: socket open, hello size + handler cost, server retry\n * hints — attributed to the attach's own thread, since a late event from the\n * thread being left can land after the host has moved on.\n */\n onAttachTiming?: (info: { threadId: string; event: DeepAgentAttachTimingEvent }) => void;\n /** Where this attach's first frame comes from; once per attach, at mount. */\n onAttachSource?: (info: { threadId: string; source: DeepAgentAttachSource }) => void;\n /** Full transport error policy (paywall, toast, telemetry) — client-owned. */\n onError?: ThreadOptions['onError'];\n /**\n * Raw statewire state observer (first-attach latches, telemetry). The state\n * argument is never `undefined`/`null`: passes where the wire has no\n * snapshot yet (pre-attach, replay boundaries) deliver the typed\n * `DEEP_AGENT_PRE_STREAM_STATE` default — `isDeepAgentStateHydrated`\n * distinguishes them from a real (possibly empty) snapshot.\n */\n onStateChange?: ThreadOptions['onStateChange'];\n /** Raw command-lifecycle observer (send-failure salvage, telemetry). */\n onCommandChange?: ThreadOptions['onCommandChange'];\n /** Raw connection observer (reconnect handles, telemetry). */\n onRawConnectionChange?: ThreadOptions['onConnectionChange'];\n /**\n * Projected connection status changes, deduped per thread, with `null` on\n * unmount so an indicator never shows a stale status.\n */\n onConnectionChange?: (connection: DeepAgentConnectionState | null) => void;\n /** Notified when a run starts/stops so the page can reflect the running thread. */\n onRunningChange?: (isRunning: boolean) => void;\n /**\n * Notified once per thread with the `runConfig` carried by the statewire\n * snapshot (the backend's persisted panel config, seeded in `_load_history`),\n * so the host can hydrate its config store from the thread. Notified with\n * `null` (at most once per attach) when a server snapshot arrives WITHOUT a\n * persisted config, so a host waiting on hydration can stop waiting; a\n * config appearing on a later snapshot of the same attach (mirrored at run\n * start) still delivers the config notification.\n */\n onServerRunConfig?: (config: Record<string, unknown> | null) => void;\n /**\n * Notified when the snapshot's `outOfBandRunActive` flag changes: the host set\n * it while a run executing OUTSIDE this surface (an AOP / HTTP-route run)\n * is actively writing the thread's bare-namespace history, which the host\n * live-refreshes onto the wire. Lets the page show run-in-progress\n * treatment instead of an empty conversation, and stand down its own\n * re-attach polling. Change-notified per attach (an initial `false` before\n * the snapshot stays silent), mirroring `onRunningChange`.\n */\n onOutOfBandRunActive?: (outOfBandRunActive: boolean) => void;\n /**\n * Notified with the state's session-asset lifecycle flag\n * ({@link readSessionPersisted}) PAIRED with the thread it describes: `false`\n * while the thread's `session` asset provably does not exist yet (a\n * never-started chat's preload, a first run before the mint), `true` once\n * minted, `null` when the state carries no flag (a host that predates it).\n *\n * Fired from the converter pass, so the pair is read from the state of the\n * thread this attach was mounted for. A component reading the flag off the\n * client store instead lags the mounted thread id by one render on a switch\n * and can pair the new id with the previous thread's flag — releasing a\n * session-scoped read for a row the backend has not minted. Change-notified\n * per attach; the first pass of every attach notifies, so a host holding a\n * read back until it hears from the mounted thread always does.\n */\n onSessionPersistedChange?: (event: {\n threadId: string;\n sessionPersisted: boolean | null;\n }) => void;\n /**\n * Notified with the wire-contract status once the attach snapshot\n * advertises the `protocol` lane, and again whenever it changes (a\n * re-attach after a server roll can advertise a new `version`). Telemetry\n * and per-version behaviour only: the host has no minimum client version,\n * so nothing about the status gates a send. Never called for\n * pre-declaration servers (their snapshots carry no lane) — absence of a\n * call means \"no advertisement\", not \"compatible\".\n */\n onProtocolChange?: (status: DeepAgentProtocolStatus) => void;\n}\n\n/** One thread's replicated running state, keyed so a switch between two\n * threads with the same status still re-notifies (the host page clears its\n * per-thread state on selection, mirroring the connection dedupe below). */\nexport interface RunningSnapshot {\n threadId: string;\n isRunning: boolean;\n}\n\n/** Whether the thread's `session` asset exists, read off a statewire state, or\n * `null` when the state does not carry the flag.\n *\n * Absence is not absence of the asset: a snapshot from a host that predates\n * the flag has no such key, and reading that as \"the asset does not exist\"\n * would gate a read the client is entitled to make, with nothing left to\n * un-gate it. A new thread's preload seeds `false` (see `initialState`), so\n * the pre-attach pass of a chat that cannot have an asset yet still gates.\n *\n * Hosts pass this to `useStatewireState` so the flag and the thread it belongs\n * to are read in one component — a thread switch then cannot pair one thread's\n * id with the other's flag. */\nexport function readSessionPersisted(state: unknown): boolean | null {\n const flag = (state as { sessionPersisted?: unknown } | null)?.sessionPersisted;\n return typeof flag === 'boolean' ? flag : null;\n}\n\n/** Whether a converter pass must notify `onRunningChange`.\n *\n * The mount's very first status notifies only when it is `true` (a snapshot\n * carrying a live run) — an initial `false` before the snapshot must stay\n * silent so it cannot clear a still-valid indicator. After that, every\n * transition notifies, and so does the first status after a thread switch\n * (the host page clears its per-thread state on selection, so a switch\n * between two threads with the same status must still re-notify). */\nexport function shouldNotifyRunningChange(\n previous: RunningSnapshot | null,\n next: RunningSnapshot\n): boolean {\n if (previous === null) return next.isRunning;\n return previous.threadId !== next.threadId || previous.isRunning !== next.isRunning;\n}\n\n/** Whether two transcripts hold the very same message objects, in order. */\nfunction isSameMessageArray(\n previous: readonly LangChainMessage[],\n next: readonly LangChainMessage[]\n): boolean {\n return (\n previous.length === next.length && previous.every((message, index) => message === next[index])\n );\n}\n\n/**\n * The shared deep-agent Statewire thread.\n *\n * `StatewireThread` (from `@assistant-ui/react-statewire`) attaches to the\n * agora-hosted thread at `${baseUrl}/${threadId}` (WebSocket by default,\n * with SSE retained only as a host-selected compatibility switch): the backend\n * hosts the thread state and this client\n * replicates it live, so the server snapshot is the single source of history\n * (no local import, no `/status` resume probing) and run scheduling — queueing,\n * steering, stop, edit rewinds — rides the built-in `run/*` commands.\n *\n * The thread element is keyed by `threadId`, so a thread switch remounts the\n * session against the new URL instead of importing history in place.\n *\n * Returns the thread element for the host's own `useAui({ thread })` — web\n * hosts take `useAui` from `@assistant-ui/react`, React Native hosts from\n * `@assistant-ui/react-native`; this package imports neither.\n */\nexport function useDeepAgentThread({\n threadId,\n baseUrl,\n sse = false,\n headers,\n sessionStore,\n storage,\n isNewChat = false,\n attachKey,\n suspended = false,\n capabilities,\n laneProjection = 'both',\n selfAuthor = null,\n getMessageMetadata,\n adapters,\n diagnostics,\n snapshotCache,\n messageWindow,\n laneHistory = false,\n readMessageWindow = readDeepAgentMessageWindow,\n createConversionCache = createDeepAgentMessageConversionCache,\n toolOutputs,\n withdrawnTurns,\n prewarmPool,\n onAttachTiming,\n onAttachSource,\n onError,\n onStateChange,\n onCommandChange,\n onRawConnectionChange,\n onConnectionChange,\n onRunningChange,\n onServerRunConfig,\n onOutOfBandRunActive,\n onSessionPersistedChange,\n onProtocolChange,\n}: UseDeepAgentThreadOptions) {\n const onRunningChangeRef = useRef(onRunningChange);\n onRunningChangeRef.current = onRunningChange;\n const onSessionPersistedChangeRef = useRef(onSessionPersistedChange);\n onSessionPersistedChangeRef.current = onSessionPersistedChange;\n const onConnectionChangeRef = useRef(onConnectionChange);\n onConnectionChangeRef.current = onConnectionChange;\n const onServerRunConfigRef = useRef(onServerRunConfig);\n onServerRunConfigRef.current = onServerRunConfig;\n const onOutOfBandRunActiveRef = useRef(onOutOfBandRunActive);\n onOutOfBandRunActiveRef.current = onOutOfBandRunActive;\n const onProtocolChangeRef = useRef(onProtocolChange);\n onProtocolChangeRef.current = onProtocolChange;\n const onErrorRef = useRef(onError);\n onErrorRef.current = onError;\n const onStateChangeRef = useRef(onStateChange);\n onStateChangeRef.current = onStateChange;\n const onCommandChangeRef = useRef(onCommandChange);\n onCommandChangeRef.current = onCommandChange;\n const onRawConnectionChangeRef = useRef(onRawConnectionChange);\n onRawConnectionChangeRef.current = onRawConnectionChange;\n const headersRef = useRef(headers);\n headersRef.current = headers;\n const messageWindowRef = useRef(messageWindow);\n messageWindowRef.current = messageWindow;\n const laneHistoryRef = useRef(laneHistory);\n laneHistoryRef.current = laneHistory;\n // The head interest is checked once per connected session, after the hello\n // negotiated the lane (a statement before that would be minted durable):\n // the hello carries the host's head — the attach header's size, or what\n // this client id held — and a set goes out only to widen it.\n const interestRef = useRef({ attachId: '', stated: false });\n // The last joined transcript, held across the gap between a hello and its\n // interest answer (and across a reconnect) so the thread never blanks.\n const lastMessagesRef = useRef<{ attachId: string; messages: LangChainMessage[] } | null>(null);\n // A fetched tool output must re-run the converter (the store is read inside\n // it); the version is the memo's signal that the store's contents moved.\n const [toolOutputsVersion, setToolOutputsVersion] = useState(0);\n useEffect(() => {\n if (!toolOutputs) return undefined;\n return toolOutputs.subscribe(() => setToolOutputsVersion((version) => version + 1));\n }, [toolOutputs]);\n const [withdrawnTurnsVersion, setWithdrawnTurnsVersion] = useState(0);\n useEffect(() => {\n if (!withdrawnTurns) return undefined;\n return withdrawnTurns.subscribe(() => setWithdrawnTurnsVersion((version) => version + 1));\n }, [withdrawnTurns]);\n const diagnosticsRef = useRef(diagnostics);\n diagnosticsRef.current = diagnostics;\n const onAttachTimingRef = useRef(onAttachTiming);\n onAttachTimingRef.current = onAttachTiming;\n const onAttachSourceRef = useRef(onAttachSource);\n onAttachSourceRef.current = onAttachSource;\n\n const lastRunningRef = useRef<RunningSnapshot | null>(null);\n // Out-of-band run activity (`outOfBandRunActive`), change-notified with the\n // same semantics as the running flag above.\n const lastOutOfBandRunActiveRef = useRef<RunningSnapshot | null>(null);\n // The session-asset lifecycle flag last notified, keyed by attach so every\n // attach's first pass notifies (a host holding a read back until it hears\n // from the mounted thread must always hear from it).\n const lastSessionPersistedRef = useRef<{\n attachId: string;\n threadId: string;\n sessionPersisted: boolean | null;\n } | null>(null);\n // The last wire-contract status notified, keyed by attach so a re-attach\n // re-notifies (the new snapshot may advertise a different version).\n // `status: null` = nothing advertised yet on this attach.\n const lastProtocolRef = useRef<{\n attachId: string;\n status: DeepAgentProtocolStatus | null;\n } | null>(null);\n // The thread whose statewire-carried runConfig has been surfaced to the host\n // (once per thread; later snapshots must not clobber edits).\n const appliedStateRunConfigThreadRef = useRef<string | null>(null);\n // The attach that already notified `onServerRunConfig(null)` (snapshot\n // arrived carrying no persisted config) — at most once per attach.\n const absentStateRunConfigAttachRef = useRef<string | null>(null);\n // Live thread id for the callbacks: they are created per render but may\n // settle after a switch, so they compare against the current id.\n const threadIdRef = useRef(threadId);\n threadIdRef.current = threadId;\n // Identity of the current attach. Every per-thread latch keys off this, so a\n // re-attach of the same thread (a bumped `attachKey`) re-notifies the host\n // exactly like a switch would — the host clears its per-thread state on\n // re-attach, so a snapshot that repeats the previous status must not dedupe.\n const attachId: string = attachKey === undefined ? threadId : `${threadId}:${attachKey}`;\n const attachIdRef = useRef(attachId);\n attachIdRef.current = attachId;\n\n // Auth-denial park: the server fins every denied attach `unauthorized` and\n // the reconnect loop would otherwise retry it every 30s forever (prod tabs\n // looped for hours against revoked/workspace-less credentials). After a\n // streak of denials the live transport is swapped for a parked one; a new\n // attach (thread switch, bumped attachKey) or the parked connection's\n // `reconnect()` re-arms it.\n const [authParked, setAuthParked] = useState<{\n attachId: string;\n message: string | undefined;\n } | null>(null);\n const authDenialRef = useRef<{ attachId: string; state: AuthDenialTrackerState }>({\n attachId,\n state: initialAuthDenialTrackerState(),\n });\n useEffect(() => {\n setAuthParked((prev) => (prev !== null && prev.attachId !== attachId ? null : prev));\n }, [attachId]);\n\n // Field-wise stable identity for the viewer stamp, so a host that rebuilds\n // the author object per render does not rebuild the converter (and with it\n // every message) per render. Adjusting state during render is React's\n // sanctioned derived-state pattern: it re-renders once with the new value.\n const [stableSelfAuthor, setStableSelfAuthor] = useState(selfAuthor);\n if (!isSameMessageAuthor(stableSelfAuthor, selfAuthor)) setStableSelfAuthor(selfAuthor);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: toolOutputsVersion / withdrawnTurnsVersion are the stores' change signals — the converter reads the stores' contents, not the counters.\n const converter = useMemo(\n () => (state: unknown, isRunning: boolean, conversionCache: DeepAgentConversionMemory) => {\n // Statewire snapshots carry the thread's persisted panel config (the\n // backend seeds `runConfig` in `_load_history` and mirrors it on run\n // start). Surface it once per thread so the host can hydrate its config\n // store.\n const stateRunConfig = (state as { runConfig?: unknown } | null)?.runConfig;\n if (\n stateRunConfig &&\n typeof stateRunConfig === 'object' &&\n !Array.isArray(stateRunConfig) &&\n appliedStateRunConfigThreadRef.current !== attachIdRef.current\n ) {\n appliedStateRunConfigThreadRef.current = attachIdRef.current;\n const notifyRunConfig = onServerRunConfigRef.current;\n if (notifyRunConfig) {\n queueMicrotask(() => notifyRunConfig(stateRunConfig as Record<string, unknown>));\n }\n } else if (\n !stateRunConfig &&\n appliedStateRunConfigThreadRef.current !== attachIdRef.current &&\n absentStateRunConfigAttachRef.current !== attachIdRef.current &&\n projectDeepAgentProtocol(state) !== null\n ) {\n // No persisted config on a SERVER state: the protocol lane rides every\n // attach snapshot, so its presence distinguishes \"the server answered\n // without a config\" from the pre-attach `initialState` (which must not\n // end the host's hydration wait).\n absentStateRunConfigAttachRef.current = attachIdRef.current;\n const notifyRunConfig = onServerRunConfigRef.current;\n if (notifyRunConfig) {\n queueMicrotask(() => notifyRunConfig(null));\n }\n }\n // Out-of-band run activity: the host live-refreshes a bare-namespace\n // (AOP / HTTP-route) thread's transcript while its run is active and\n // flags the wire state so the page can reflect it.\n const stateOutOfBandRunActive =\n (state as { outOfBandRunActive?: unknown } | null)?.outOfBandRunActive === true;\n const outOfBandRunActiveSnapshot = {\n threadId: attachIdRef.current,\n isRunning: stateOutOfBandRunActive,\n };\n if (shouldNotifyRunningChange(lastOutOfBandRunActiveRef.current, outOfBandRunActiveSnapshot)) {\n lastOutOfBandRunActiveRef.current = outOfBandRunActiveSnapshot;\n const observedAttachId = attachIdRef.current;\n queueMicrotask(() => {\n // A re-attach or newer snapshot between the converter pass and this\n // microtask owns the channel now — a stale flag must not leak into\n // the new attach's per-thread state.\n if (\n attachIdRef.current !== observedAttachId ||\n lastOutOfBandRunActiveRef.current !== outOfBandRunActiveSnapshot\n ) {\n return;\n }\n onOutOfBandRunActiveRef.current?.(stateOutOfBandRunActive);\n });\n }\n // Wire-contract advertisement: the host publishes the `protocol` lane in\n // every attach snapshot. Notify on first advertisement and on change\n // (dedupe by value, re-keyed per attach), with the same stale-guard\n // microtask as the other observers.\n const protocolStatus = projectDeepAgentProtocol(state, {\n laneHistory: laneHistoryRef.current,\n });\n if (protocolStatus !== null) {\n const previous =\n lastProtocolRef.current?.attachId === attachIdRef.current\n ? lastProtocolRef.current.status\n : null;\n if (previous === null || previous.protocolVersion !== protocolStatus.protocolVersion) {\n const update = { attachId: attachIdRef.current, status: protocolStatus };\n lastProtocolRef.current = update;\n queueMicrotask(() => {\n if (lastProtocolRef.current !== update || attachIdRef.current !== update.attachId) {\n return;\n }\n onProtocolChangeRef.current?.(protocolStatus);\n });\n }\n }\n const runningSnapshot = { threadId: attachIdRef.current, isRunning };\n if (shouldNotifyRunningChange(lastRunningRef.current, runningSnapshot)) {\n lastRunningRef.current = runningSnapshot;\n const notify = onRunningChangeRef.current;\n if (notify) queueMicrotask(() => notify(isRunning));\n }\n return {\n ...convertDeepAgentState(state, {\n laneProjection,\n selfAuthor: stableSelfAuthor,\n toolOutputs,\n withdrawnTurns,\n conversionCache,\n }),\n state: state as StatewireThread.State['state'],\n };\n },\n [\n laneProjection,\n stableSelfAuthor,\n toolOutputs,\n toolOutputsVersion,\n withdrawnTurns,\n withdrawnTurnsVersion,\n ]\n );\n const materialize = useMemo(\n () => createDeepAgentMaterialize({ selfAuthor: stableSelfAuthor }),\n [stableSelfAuthor]\n );\n\n // Surface the statewire connection status through `onConnectionChange` (the\n // converter re-runs on connection changes), and clear it when the runtime\n // unmounts so an indicator never shows a stale status. Dedup is keyed by\n // thread: the runtime stays mounted across idle thread switches, so the new\n // thread's first status must notify even when it equals the previous\n // thread's last one.\n const lastConnectionRef = useRef<{\n threadId: string;\n connection: DeepAgentConnectionState;\n } | null>(null);\n useEffect(\n () => () => {\n lastConnectionRef.current = null;\n onConnectionChangeRef.current?.(null);\n },\n []\n );\n\n // The preload decision pins at thread entry: `isNewChat` flips once the\n // thread persists, but the mount that began as a new thread stays preloaded.\n const preloadRef = useRef({ threadId, preload: isNewChat });\n if (preloadRef.current.threadId !== threadId)\n preloadRef.current = { threadId, preload: isNewChat };\n\n const snapshotCacheRef = useRef(snapshotCache);\n snapshotCacheRef.current = snapshotCache;\n // The snapshot seed pins at attach identity, like the preload decision: the\n // cache is read once per mount, so a mid-session cache write (this thread's\n // own states being remembered) never changes what `initialState` was.\n //\n // A suspended attach never seeds: the suspended transport never dials, so a\n // seed would be served indefinitely — under a view-as lens that would paint\n // thread content the parked transport exists to withhold (the lens must\n // show what the LENSED identity is authorized to see, and only the server\n // can decide that).\n //\n // The pooled hello is taken the same way: exactly once, at the attach's\n // first render (the pool disposes its root in that step).\n const snapshotSeedRef = useRef<{\n attachId: string;\n seed: DeepAgentStreamValues | undefined;\n } | null>(null);\n if (snapshotSeedRef.current === null || snapshotSeedRef.current.attachId !== attachId) {\n const preload = preloadRef.current.preload;\n let pooledState: DeepAgentStreamValues | undefined;\n if (prewarmPool?.has(threadId)) {\n if (preload || suspended) {\n // This mount attaches on its own terms (or not at all): the pooled\n // dial must not linger as a second socket to the same session.\n prewarmPool.discard(threadId);\n } else {\n pooledState = prewarmPool.take(threadId)?.state;\n }\n }\n // The pooled hello outranks a remembered snapshot (it is newer by\n // construction); a pooled dial that had not landed seeds like a plain\n // attach and reconciles when this attach's own hello lands.\n const remembered =\n preload || suspended ? undefined : (pooledState ?? snapshotCache?.take(threadId));\n const seed = remembered ? toThreadSnapshotSeed(remembered) : undefined;\n snapshotSeedRef.current = { attachId, seed };\n const source: DeepAgentAttachSource =\n pooledState !== undefined ? 'prewarm' : seed ? 'snapshot' : 'none';\n const notifySource = onAttachSourceRef.current;\n if (notifySource) queueMicrotask(() => notifySource({ threadId, source }));\n }\n\n // The conversion memory pins at attach identity like the snapshot seed: an\n // unchanged message converts to the identical thread message on every pass\n // of this attach, and a switch or re-attach starts from an empty memory, so\n // one thread's converted messages can never be served for another.\n const conversionCacheRef = useRef<{\n attachId: string;\n cache: DeepAgentConversionMemory;\n } | null>(null);\n if (conversionCacheRef.current === null || conversionCacheRef.current.attachId !== attachId) {\n conversionCacheRef.current = { attachId, cache: createConversionCache() };\n }\n const conversionCache = conversionCacheRef.current.cache;\n // The last pass's joined transcript, reused when a packet moved no message\n // (a run-status flip, a todo, another document) so readers memoized on\n // `state.messages` skip that pass.\n const lastStateMessagesRef = useRef<{ attachId: string; messages: LangChainMessage[] } | null>(\n null\n );\n\n // react-statewire memoizes the converted thread on this function's\n // identity, so an inline closure would re-run `convertDeepAgentState` over\n // the whole transcript on every host render. Keyed on the attach identity (a\n // re-attach re-keys its per-thread latches); everything else the body needs\n // is read through the per-render refs above.\n const threadConverter = useCallback<ThreadOptions['converter']>(\n (wireState, meta) => {\n // The wire main document carries no messages: join the interest window\n // off the mounted documents, fold the dispatch record onto it, and hand\n // the composite on as the thread state every reader sees.\n const values = readDeepAgentStreamValues(wireState);\n const window = readMessageWindow(meta.mounted);\n const held =\n lastMessagesRef.current?.attachId === attachId ? lastMessagesRef.current.messages : null;\n const messages = window?.messages ?? values.messages ?? held ?? [];\n if (window !== null || messages.length > 0) lastMessagesRef.current = { attachId, messages };\n const state: DeepAgentStreamValues = { ...values, messages: [...messages] };\n // Every state this attach publishes names its thread, so a binding\n // mounted for the next thread can tell this state apart from its own\n // for the render it is still projected after a switch.\n pairDeepAgentStateWithThread({ state, threadId });\n materialize(state);\n const joined: LangChainMessage[] = state.messages ?? [];\n const previousJoined = lastStateMessagesRef.current;\n if (\n previousJoined !== null &&\n previousJoined.attachId === attachId &&\n isSameMessageArray(previousJoined.messages, joined)\n ) {\n state.messages = previousJoined.messages;\n } else {\n lastStateMessagesRef.current = { attachId, messages: joined };\n }\n if (window !== null && isDeepAgentStateHydrated(values)) {\n snapshotCacheRef.current?.remember(threadId, state);\n }\n if (interestRef.current.attachId !== attachId) {\n interestRef.current = { attachId, stated: false };\n }\n if (meta.connection.status !== 'connected') {\n interestRef.current.stated = false;\n } else if (!interestRef.current.stated) {\n interestRef.current.stated = true;\n const wanted = headInterestRequest(messageWindowRef.current?.size);\n const head = window?.requests[0];\n // The hello's head is kept whenever it is at least what this surface\n // wants (a remembered client's wider view, the declared header):\n // stating a narrower head would shrink what the viewer holds. A\n // narrower head — or none, a host without the lane — is widened, the\n // hello's pages kept alongside.\n const preloaded = head !== undefined && head.at === undefined && head.before >= wanted.before;\n if (!preloaded) {\n const requests = [\n wanted,\n ...(window?.requests.filter((request) => request.at !== undefined) ?? []),\n ];\n queueMicrotask(() => {\n if (attachIdRef.current !== attachId) return;\n meta\n .invoke(DEEP_AGENT_INTEREST_PROTOCOL, 'set', [requests])\n .catch((error: unknown) => {\n onErrorRef.current?.(error instanceof Error ? error : new Error(String(error)));\n });\n });\n }\n }\n // Projected once per pass through the single seam; every fold below\n // consumes the projection (the recorder projects the raw connection\n // itself, so any host observer can feed it).\n const connection = projectDeepAgentConnection(meta.connection);\n // Transition-deduped inside the recorder, so this per-pass observation\n // is free while the connection is unchanged.\n diagnosticsRef.current?.observeConnection(meta.connection);\n // Fold this pass's connection into the denial streak (the converter\n // re-runs on every reconnect-attempt bump). Parking is deferred to a\n // microtask like every other notification here.\n if (authDenialRef.current.attachId !== attachIdRef.current) {\n authDenialRef.current = {\n attachId: attachIdRef.current,\n state: initialAuthDenialTrackerState(),\n };\n }\n const denial = observeAuthDenial(authDenialRef.current.state, connection);\n authDenialRef.current.state = denial.state;\n if (denial.park) {\n const parkAttachId = attachIdRef.current;\n const parkMessage = denial.state.message;\n // The server refused authorization for this thread: any remembered\n // snapshot must not warm-paint it on a later mount (permissions may\n // have been revoked since the state was captured).\n snapshotCacheRef.current?.forget(threadId);\n queueMicrotask(() => {\n if (attachIdRef.current !== parkAttachId) return;\n setAuthParked((prev) =>\n prev?.attachId === parkAttachId\n ? prev\n : { attachId: parkAttachId, message: parkMessage }\n );\n });\n }\n const previous = lastConnectionRef.current;\n if (\n previous?.threadId !== attachId ||\n !Object.keys({ ...previous.connection, ...connection }).every(\n (key) => Reflect.get(previous.connection, key) === Reflect.get(connection, key)\n )\n ) {\n const update = { threadId: attachId, connection };\n lastConnectionRef.current = update;\n queueMicrotask(() => {\n // A newer projection, a thread switch, or the unmount cleanup owns\n // the channel now.\n if (lastConnectionRef.current !== update || attachIdRef.current !== attachId) return;\n onConnectionChangeRef.current?.(connection);\n });\n }\n // Session-asset lifecycle, paired with THIS attach's thread by closure:\n // the state converted here is the state of the thread the element was\n // built for, so the pair can never straddle a switch (a client-store\n // read would). Deduped by value per attach. A pass from an attach the\n // host has already replaced never touches the latch — it could otherwise\n // cancel the current attach's still-queued verdict — and the microtask\n // guard drops a pass whose attach is replaced before it settles.\n if (attachIdRef.current === attachId) {\n const sessionPersisted = readSessionPersisted(state);\n const lastSession = lastSessionPersistedRef.current;\n if (\n lastSession?.attachId !== attachId ||\n lastSession.sessionPersisted !== sessionPersisted\n ) {\n const sessionUpdate = { attachId, threadId, sessionPersisted };\n lastSessionPersistedRef.current = sessionUpdate;\n queueMicrotask(() => {\n if (\n lastSessionPersistedRef.current !== sessionUpdate ||\n attachIdRef.current !== attachId\n ) {\n return;\n }\n onSessionPersistedChangeRef.current?.({ threadId, sessionPersisted });\n });\n }\n }\n // The replicated run status IS the running signal: the lib's run-dialect\n // fold flips it to \"running\" on the send, before the app converter runs,\n // so the optimistic window needs no hand-tracked flag.\n return converter(state, state.runs?.[0]?.status === 'running', conversionCache);\n },\n [attachId, conversionCache, converter, materialize, readMessageWindow, threadId]\n );\n\n return StatewireThread<DeepAgentStreamValues>({\n // The transport stamps this protocol on every command; the thread client\n // must declare it too or the core skips the optimistic folds and a send\n // stays invisible until the server frame lands.\n protocol: DEEP_AGENT_STATEWIRE_PROTOCOL,\n // New threads preload an empty runs mount and report `deferred` — the\n // composer is interactive before the stream attaches and pre-attach sends\n // render through the optimistic folds. Existing threads omit the runs\n // mount and project \"loading\" until the attach snapshot: the server seeds\n // messages/todos/runConfig in `_load_history`.\n //\n // The preload also seeds `sessionPersisted: false`: a never-started chat\n // provably has no `session` asset until its first run mints one, so the\n // wire carries that from the first render and a host reading the flag off\n // the state needs no separate \"is this a new chat\" knowledge. An existing\n // thread leaves it absent (unknown) so its asset read is never gated.\n initialState:\n snapshotSeedRef.current.seed ??\n ({\n messages: [],\n todos: [],\n ...(preloadRef.current.preload && { runs: [], sessionPersisted: false }),\n } satisfies DeepAgentStreamValues),\n // Keyed by thread: a switch unmounts the previous thread's session and\n // remounts against the new URL, so the converter never sees the previous\n // thread's snapshot (a new thread serves its preload again, an existing\n // one loads until its snapshot). A host-suspended or auth-parked attach\n // swaps in the never-dialing transport instead.\n transport: withKey(\n attachId,\n (() => {\n if (suspended) return StatewireSuspended({ message: suspended.reason });\n if (authParked !== null && authParked.attachId === attachId) {\n return StatewireSuspended({\n message: authParked.message ?? 'Session authorization was refused.',\n onReconnectRequested: () => {\n authDenialRef.current = {\n attachId: attachIdRef.current,\n state: initialAuthDenialTrackerState(),\n };\n setAuthParked(null);\n },\n });\n }\n // The shared recipe (URL, auth + wire-contract headers, session lane,\n // attach instrumentation, retry-hint floor) — the same one the prewarm\n // pool dials with, so the instance a pooled attach warmed is the one\n // this dial reaches. A never-started chat dials nothing until its\n // first send.\n return createDeepAgentAttachTransport<DeepAgentStreamValues>({\n baseUrl,\n threadId,\n sse,\n isNew: preloadRef.current.preload,\n headers: (ctx) => headersRef.current(ctx),\n messageWindow: messageWindowRef.current,\n laneHistory: laneHistoryRef.current,\n sessionStore,\n onAuthResolve: (ctx) => diagnosticsRef.current?.observeAuthResolve(ctx),\n onAttachTiming: (event) => onAttachTimingRef.current?.({ threadId, event }),\n });\n })()\n ),\n // Survives the transport remounts above: a bumped attachKey re-attaches the\n // same lane, and a persistent session storage carries it across reloads.\n ...(storage && { storage }),\n // The run subsystem (status, lanes, input requests, runId) is mounted on\n // the state root and always replicated by react-statewire >= 0.14 (the\n // former `runs: true` opt-in is gone) — the converter only maps app-owned\n // keys, including the `run/stop` runId stamp the lib projects itself.\n converter: threadConverter,\n // Native message editing: an edited turn is resubmitted as a `run/edit`\n // command, and the deep-agent backend rewinds the checkpointed thread to\n // the edited message's parent before appending (route `plan_rewind`).\n capabilities: capabilities ?? { edit: true },\n ...(getMessageMetadata && { getMessageMetadata }),\n ...(adapters && { adapters }),\n contextSync: 'command-send',\n // The Stop control is the only `cancelRun()` caller — teardown paths\n // unmount the keyed transport instead — so every `run/stop` is a\n // deliberate user stop.\n stopPayload: () => ({ reason: 'user_stop' }),\n onError: (error) => {\n diagnosticsRef.current?.observeError(error);\n onErrorRef.current?.(error);\n },\n ...(onStateChange && {\n // The lib types the observed state `NonNullable`, but the runtime\n // delivers `undefined` where the wire has no snapshot (pre-attach and\n // replay boundaries republish it) — normalize so the type is true and\n // host observers never crash on a field read.\n onStateChange: ((state, ctx) => {\n onStateChangeRef.current?.(readDeepAgentStreamValues(state), ctx);\n }) satisfies ThreadOptions['onStateChange'],\n }),\n ...((onCommandChange || diagnostics) && {\n onCommandChange: ((update, ctx) => {\n diagnosticsRef.current?.observeCommand(update);\n onCommandChangeRef.current?.(update, ctx);\n }) satisfies ThreadOptions['onCommandChange'],\n }),\n ...(onRawConnectionChange && {\n onConnectionChange: ((connection) =>\n onRawConnectionChangeRef.current?.(\n connection\n )) satisfies ThreadOptions['onConnectionChange'],\n }),\n });\n}\n"],"names":["messageWindow","readDeepAgentMessageWindow","createDeepAgentMessageConversionCache","useRef","useState","useEffect","initialAuthDenialTrackerState","isSameMessageAuthor","useMemo","conversionCache","projectDeepAgentProtocol","_a","convertDeepAgentState","createDeepAgentMaterialize","toThreadSnapshotSeed","useCallback","readDeepAgentStreamValues","messages","pairDeepAgentStateWithThread","isDeepAgentStateHydrated","headInterestRequest","DEEP_AGENT_INTEREST_PROTOCOL","projectDeepAgentConnection","observeAuthDenial","StatewireThread","DEEP_AGENT_STATEWIRE_PROTOCOL","withKey","StatewireSuspended","createDeepAgentAttachTransport"],"mappings":"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"}
1
+ {"version":3,"file":"use-deep-agent-thread.cjs","sources":["../../../../agent-runtime/src/use-deep-agent-thread.ts"],"sourcesContent":["import type { LangChainMessage } from '@assistant-ui/react-langgraph';\nimport { StatewireThread } from '@assistant-ui/react-statewire';\nimport { withKey } from '@assistant-ui/tap';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport {\n type AuthDenialTrackerState,\n initialAuthDenialTrackerState,\n observeAuthDenial,\n} from './auth-denial';\nimport {\n createDeepAgentAttachTransport,\n type DeepAgentHeadersResolver,\n} from './deep-agent-transport-recipe';\nimport { projectDeepAgentConnection } from './errors';\nimport { type DeepAgentMessageAuthor, isSameMessageAuthor } from './message-author';\nimport { DEEP_AGENT_INTEREST_PROTOCOL } from './generated/runconfig-contract';\nimport {\n type DeepAgentMessageWindowDeclaration,\n headInterestRequest,\n readDeepAgentMessageWindow,\n} from './message-window';\nimport {\n convertDeepAgentState,\n createDeepAgentMaterialize,\n createDeepAgentMessageConversionCache,\n type DeepAgentConversionMemory,\n type DeepAgentMessageConversionOptions,\n} from './messages';\nimport { type DeepAgentProtocolStatus, projectDeepAgentProtocol } from './protocol';\nimport type { DeepAgentAttachTimingEvent } from './statewire-attach-observer';\nimport type { StatewireDiagnosticsRecorder } from './statewire-diagnostics';\nimport {\n isDeepAgentStateHydrated,\n pairDeepAgentStateWithThread,\n readDeepAgentStreamValues,\n} from './statewire-state';\nimport { StatewireSuspended } from './statewire-suspended';\nimport {\n DEEP_AGENT_STATEWIRE_PROTOCOL,\n type StatewireTransportOptions,\n} from './statewire-transport';\nimport type { DeepAgentThreadPrewarmPool } from './thread-prewarm';\nimport { type DeepAgentThreadSnapshotCache, toThreadSnapshotSeed } from './thread-snapshot-cache';\nimport type { DeepAgentToolOutputStore } from './tool-outputs';\nimport type { DeepAgentWithdrawnTurns } from './withdrawn-turns';\nimport type {\n DeepAgentConnectionState,\n DeepAgentLaneProjection,\n DeepAgentStreamValues,\n} from './types';\n\ntype ThreadOptions = StatewireThread.Options<DeepAgentStreamValues>;\n\nexport type { DeepAgentHeadersResolver } from './deep-agent-transport-recipe';\n\n/**\n * The identity of one attach of a thread: the thread, the host's `attachKey`\n * and the lane epoch (`UseDeepAgentThreadOptions['laneEpoch']`). The transport\n * mount and every per-attach decision (snapshot seed, pooled hello, conversion\n * memory, the host's per-thread latches, send recovery) key off it, so a new\n * lane epoch is a new attach exactly like a thread switch.\n */\nexport function deepAgentAttachId({\n threadId,\n attachKey,\n laneEpoch = 0,\n}: {\n threadId: string;\n attachKey?: string | number | undefined;\n laneEpoch?: number;\n}): string {\n const attach = attachKey === undefined ? threadId : `${threadId}:${attachKey}`;\n return laneEpoch === 0 ? attach : `${attach}#lane-epoch:${laneEpoch}`;\n}\n\n/**\n * How an attach's FIRST frame is sourced, decided once per attach at mount: the\n * hello a pooled pre-dialed attach already replicated (`prewarm`), a remembered\n * snapshot seed (`snapshot`), or nothing (`none` — the thread loads until its\n * hello).\n */\nexport type DeepAgentAttachSource = 'prewarm' | 'snapshot' | 'none';\n\nexport interface UseDeepAgentThreadOptions {\n threadId: string;\n /** Base URL of the deep-agent statewire host (the `…/v2/threads` mount). */\n baseUrl: string;\n /** Transport selection: SSE when true, WebSocket otherwise. */\n sse?: boolean;\n /**\n * Auth headers for the transport. Statewire passes `{ staleAuth: true }`\n * when the previous credential was refused, so resolvers that can\n * force-renew should honor it.\n */\n headers: DeepAgentHeadersResolver;\n /**\n * Statewire session store for reconnect command-session recovery (e.g. the\n * mobile AsyncStorage-backed store). Host-resolved; omitted, sessions are\n * process-local.\n */\n sessionStore?: StatewireTransportOptions['sessionStore'];\n /**\n * Mounted storage seams for the thread: `session` holds the lane identity and\n * the commands still awaiting a durable verdict, so a reload resumes the lane\n * instead of minting a new client (browser hosts pass `LocalStorageSession`\n * from `@assistant-ui/react-statewire/local-storage`); `composer` persists the\n * draft. Mutually exclusive with `sessionStore` — statewire refuses a session\n * mounted twice.\n */\n storage?: ThreadOptions['storage'];\n /** True only for a never-started local chat; existing empty threads still load until their snapshot. */\n isNewChat?: boolean;\n /**\n * Extra transport-key discriminator for the SAME thread. The session is keyed\n * by `threadId` alone by default; hosts that must re-read the server snapshot\n * without a thread switch (a run dispatched outside this client, e.g. a\n * backgrounded AOP run writing the thread's first messages) bump this to\n * remount the session against the same URL.\n */\n attachKey?: string | number;\n /**\n * Bumped when the durable lane this mount runs on is replaced by a lane of\n * another scope (another principal signed in, or another backend) while the\n * thread stays mounted; `useDeepAgentRuntime` derives it from the session\n * `key`. Each epoch remounts the whole statewire thread (its session with\n * the client id, lane clocks and journal it loaded, the session storage, the\n * composer and the thread state) instead of moving it to the new lane, and\n * is an attach identity of its own. Its first attach seeds nothing this tab\n * remembered of the thread and takes no dial pooled for it: both were\n * captured as the previous principal. It reads `isNewChat` afresh, so a\n * thread that persisted under the previous principal attaches at once.\n * Default 0.\n */\n laneEpoch?: number;\n /**\n * Host gate: park the transport instead of attaching — no socket, no retry\n * loop. The thread element stays mounted so assistant-ui consumers keep\n * rendering. Pass it when the host already knows every attach would be denied (e.g.\n * the signed-in user has no workspace yet), so a locked-out tab does not\n * hammer the attach endpoint on every backoff interval. `reason` is the\n * connection message; a park with none (a `pending` session lane) reads\n * like a never-started chat.\n */\n suspended?: { reason?: string | undefined } | false;\n /**\n * Adapter capabilities the host UI exposes. Defaults preserve each client's\n * pre-extraction surface — pass what the product mounts.\n */\n capabilities?: {\n edit?: boolean;\n continue?: boolean;\n reload?: boolean;\n unstable_switchBranch?: boolean;\n };\n /** Which run lanes project into the message tail; see {@link DeepAgentLaneProjection}. */\n laneProjection?: DeepAgentLaneProjection;\n /**\n * Render quiet tools (`QUIET_TOOL_NAMES`, e.g. `submit_feedback`) like any\n * other tool call. Off by default; pass it only for viewers allowed to see\n * them (Olympus: Athena's internal admins on managed cloud).\n */\n showQuietTools?: boolean;\n /**\n * The viewer, as the backend would stamp them (`metadata.custom.author`).\n * Stamped onto this client's own optimistic bubbles — a send still in flight\n * has no host `meta` yet — so a just-sent turn is attributed, and the\n * multi-author avatar gate flips for a second poster, without waiting for\n * the acknowledgment. Replicated entries carry the host's own stamp instead.\n * Compared field-wise, so callers need not memoize the object.\n */\n selfAuthor?: DeepAgentMessageAuthor | null;\n /**\n * Snapshot host context when each user message is submitted, before it is\n * journaled. Includes programmatic appends, keyboard sends, queued messages,\n * steering, and edits. The async getter starts immediately at submission;\n * sends retain their order while it resolves. Retries, queue text edits,\n * lane moves, reload, and continue preserve the original association.\n * Return JSON-serializable metadata; optional capture failures can\n * return an explicit null field rather than blocking the message.\n *\n * Declared structurally rather than as `ThreadOptions['getMessageMetadata']`:\n * the option is what the monorepo patches into\n * `@assistant-ui/react-statewire@0.31.0`, and the published sources must also\n * compile against the unpatched upstream typings, where the library does not\n * know the option and ignores it.\n */\n getMessageMetadata?:\n | (() => Record<string, unknown> | undefined | Promise<Record<string, unknown> | undefined>)\n | undefined;\n /** Host-owned adapters: attachments (upload/mention choreography), speech, feedback. */\n adapters?: ThreadOptions['adapters'];\n /**\n * Transport lifecycle recorder (attach latency, reconnect loops, command\n * settle fates, stale-auth renewals) fed from the hook's own seams. Pass one\n * only when the host's diagnostics channel is enabled — the recorder is\n * observed on hot paths, and an absent recorder costs a single optional call.\n */\n diagnostics?: StatewireDiagnosticsRecorder;\n /**\n * Per-tab LRU of the last wire state seen per thread. When set, every\n * hydrated state the transport publishes is remembered, and a mount of a\n * remembered thread seeds `initialState` from the cache (through\n * {@link toThreadSnapshotSeed}) — the thread renders its last-known\n * messages on the first frame instead of holding a loading treatment for\n * the attach round-trip, and projects \"running\" when the run was live at\n * capture (else \"ready\"). The attach snapshot then\n * REPLACES the seeded state (statewire's `initialState` contract), so a\n * thread that advanced elsewhere reconciles in one round-trip. New chats\n * keep their preload and never read the cache.\n */\n snapshotCache?: DeepAgentThreadSnapshotCache;\n /**\n * The head window this surface wants: declared on every attach\n * (`x-athena-deep-agent-window`, part of the transport recipe) so the hello\n * of a client the host does not remember carries the newest `size`\n * messages, and stated as `athena/interest` after the hello only when the\n * hello's head is narrower — never to shrink it (the host holds what a\n * viewer has loaded for the life of its connection). The resolved chain is\n * joined with the mounted message documents into the converted transcript\n * (see `message-window.ts`); older pages load through\n * `useDeepAgentWindowSync`. Read once per attach.\n */\n messageWindow?: DeepAgentMessageWindowDeclaration;\n /**\n * Set only by a surface that pages a cut lane's earlier steps wherever it\n * shows a lane's steps (`useDeepAgentLaneHistory`). Its attaches then\n * declare the lane-cut contract version, and the host sends a finished\n * `task` card with a deep transcript cut to its newest steps (see\n * `DeepAgentProtocolDeclaration`). Omitted, every attach declares the\n * default version and finished cards ride whole. Part of the transport\n * recipe: a prewarm controller for this thread must be given the same\n * value. Read once per attach.\n */\n laneHistory?: boolean;\n /**\n * Joins the interest window off the mounted documents; the default is\n * {@link readDeepAgentMessageWindow}. Olympus passes\n * `readDeepAgentFollowedWindow`, which also reads the lanes it follows onto\n * their `task` cards.\n */\n readMessageWindow?: typeof readDeepAgentMessageWindow;\n /**\n * Creates each attach's conversion memory; the default is\n * {@link createDeepAgentMessageConversionCache}. Olympus passes\n * `createSubagentTranscriptConversionCache`, which also turns each `task`\n * card's nested transcript into its tool-call part's `messages`.\n */\n createConversionCache?: (options: DeepAgentMessageConversionOptions) => DeepAgentConversionMemory;\n /**\n * Full tool outputs fetched back for the wire's cut tool messages\n * (`history/tool_output`); the thread re-converts when one lands.\n */\n toolOutputs?: DeepAgentToolOutputStore;\n /**\n * User turns this client took back ahead of the wire (a user stop before\n * any assistant output); the thread re-converts when one opens or closes.\n */\n withdrawnTurns?: DeepAgentWithdrawnTurns;\n /**\n * Pre-dialed attaches (see `thread-prewarm.ts`). When the pool holds a root\n * for this thread at mount, the thread takes the hello it replicated as its\n * seed — the transcript is the first frame — and dials its own attach\n * against the instance the pool warmed; the pooled root is disposed in the\n * same step, so one socket is live per thread after mount. Never seeded for\n * a preloaded new chat or a suspended attach; such a root is discarded.\n */\n prewarmPool?: DeepAgentThreadPrewarmPool<DeepAgentStreamValues>;\n /**\n * Attach-path timing: socket open, hello size + handler cost, server retry\n * hints — attributed to the attach's own thread, since a late event from the\n * thread being left can land after the host has moved on.\n */\n onAttachTiming?: (info: { threadId: string; event: DeepAgentAttachTimingEvent }) => void;\n /** Where this attach's first frame comes from; once per attach, at mount. */\n onAttachSource?: (info: { threadId: string; source: DeepAgentAttachSource }) => void;\n /** Full transport error policy (paywall, toast, telemetry) — client-owned. */\n onError?: ThreadOptions['onError'];\n /**\n * Raw statewire state observer (first-attach latches, telemetry). The state\n * argument is never `undefined`/`null`: passes where the wire has no\n * snapshot yet (pre-attach, replay boundaries) deliver the typed\n * `DEEP_AGENT_PRE_STREAM_STATE` default — `isDeepAgentStateHydrated`\n * distinguishes them from a real (possibly empty) snapshot.\n */\n onStateChange?: ThreadOptions['onStateChange'];\n /** Raw command-lifecycle observer (send-failure salvage, telemetry). */\n onCommandChange?: ThreadOptions['onCommandChange'];\n /** Raw connection observer (reconnect handles, telemetry). */\n onRawConnectionChange?: ThreadOptions['onConnectionChange'];\n /**\n * Projected connection status changes, deduped per thread, with `null` on\n * unmount so an indicator never shows a stale status.\n */\n onConnectionChange?: (connection: DeepAgentConnectionState | null) => void;\n /** Notified when a run starts/stops so the page can reflect the running thread. */\n onRunningChange?: (isRunning: boolean) => void;\n /**\n * Notified once per thread with the `runConfig` carried by the statewire\n * snapshot (the backend's persisted panel config, seeded in `_load_history`),\n * so the host can hydrate its config store from the thread. Notified with\n * `null` (at most once per attach) when a server snapshot arrives WITHOUT a\n * persisted config, so a host waiting on hydration can stop waiting; a\n * config appearing on a later snapshot of the same attach (mirrored at run\n * start) still delivers the config notification.\n */\n onServerRunConfig?: (config: Record<string, unknown> | null) => void;\n /**\n * Notified when the snapshot's `outOfBandRunActive` flag changes: the host set\n * it while a run executing OUTSIDE this surface (an AOP / HTTP-route run)\n * is actively writing the thread's bare-namespace history, which the host\n * live-refreshes onto the wire. Lets the page show run-in-progress\n * treatment instead of an empty conversation, and stand down its own\n * re-attach polling. Change-notified per attach (an initial `false` before\n * the snapshot stays silent), mirroring `onRunningChange`.\n */\n onOutOfBandRunActive?: (outOfBandRunActive: boolean) => void;\n /**\n * Notified with the state's session-asset lifecycle flag\n * ({@link readSessionPersisted}) PAIRED with the thread it describes: `false`\n * while the thread's `session` asset provably does not exist yet (a\n * never-started chat's preload, a first run before the mint), `true` once\n * minted, `null` when the state carries no flag (a host that predates it).\n *\n * Fired from the converter pass, so the pair is read from the state of the\n * thread this attach was mounted for. A component reading the flag off the\n * client store instead lags the mounted thread id by one render on a switch\n * and can pair the new id with the previous thread's flag — releasing a\n * session-scoped read for a row the backend has not minted. Change-notified\n * per attach; the first pass of every attach notifies, so a host holding a\n * read back until it hears from the mounted thread always does.\n */\n onSessionPersistedChange?: (event: {\n threadId: string;\n sessionPersisted: boolean | null;\n }) => void;\n /**\n * Notified with the wire-contract status once the attach snapshot\n * advertises the `protocol` lane, and again whenever it changes (a\n * re-attach after a server roll can advertise a new `version`). Telemetry\n * and per-version behaviour only: the host has no minimum client version,\n * so nothing about the status gates a send. Never called for\n * pre-declaration servers (their snapshots carry no lane) — absence of a\n * call means \"no advertisement\", not \"compatible\".\n */\n onProtocolChange?: (status: DeepAgentProtocolStatus) => void;\n}\n\n/** One thread's replicated running state, keyed so a switch between two\n * threads with the same status still re-notifies (the host page clears its\n * per-thread state on selection, mirroring the connection dedupe below). */\nexport interface RunningSnapshot {\n threadId: string;\n isRunning: boolean;\n}\n\n/** Whether the thread's `session` asset exists, read off a statewire state, or\n * `null` when the state does not carry the flag.\n *\n * Absence is not absence of the asset: a snapshot from a host that predates\n * the flag has no such key, and reading that as \"the asset does not exist\"\n * would gate a read the client is entitled to make, with nothing left to\n * un-gate it. A new thread's preload seeds `false` (see `initialState`), so\n * the pre-attach pass of a chat that cannot have an asset yet still gates.\n *\n * Hosts pass this to `useStatewireState` so the flag and the thread it belongs\n * to are read in one component — a thread switch then cannot pair one thread's\n * id with the other's flag. */\nexport function readSessionPersisted(state: unknown): boolean | null {\n const flag = (state as { sessionPersisted?: unknown } | null)?.sessionPersisted;\n return typeof flag === 'boolean' ? flag : null;\n}\n\n/** Whether a converter pass must notify `onRunningChange`.\n *\n * The mount's very first status notifies only when it is `true` (a snapshot\n * carrying a live run) — an initial `false` before the snapshot must stay\n * silent so it cannot clear a still-valid indicator. After that, every\n * transition notifies, and so does the first status after a thread switch\n * (the host page clears its per-thread state on selection, so a switch\n * between two threads with the same status must still re-notify). */\nexport function shouldNotifyRunningChange(\n previous: RunningSnapshot | null,\n next: RunningSnapshot\n): boolean {\n if (previous === null) return next.isRunning;\n return previous.threadId !== next.threadId || previous.isRunning !== next.isRunning;\n}\n\n/** Whether two transcripts hold the very same message objects, in order. */\nfunction isSameMessageArray(\n previous: readonly LangChainMessage[],\n next: readonly LangChainMessage[]\n): boolean {\n return (\n previous.length === next.length && previous.every((message, index) => message === next[index])\n );\n}\n\n/**\n * The shared deep-agent Statewire thread.\n *\n * `StatewireThread` (from `@assistant-ui/react-statewire`) attaches to the\n * agora-hosted thread at `${baseUrl}/${threadId}` (WebSocket by default,\n * with SSE retained only as a host-selected compatibility switch): the backend\n * hosts the thread state and this client\n * replicates it live, so the server snapshot is the single source of history\n * (no local import, no `/status` resume probing) and run scheduling — queueing,\n * steering, stop, edit rewinds — rides the built-in `run/*` commands.\n *\n * The thread element is keyed by `threadId`, so a thread switch remounts the\n * session against the new URL instead of importing history in place.\n *\n * Returns the thread element for the host's own `useAui({ thread })` — web\n * hosts take `useAui` from `@assistant-ui/react`, React Native hosts from\n * `@assistant-ui/react-native`; this package imports neither.\n */\nexport function useDeepAgentThread({\n threadId,\n baseUrl,\n sse = false,\n headers,\n sessionStore,\n storage,\n isNewChat = false,\n attachKey,\n laneEpoch = 0,\n suspended = false,\n capabilities,\n laneProjection = 'both',\n showQuietTools = false,\n selfAuthor = null,\n getMessageMetadata,\n adapters,\n diagnostics,\n snapshotCache,\n messageWindow,\n laneHistory = false,\n readMessageWindow = readDeepAgentMessageWindow,\n createConversionCache = createDeepAgentMessageConversionCache,\n toolOutputs,\n withdrawnTurns,\n prewarmPool,\n onAttachTiming,\n onAttachSource,\n onError,\n onStateChange,\n onCommandChange,\n onRawConnectionChange,\n onConnectionChange,\n onRunningChange,\n onServerRunConfig,\n onOutOfBandRunActive,\n onSessionPersistedChange,\n onProtocolChange,\n}: UseDeepAgentThreadOptions) {\n const onRunningChangeRef = useRef(onRunningChange);\n onRunningChangeRef.current = onRunningChange;\n const onSessionPersistedChangeRef = useRef(onSessionPersistedChange);\n onSessionPersistedChangeRef.current = onSessionPersistedChange;\n const onConnectionChangeRef = useRef(onConnectionChange);\n onConnectionChangeRef.current = onConnectionChange;\n const onServerRunConfigRef = useRef(onServerRunConfig);\n onServerRunConfigRef.current = onServerRunConfig;\n const onOutOfBandRunActiveRef = useRef(onOutOfBandRunActive);\n onOutOfBandRunActiveRef.current = onOutOfBandRunActive;\n const onProtocolChangeRef = useRef(onProtocolChange);\n onProtocolChangeRef.current = onProtocolChange;\n const onErrorRef = useRef(onError);\n onErrorRef.current = onError;\n const onStateChangeRef = useRef(onStateChange);\n onStateChangeRef.current = onStateChange;\n const onCommandChangeRef = useRef(onCommandChange);\n onCommandChangeRef.current = onCommandChange;\n const onRawConnectionChangeRef = useRef(onRawConnectionChange);\n onRawConnectionChangeRef.current = onRawConnectionChange;\n const headersRef = useRef(headers);\n headersRef.current = headers;\n const messageWindowRef = useRef(messageWindow);\n messageWindowRef.current = messageWindow;\n const laneHistoryRef = useRef(laneHistory);\n laneHistoryRef.current = laneHistory;\n // The head interest is checked once per connected session, after the hello\n // negotiated the lane (a statement before that would be minted durable):\n // the hello carries the host's head — the attach header's size, or what\n // this client id held — and a set goes out only to widen it.\n const interestRef = useRef({ attachId: '', stated: false });\n // The last joined transcript, held across the gap between a hello and its\n // interest answer (and across a reconnect) so the thread never blanks.\n const lastMessagesRef = useRef<{ attachId: string; messages: LangChainMessage[] } | null>(null);\n // A fetched tool output must re-run the converter (the store is read inside\n // it); the version is the memo's signal that the store's contents moved.\n const [toolOutputsVersion, setToolOutputsVersion] = useState(0);\n useEffect(() => {\n if (!toolOutputs) return undefined;\n return toolOutputs.subscribe(() => setToolOutputsVersion((version) => version + 1));\n }, [toolOutputs]);\n const [withdrawnTurnsVersion, setWithdrawnTurnsVersion] = useState(0);\n useEffect(() => {\n if (!withdrawnTurns) return undefined;\n return withdrawnTurns.subscribe(() => setWithdrawnTurnsVersion((version) => version + 1));\n }, [withdrawnTurns]);\n const diagnosticsRef = useRef(diagnostics);\n diagnosticsRef.current = diagnostics;\n const onAttachTimingRef = useRef(onAttachTiming);\n onAttachTimingRef.current = onAttachTiming;\n const onAttachSourceRef = useRef(onAttachSource);\n onAttachSourceRef.current = onAttachSource;\n\n const lastRunningRef = useRef<RunningSnapshot | null>(null);\n // Out-of-band run activity (`outOfBandRunActive`), change-notified with the\n // same semantics as the running flag above.\n const lastOutOfBandRunActiveRef = useRef<RunningSnapshot | null>(null);\n // The session-asset lifecycle flag last notified, keyed by attach so every\n // attach's first pass notifies (a host holding a read back until it hears\n // from the mounted thread must always hear from it).\n const lastSessionPersistedRef = useRef<{\n attachId: string;\n threadId: string;\n sessionPersisted: boolean | null;\n } | null>(null);\n // The last wire-contract status notified, keyed by attach so a re-attach\n // re-notifies (the new snapshot may advertise a different version).\n // `status: null` = nothing advertised yet on this attach.\n const lastProtocolRef = useRef<{\n attachId: string;\n status: DeepAgentProtocolStatus | null;\n } | null>(null);\n // The thread whose statewire-carried runConfig has been surfaced to the host\n // (once per thread; later snapshots must not clobber edits).\n const appliedStateRunConfigThreadRef = useRef<string | null>(null);\n // The attach that already notified `onServerRunConfig(null)` (snapshot\n // arrived carrying no persisted config) — at most once per attach.\n const absentStateRunConfigAttachRef = useRef<string | null>(null);\n // Live thread id for the callbacks: they are created per render but may\n // settle after a switch, so they compare against the current id.\n const threadIdRef = useRef(threadId);\n threadIdRef.current = threadId;\n // Identity of the current attach. Every per-thread latch keys off this, so a\n // re-attach of the same thread (a bumped `attachKey`, a new lane epoch)\n // re-notifies the host exactly like a switch would — the host clears its\n // per-thread state on re-attach, so a snapshot that repeats the previous\n // status must not dedupe.\n const attachId: string = deepAgentAttachId({ threadId, attachKey, laneEpoch });\n const attachIdRef = useRef(attachId);\n attachIdRef.current = attachId;\n\n // Auth-denial park: the server fins every denied attach `unauthorized` and\n // the reconnect loop would otherwise retry it every 30s forever (prod tabs\n // looped for hours against revoked/workspace-less credentials). After a\n // streak of denials the live transport is swapped for a parked one; a new\n // attach (thread switch, bumped attachKey) or the parked connection's\n // `reconnect()` re-arms it.\n const [authParked, setAuthParked] = useState<{\n attachId: string;\n message: string | undefined;\n } | null>(null);\n const authDenialRef = useRef<{ attachId: string; state: AuthDenialTrackerState }>({\n attachId,\n state: initialAuthDenialTrackerState(),\n });\n useEffect(() => {\n setAuthParked((prev) => (prev !== null && prev.attachId !== attachId ? null : prev));\n }, [attachId]);\n\n // Field-wise stable identity for the viewer stamp, so a host that rebuilds\n // the author object per render does not rebuild the converter (and with it\n // every message) per render. Adjusting state during render is React's\n // sanctioned derived-state pattern: it re-renders once with the new value.\n const [stableSelfAuthor, setStableSelfAuthor] = useState(selfAuthor);\n if (!isSameMessageAuthor(stableSelfAuthor, selfAuthor)) setStableSelfAuthor(selfAuthor);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: toolOutputsVersion / withdrawnTurnsVersion are the stores' change signals — the converter reads the stores' contents, not the counters.\n const converter = useMemo(\n () => (state: unknown, isRunning: boolean, conversionCache: DeepAgentConversionMemory) => {\n // Statewire snapshots carry the thread's persisted panel config (the\n // backend seeds `runConfig` in `_load_history` and mirrors it on run\n // start). Surface it once per thread so the host can hydrate its config\n // store.\n const stateRunConfig = (state as { runConfig?: unknown } | null)?.runConfig;\n if (\n stateRunConfig &&\n typeof stateRunConfig === 'object' &&\n !Array.isArray(stateRunConfig) &&\n appliedStateRunConfigThreadRef.current !== attachIdRef.current\n ) {\n appliedStateRunConfigThreadRef.current = attachIdRef.current;\n const notifyRunConfig = onServerRunConfigRef.current;\n if (notifyRunConfig) {\n queueMicrotask(() => notifyRunConfig(stateRunConfig as Record<string, unknown>));\n }\n } else if (\n !stateRunConfig &&\n appliedStateRunConfigThreadRef.current !== attachIdRef.current &&\n absentStateRunConfigAttachRef.current !== attachIdRef.current &&\n projectDeepAgentProtocol(state) !== null\n ) {\n // No persisted config on a SERVER state: the protocol lane rides every\n // attach snapshot, so its presence distinguishes \"the server answered\n // without a config\" from the pre-attach `initialState` (which must not\n // end the host's hydration wait).\n absentStateRunConfigAttachRef.current = attachIdRef.current;\n const notifyRunConfig = onServerRunConfigRef.current;\n if (notifyRunConfig) {\n queueMicrotask(() => notifyRunConfig(null));\n }\n }\n // Out-of-band run activity: the host live-refreshes a bare-namespace\n // (AOP / HTTP-route) thread's transcript while its run is active and\n // flags the wire state so the page can reflect it.\n const stateOutOfBandRunActive =\n (state as { outOfBandRunActive?: unknown } | null)?.outOfBandRunActive === true;\n const outOfBandRunActiveSnapshot = {\n threadId: attachIdRef.current,\n isRunning: stateOutOfBandRunActive,\n };\n if (shouldNotifyRunningChange(lastOutOfBandRunActiveRef.current, outOfBandRunActiveSnapshot)) {\n lastOutOfBandRunActiveRef.current = outOfBandRunActiveSnapshot;\n const observedAttachId = attachIdRef.current;\n queueMicrotask(() => {\n // A re-attach or newer snapshot between the converter pass and this\n // microtask owns the channel now — a stale flag must not leak into\n // the new attach's per-thread state.\n if (\n attachIdRef.current !== observedAttachId ||\n lastOutOfBandRunActiveRef.current !== outOfBandRunActiveSnapshot\n ) {\n return;\n }\n onOutOfBandRunActiveRef.current?.(stateOutOfBandRunActive);\n });\n }\n // Wire-contract advertisement: the host publishes the `protocol` lane in\n // every attach snapshot. Notify on first advertisement and on change\n // (dedupe by value, re-keyed per attach), with the same stale-guard\n // microtask as the other observers.\n const protocolStatus = projectDeepAgentProtocol(state, {\n laneHistory: laneHistoryRef.current,\n });\n if (protocolStatus !== null) {\n const previous =\n lastProtocolRef.current?.attachId === attachIdRef.current\n ? lastProtocolRef.current.status\n : null;\n if (previous === null || previous.protocolVersion !== protocolStatus.protocolVersion) {\n const update = { attachId: attachIdRef.current, status: protocolStatus };\n lastProtocolRef.current = update;\n queueMicrotask(() => {\n if (lastProtocolRef.current !== update || attachIdRef.current !== update.attachId) {\n return;\n }\n onProtocolChangeRef.current?.(protocolStatus);\n });\n }\n }\n const runningSnapshot = { threadId: attachIdRef.current, isRunning };\n if (shouldNotifyRunningChange(lastRunningRef.current, runningSnapshot)) {\n lastRunningRef.current = runningSnapshot;\n const notify = onRunningChangeRef.current;\n if (notify) queueMicrotask(() => notify(isRunning));\n }\n return {\n ...convertDeepAgentState(state, {\n laneProjection,\n selfAuthor: stableSelfAuthor,\n toolOutputs,\n withdrawnTurns,\n conversionCache,\n }),\n state: state as StatewireThread.State['state'],\n };\n },\n [\n laneProjection,\n stableSelfAuthor,\n toolOutputs,\n toolOutputsVersion,\n withdrawnTurns,\n withdrawnTurnsVersion,\n ]\n );\n const materialize = useMemo(\n () => createDeepAgentMaterialize({ selfAuthor: stableSelfAuthor }),\n [stableSelfAuthor]\n );\n\n // Surface the statewire connection status through `onConnectionChange` (the\n // converter re-runs on connection changes), and clear it when the runtime\n // unmounts so an indicator never shows a stale status. Dedup is keyed by\n // thread: the runtime stays mounted across idle thread switches, so the new\n // thread's first status must notify even when it equals the previous\n // thread's last one.\n const lastConnectionRef = useRef<{\n threadId: string;\n connection: DeepAgentConnectionState;\n } | null>(null);\n useEffect(\n () => () => {\n lastConnectionRef.current = null;\n onConnectionChangeRef.current?.(null);\n },\n []\n );\n\n // The preload decision pins at thread entry: `isNewChat` flips once the\n // thread persists, but the mount that began as a new thread stays preloaded.\n // A new lane epoch enters the thread again, as another principal, so it pins\n // afresh: a thread that persisted under the previous principal attaches at\n // once and loads, rather than waiting for the new principal's first send.\n const preloadRef = useRef({ threadId, laneEpoch, preload: isNewChat });\n if (preloadRef.current.threadId !== threadId || preloadRef.current.laneEpoch !== laneEpoch)\n preloadRef.current = { threadId, laneEpoch, preload: isNewChat };\n\n const snapshotCacheRef = useRef(snapshotCache);\n snapshotCacheRef.current = snapshotCache;\n // The snapshot seed pins at attach identity, like the preload decision: the\n // cache is read once per mount, so a mid-session cache write (this thread's\n // own states being remembered) never changes what `initialState` was.\n //\n // A suspended attach never seeds: the suspended transport never dials, so a\n // seed would be served indefinitely — under a view-as lens that would paint\n // thread content the parked transport exists to withhold (the lens must\n // show what the LENSED identity is authorized to see, and only the server\n // can decide that).\n //\n // The pooled hello is taken the same way: exactly once, at the attach's\n // first render (the pool disposes its root in that step).\n //\n // The first attach of a new lane epoch belongs to another principal: what\n // this tab remembered of the thread, and a dial pooled for it, were captured\n // as the previous one. Neither seeds it, and the remembered state is dropped\n // so no later attach of the thread paints it either.\n const snapshotSeedRef = useRef<{\n attachId: string;\n laneEpoch: number;\n seed: DeepAgentStreamValues | undefined;\n } | null>(null);\n if (snapshotSeedRef.current === null || snapshotSeedRef.current.attachId !== attachId) {\n const preload = preloadRef.current.preload;\n const laneReplaced =\n snapshotSeedRef.current !== null && snapshotSeedRef.current.laneEpoch !== laneEpoch;\n if (laneReplaced) snapshotCache?.forget(threadId);\n let pooledState: DeepAgentStreamValues | undefined;\n if (prewarmPool?.has(threadId)) {\n if (preload || suspended || laneReplaced) {\n // This mount attaches on its own terms (or not at all): the pooled\n // dial must not linger as a second socket to the same session.\n prewarmPool.discard(threadId);\n } else {\n pooledState = prewarmPool.take(threadId)?.state;\n }\n }\n // The pooled hello outranks a remembered snapshot (it is newer by\n // construction); a pooled dial that had not landed seeds like a plain\n // attach and reconciles when this attach's own hello lands.\n const remembered =\n preload || suspended || laneReplaced\n ? undefined\n : (pooledState ?? snapshotCache?.take(threadId));\n const seed = remembered ? toThreadSnapshotSeed(remembered) : undefined;\n snapshotSeedRef.current = { attachId, laneEpoch, seed };\n const source: DeepAgentAttachSource =\n pooledState !== undefined ? 'prewarm' : seed ? 'snapshot' : 'none';\n const notifySource = onAttachSourceRef.current;\n if (notifySource) queueMicrotask(() => notifySource({ threadId, source }));\n }\n\n // The conversion memory pins at attach identity like the snapshot seed: an\n // unchanged message converts to the identical thread message on every pass\n // of this attach, and a switch or re-attach starts from an empty memory, so\n // one thread's converted messages can never be served for another.\n // A change in who may see quiet tools starts a new memory too, so every\n // message reconverts under the new setting.\n const conversionCacheRef = useRef<{\n attachId: string;\n showQuietTools: boolean;\n cache: DeepAgentConversionMemory;\n } | null>(null);\n if (\n conversionCacheRef.current === null ||\n conversionCacheRef.current.attachId !== attachId ||\n conversionCacheRef.current.showQuietTools !== showQuietTools\n ) {\n conversionCacheRef.current = {\n attachId,\n showQuietTools,\n cache: createConversionCache({ showQuietTools }),\n };\n }\n const conversionCache = conversionCacheRef.current.cache;\n // The last pass's joined transcript, reused when a packet moved no message\n // (a run-status flip, a todo, another document) so readers memoized on\n // `state.messages` skip that pass.\n const lastStateMessagesRef = useRef<{ attachId: string; messages: LangChainMessage[] } | null>(\n null\n );\n\n // react-statewire memoizes the converted thread on this function's\n // identity, so an inline closure would re-run `convertDeepAgentState` over\n // the whole transcript on every host render. Keyed on the attach identity (a\n // re-attach re-keys its per-thread latches); everything else the body needs\n // is read through the per-render refs above.\n const threadConverter = useCallback<ThreadOptions['converter']>(\n (wireState, meta) => {\n // The wire main document carries no messages: join the interest window\n // off the mounted documents, fold the dispatch record onto it, and hand\n // the composite on as the thread state every reader sees.\n const values = readDeepAgentStreamValues(wireState);\n const window = readMessageWindow(meta.mounted);\n const held =\n lastMessagesRef.current?.attachId === attachId ? lastMessagesRef.current.messages : null;\n const messages = window?.messages ?? values.messages ?? held ?? [];\n if (window !== null || messages.length > 0) lastMessagesRef.current = { attachId, messages };\n const state: DeepAgentStreamValues = { ...values, messages: [...messages] };\n // Every state this attach publishes names its thread, so a binding\n // mounted for the next thread can tell this state apart from its own\n // for the render it is still projected after a switch.\n pairDeepAgentStateWithThread({ state, threadId });\n materialize(state);\n const joined: LangChainMessage[] = state.messages ?? [];\n const previousJoined = lastStateMessagesRef.current;\n if (\n previousJoined !== null &&\n previousJoined.attachId === attachId &&\n isSameMessageArray(previousJoined.messages, joined)\n ) {\n state.messages = previousJoined.messages;\n } else {\n lastStateMessagesRef.current = { attachId, messages: joined };\n }\n if (window !== null && isDeepAgentStateHydrated(values)) {\n snapshotCacheRef.current?.remember(threadId, state);\n }\n if (interestRef.current.attachId !== attachId) {\n interestRef.current = { attachId, stated: false };\n }\n if (meta.connection.status !== 'connected') {\n interestRef.current.stated = false;\n } else if (!interestRef.current.stated) {\n interestRef.current.stated = true;\n const wanted = headInterestRequest(messageWindowRef.current?.size);\n const head = window?.requests[0];\n // The hello's head is kept whenever it is at least what this surface\n // wants (a remembered client's wider view, the declared header):\n // stating a narrower head would shrink what the viewer holds. A\n // narrower head — or none, a host without the lane — is widened, the\n // hello's pages kept alongside.\n const preloaded = head !== undefined && head.at === undefined && head.before >= wanted.before;\n if (!preloaded) {\n const requests = [\n wanted,\n ...(window?.requests.filter((request) => request.at !== undefined) ?? []),\n ];\n queueMicrotask(() => {\n if (attachIdRef.current !== attachId) return;\n meta\n .invoke(DEEP_AGENT_INTEREST_PROTOCOL, 'set', [requests])\n .catch((error: unknown) => {\n onErrorRef.current?.(error instanceof Error ? error : new Error(String(error)));\n });\n });\n }\n }\n // Projected once per pass through the single seam; every fold below\n // consumes the projection (the recorder projects the raw connection\n // itself, so any host observer can feed it).\n const connection = projectDeepAgentConnection(meta.connection);\n // Transition-deduped inside the recorder, so this per-pass observation\n // is free while the connection is unchanged.\n diagnosticsRef.current?.observeConnection(meta.connection);\n // Fold this pass's connection into the denial streak (the converter\n // re-runs on every reconnect-attempt bump). Parking is deferred to a\n // microtask like every other notification here.\n if (authDenialRef.current.attachId !== attachIdRef.current) {\n authDenialRef.current = {\n attachId: attachIdRef.current,\n state: initialAuthDenialTrackerState(),\n };\n }\n const denial = observeAuthDenial(authDenialRef.current.state, connection);\n authDenialRef.current.state = denial.state;\n if (denial.park) {\n const parkAttachId = attachIdRef.current;\n const parkMessage = denial.state.message;\n // The server refused authorization for this thread: any remembered\n // snapshot must not warm-paint it on a later mount (permissions may\n // have been revoked since the state was captured).\n snapshotCacheRef.current?.forget(threadId);\n queueMicrotask(() => {\n if (attachIdRef.current !== parkAttachId) return;\n setAuthParked((prev) =>\n prev?.attachId === parkAttachId\n ? prev\n : { attachId: parkAttachId, message: parkMessage }\n );\n });\n }\n const previous = lastConnectionRef.current;\n if (\n previous?.threadId !== attachId ||\n !Object.keys({ ...previous.connection, ...connection }).every(\n (key) => Reflect.get(previous.connection, key) === Reflect.get(connection, key)\n )\n ) {\n const update = { threadId: attachId, connection };\n lastConnectionRef.current = update;\n queueMicrotask(() => {\n // A newer projection, a thread switch, or the unmount cleanup owns\n // the channel now.\n if (lastConnectionRef.current !== update || attachIdRef.current !== attachId) return;\n onConnectionChangeRef.current?.(connection);\n });\n }\n // Session-asset lifecycle, paired with THIS attach's thread by closure:\n // the state converted here is the state of the thread the element was\n // built for, so the pair can never straddle a switch (a client-store\n // read would). Deduped by value per attach. A pass from an attach the\n // host has already replaced never touches the latch — it could otherwise\n // cancel the current attach's still-queued verdict — and the microtask\n // guard drops a pass whose attach is replaced before it settles.\n if (attachIdRef.current === attachId) {\n const sessionPersisted = readSessionPersisted(state);\n const lastSession = lastSessionPersistedRef.current;\n if (\n lastSession?.attachId !== attachId ||\n lastSession.sessionPersisted !== sessionPersisted\n ) {\n const sessionUpdate = { attachId, threadId, sessionPersisted };\n lastSessionPersistedRef.current = sessionUpdate;\n queueMicrotask(() => {\n if (\n lastSessionPersistedRef.current !== sessionUpdate ||\n attachIdRef.current !== attachId\n ) {\n return;\n }\n onSessionPersistedChangeRef.current?.({ threadId, sessionPersisted });\n });\n }\n }\n // The replicated run status IS the running signal: the lib's run-dialect\n // fold flips it to \"running\" on the send, before the app converter runs,\n // so the optimistic window needs no hand-tracked flag.\n return converter(state, state.runs?.[0]?.status === 'running', conversionCache);\n },\n [attachId, conversionCache, converter, materialize, readMessageWindow, threadId]\n );\n\n const thread = StatewireThread<DeepAgentStreamValues>({\n // The transport stamps this protocol on every command; the thread client\n // must declare it too or the core skips the optimistic folds and a send\n // stays invisible until the server frame lands.\n protocol: DEEP_AGENT_STATEWIRE_PROTOCOL,\n // New threads preload an empty runs mount and report `deferred` — the\n // composer is interactive before the stream attaches and pre-attach sends\n // render through the optimistic folds. Existing threads omit the runs\n // mount and project \"loading\" until the attach snapshot: the server seeds\n // messages/todos/runConfig in `_load_history`.\n //\n // The preload also seeds `sessionPersisted: false`: a never-started chat\n // provably has no `session` asset until its first run mints one, so the\n // wire carries that from the first render and a host reading the flag off\n // the state needs no separate \"is this a new chat\" knowledge. An existing\n // thread leaves it absent (unknown) so its asset read is never gated.\n initialState:\n snapshotSeedRef.current.seed ??\n ({\n messages: [],\n todos: [],\n ...(preloadRef.current.preload && { runs: [], sessionPersisted: false }),\n } satisfies DeepAgentStreamValues),\n // Keyed by thread: a switch unmounts the previous thread's session and\n // remounts against the new URL, so the converter never sees the previous\n // thread's snapshot (a new thread serves its preload again, an existing\n // one loads until its snapshot). A host-suspended or auth-parked attach\n // swaps in the never-dialing transport instead.\n transport: withKey(\n attachId,\n (() => {\n if (suspended) return StatewireSuspended({ message: suspended.reason });\n if (authParked !== null && authParked.attachId === attachId) {\n return StatewireSuspended({\n message: authParked.message ?? 'Session authorization was refused.',\n onReconnectRequested: () => {\n authDenialRef.current = {\n attachId: attachIdRef.current,\n state: initialAuthDenialTrackerState(),\n };\n setAuthParked(null);\n },\n });\n }\n // The shared recipe (URL, auth + wire-contract headers, session lane,\n // attach instrumentation, retry-hint floor) — the same one the prewarm\n // pool dials with, so the instance a pooled attach warmed is the one\n // this dial reaches. A never-started chat dials nothing until its\n // first send.\n return createDeepAgentAttachTransport<DeepAgentStreamValues>({\n baseUrl,\n threadId,\n sse,\n isNew: preloadRef.current.preload,\n headers: (ctx) => headersRef.current(ctx),\n messageWindow: messageWindowRef.current,\n laneHistory: laneHistoryRef.current,\n sessionStore,\n onAuthResolve: (ctx) => diagnosticsRef.current?.observeAuthResolve(ctx),\n onAttachTiming: (event) => onAttachTimingRef.current?.({ threadId, event }),\n });\n })()\n ),\n // Survives the transport remounts above: a bumped attachKey re-attaches the\n // same lane, and a persistent session storage carries it across reloads.\n ...(storage && { storage }),\n // The run subsystem (status, lanes, input requests, runId) is mounted on\n // the state root and always replicated by react-statewire >= 0.14 (the\n // former `runs: true` opt-in is gone) — the converter only maps app-owned\n // keys, including the `run/stop` runId stamp the lib projects itself.\n converter: threadConverter,\n // Native message editing: an edited turn is resubmitted as a `run/edit`\n // command, and the deep-agent backend rewinds the checkpointed thread to\n // the edited message's parent before appending (route `plan_rewind`).\n capabilities: capabilities ?? { edit: true },\n ...(getMessageMetadata && { getMessageMetadata }),\n ...(adapters && { adapters }),\n contextSync: 'command-send',\n // The Stop control is the only `cancelRun()` caller — teardown paths\n // unmount the keyed transport instead — so every `run/stop` is a\n // deliberate user stop.\n stopPayload: () => ({ reason: 'user_stop' }),\n onError: (error) => {\n diagnosticsRef.current?.observeError(error);\n onErrorRef.current?.(error);\n },\n ...(onStateChange && {\n // The lib types the observed state `NonNullable`, but the runtime\n // delivers `undefined` where the wire has no snapshot (pre-attach and\n // replay boundaries republish it) — normalize so the type is true and\n // host observers never crash on a field read.\n onStateChange: ((state, ctx) => {\n onStateChangeRef.current?.(readDeepAgentStreamValues(state), ctx);\n }) satisfies ThreadOptions['onStateChange'],\n }),\n ...((onCommandChange || diagnostics) && {\n onCommandChange: ((update, ctx) => {\n diagnosticsRef.current?.observeCommand(update);\n onCommandChangeRef.current?.(update, ctx);\n }) satisfies ThreadOptions['onCommandChange'],\n }),\n ...(onRawConnectionChange && {\n onConnectionChange: ((connection) =>\n onRawConnectionChangeRef.current?.(\n connection\n )) satisfies ThreadOptions['onConnectionChange'],\n }),\n });\n // A lane epoch is another principal's thread. The key remounts every\n // resource the previous one held (the statewire session, its storage, the\n // composer, the core's state) rather than moving them onto the new lane; the\n // client built from the element keeps its identity across the remount.\n return laneEpoch === 0 ? thread : withKey(`lane-epoch:${laneEpoch}`, thread);\n}\n"],"names":["messageWindow","readDeepAgentMessageWindow","createDeepAgentMessageConversionCache","useRef","useState","useEffect","initialAuthDenialTrackerState","isSameMessageAuthor","useMemo","conversionCache","projectDeepAgentProtocol","_a","convertDeepAgentState","createDeepAgentMaterialize","toThreadSnapshotSeed","useCallback","readDeepAgentStreamValues","messages","pairDeepAgentStateWithThread","isDeepAgentStateHydrated","headInterestRequest","DEEP_AGENT_INTEREST_PROTOCOL","projectDeepAgentConnection","observeAuthDenial","StatewireThread","DEEP_AGENT_STATEWIRE_PROTOCOL","withKey","StatewireSuspended","createDeepAgentAttachTransport"],"mappings":"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"}