@athenaintel/react 0.54.0 → 0.55.0

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 (93) hide show
  1. package/README.md +19 -0
  2. package/dist/agent-runtime/src/edit-command.cjs +9 -3
  3. package/dist/agent-runtime/src/edit-command.cjs.map +1 -1
  4. package/dist/agent-runtime/src/edit-command.js +9 -3
  5. package/dist/agent-runtime/src/edit-command.js.map +1 -1
  6. package/dist/agent-runtime/src/generated/runconfig-contract.cjs +4 -0
  7. package/dist/agent-runtime/src/generated/runconfig-contract.cjs.map +1 -1
  8. package/dist/agent-runtime/src/generated/runconfig-contract.d.ts +12 -1
  9. package/dist/agent-runtime/src/generated/runconfig-contract.js +4 -0
  10. package/dist/agent-runtime/src/generated/runconfig-contract.js.map +1 -1
  11. package/dist/agent-runtime/src/history-pages.cjs +279 -0
  12. package/dist/agent-runtime/src/history-pages.cjs.map +1 -0
  13. package/dist/agent-runtime/src/history-pages.d.ts +252 -0
  14. package/dist/agent-runtime/src/history-pages.js +279 -0
  15. package/dist/agent-runtime/src/history-pages.js.map +1 -0
  16. package/dist/agent-runtime/src/index.d.ts +2 -1
  17. package/dist/agent-runtime/src/offline-status.cjs.map +1 -1
  18. package/dist/agent-runtime/src/offline-status.d.ts +31 -2
  19. package/dist/agent-runtime/src/offline-status.js.map +1 -1
  20. package/dist/agent-runtime/src/use-deep-agent-thread.cjs +16 -2
  21. package/dist/agent-runtime/src/use-deep-agent-thread.cjs.map +1 -1
  22. package/dist/agent-runtime/src/use-deep-agent-thread.d.ts +9 -1
  23. package/dist/agent-runtime/src/use-deep-agent-thread.js +16 -2
  24. package/dist/agent-runtime/src/use-deep-agent-thread.js.map +1 -1
  25. package/dist/agent-runtime/src/use-deep-agent-window-sync.cjs +71 -19
  26. package/dist/agent-runtime/src/use-deep-agent-window-sync.cjs.map +1 -1
  27. package/dist/agent-runtime/src/use-deep-agent-window-sync.d.ts +27 -2
  28. package/dist/agent-runtime/src/use-deep-agent-window-sync.js +73 -21
  29. package/dist/agent-runtime/src/use-deep-agent-window-sync.js.map +1 -1
  30. package/dist/chat/super-grouping/SuperGroupingCard.cjs +51 -43
  31. package/dist/chat/super-grouping/SuperGroupingCard.cjs.map +1 -1
  32. package/dist/chat/super-grouping/SuperGroupingCard.js +51 -43
  33. package/dist/chat/super-grouping/SuperGroupingCard.js.map +1 -1
  34. package/dist/chat/super-grouping/SuperGroupingTranscript.cjs +9 -4
  35. package/dist/chat/super-grouping/SuperGroupingTranscript.cjs.map +1 -1
  36. package/dist/chat/super-grouping/SuperGroupingTranscript.js +9 -4
  37. package/dist/chat/super-grouping/SuperGroupingTranscript.js.map +1 -1
  38. package/dist/chat/super-grouping/activity-timeline.cjs +7 -4
  39. package/dist/chat/super-grouping/activity-timeline.cjs.map +1 -1
  40. package/dist/chat/super-grouping/activity-timeline.d.ts +10 -1
  41. package/dist/chat/super-grouping/activity-timeline.js +7 -4
  42. package/dist/chat/super-grouping/activity-timeline.js.map +1 -1
  43. package/dist/chat/super-grouping/earlier-steps-button.cjs +50 -0
  44. package/dist/chat/super-grouping/earlier-steps-button.cjs.map +1 -0
  45. package/dist/chat/super-grouping/earlier-steps-button.d.ts +11 -0
  46. package/dist/chat/super-grouping/earlier-steps-button.js +50 -0
  47. package/dist/chat/super-grouping/earlier-steps-button.js.map +1 -0
  48. package/dist/chat/super-grouping/use-turn-steps.cjs +28 -0
  49. package/dist/chat/super-grouping/use-turn-steps.cjs.map +1 -0
  50. package/dist/chat/super-grouping/use-turn-steps.d.ts +23 -0
  51. package/dist/chat/super-grouping/use-turn-steps.js +28 -0
  52. package/dist/chat/super-grouping/use-turn-steps.js.map +1 -0
  53. package/dist/chat-ui/statewire.cjs +6 -0
  54. package/dist/chat-ui/statewire.cjs.map +1 -1
  55. package/dist/chat-ui/statewire.d.ts +3 -1
  56. package/dist/chat-ui/statewire.js +7 -1
  57. package/dist/chat-ui/statewire.js.map +1 -1
  58. package/dist/chat-ui/thread/statewire-history-edge.cjs +4 -0
  59. package/dist/chat-ui/thread/statewire-history-edge.cjs.map +1 -1
  60. package/dist/chat-ui/thread/statewire-history-edge.d.ts +11 -1
  61. package/dist/chat-ui/thread/statewire-history-edge.js +5 -1
  62. package/dist/chat-ui/thread/statewire-history-edge.js.map +1 -1
  63. package/dist/collab/client.cjs +40 -1
  64. package/dist/collab/client.cjs.map +1 -1
  65. package/dist/collab/client.d.ts +17 -0
  66. package/dist/collab/client.js +40 -1
  67. package/dist/collab/client.js.map +1 -1
  68. package/dist/composer/QuoteContext.cjs +3 -3
  69. package/dist/composer/QuoteContext.cjs.map +1 -1
  70. package/dist/composer/QuoteContext.js +3 -3
  71. package/dist/composer/QuoteContext.js.map +1 -1
  72. package/dist/provider/AthenaProvider.cjs +11 -3
  73. package/dist/provider/AthenaProvider.cjs.map +1 -1
  74. package/dist/provider/AthenaProvider.js +12 -4
  75. package/dist/provider/AthenaProvider.js.map +1 -1
  76. package/dist/provider/statewire-window-context.cjs +6 -0
  77. package/dist/provider/statewire-window-context.cjs.map +1 -1
  78. package/dist/provider/statewire-window-context.d.ts +10 -1
  79. package/dist/provider/statewire-window-context.js +6 -0
  80. package/dist/provider/statewire-window-context.js.map +1 -1
  81. package/dist/react/package.json.cjs +1 -1
  82. package/dist/react/package.json.js +1 -1
  83. package/dist/runtime/useAthenaStatewireRuntime.cjs +2 -0
  84. package/dist/runtime/useAthenaStatewireRuntime.cjs.map +1 -1
  85. package/dist/runtime/useAthenaStatewireRuntime.d.ts +7 -1
  86. package/dist/runtime/useAthenaStatewireRuntime.js +2 -0
  87. package/dist/runtime/useAthenaStatewireRuntime.js.map +1 -1
  88. package/dist/styles.css +1 -1
  89. package/dist/threads/sessions-query.cjs +2 -0
  90. package/dist/threads/sessions-query.cjs.map +1 -1
  91. package/dist/threads/sessions-query.js +2 -0
  92. package/dist/threads/sessions-query.js.map +1 -1
  93. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"use-deep-agent-window-sync.cjs","sources":["../../../../agent-runtime/src/use-deep-agent-window-sync.ts"],"sourcesContent":["import type { StatewireThread } from '@assistant-ui/react-statewire';\nimport { useAuiState } from '@assistant-ui/store';\nimport { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';\nimport {\n DEEP_AGENT_INTEREST_PROTOCOL,\n DEEP_AGENT_MAX_MESSAGE_WINDOW,\n DEEP_AGENT_OVERSIZED_INTEREST_CODE,\n} from './generated/runconfig-contract';\nimport {\n buildToolOutputCommand,\n type DeepAgentInterestRequest,\n type DeepAgentMessageWindow,\n messageWindowCarries,\n pageInterestRequests,\n readDeepAgentMessageWindow,\n readToolOutputResult,\n readTruncatedToolOutputArtifact,\n restatedInterestRequests,\n} from './message-window';\nimport { hasStatewireThreadExtras } from './thread-extras';\nimport type { DeepAgentToolOutputStore } from './tool-outputs';\n\n/**\n * Pages of older history chained per approach to the top. A renderer lowers\n * its near-top level once prepended content has pushed the first message out\n * of its band; this bounds the case where it cannot (no viewport to scroll,\n * a list too short to overflow) so a 2,000-message thread never streams whole.\n */\nexport const DEEP_AGENT_MAX_CHAINED_HISTORY_LOADS = 3;\n\n/**\n * Pages of older history that add no converted message — every message on\n * them folds into the first loaded turn, such as one agent run's hundreds of\n * tool steps — loaded on top of a request (or an approach's chain) before it\n * stops. Such a page changes nothing the user can see, so it neither answers\n * an ask nor counts against the per-approach chain.\n *\n * A page loaded while the oldest loaded message is a reply lands inside that\n * reply's turn, so it asks for the host's largest window\n * (`DEEP_AGENT_MAX_MESSAGE_WINDOW`) instead of the thread's: a run of 373\n * tool calls is about 750 messages, nineteen round trips at the default\n * window and four at the largest, and the turn shows nothing new until its\n * prompt does. A page above a prompt keeps the thread's window.\n */\nexport const DEEP_AGENT_MAX_UNSEEN_HISTORY_LOADS = 25;\n\n/** The converted thread messages the tool-output half scans (web and native `ThreadMessage` both satisfy it). */\nexport interface DeepAgentWindowSyncMessage {\n readonly id: string;\n /** The converted role; lets the opening fill tell a turn's prompt from its reply. */\n readonly role?: string;\n readonly content: readonly { readonly type: string; readonly artifact?: unknown }[];\n}\n\n/** One page interest round trip, for the host's telemetry. */\nexport interface DeepAgentHistoryLoadEvent {\n /** The page's ordinal (`1` extends the head). */\n page: number;\n request: DeepAgentInterestRequest;\n durationMs: number;\n}\n\nexport interface UseDeepAgentWindowSyncOptions {\n /**\n * The converted thread messages — read by the host through its platform\n * `useAuiState((s) => s.thread.messages)`, so this hook imports neither\n * `@assistant-ui/react` nor `@assistant-ui/react-native`.\n */\n messages: readonly DeepAgentWindowSyncMessage[];\n /** Messages per page; the thread's window size by default. */\n pageSize?: number;\n /**\n * Full tool outputs fetched back for cut tool messages; the same store the\n * thread converts with (`useDeepAgentThread({ toolOutputs })`). Omitted, the\n * tool-output half is inert (a surface that never shows a cut output whole).\n */\n toolOutputs?: DeepAgentToolOutputStore | undefined;\n /**\n * The renderer's report that the top of the LOADED history is near — a\n * level, not an edge: while it holds and older messages exist, pages load\n * one after another (bounded by `maxChainedLoads` per approach). Omitted for\n * hosts with only a manual affordance (`loadOlder`).\n */\n nearTop?: boolean;\n /** Bumped on every rise of `nearTop` (and every explicit request), so the chain bound is per approach. */\n nearTopEpoch?: number;\n /**\n * Whether a message's content is mounted right now, so lazy fetches (cut\n * tool outputs) target what the user can see rather than the whole loaded\n * history. Omitted: every message counts as mounted (the flat renderers).\n */\n isMessageMounted?: ((messageId: string) => boolean) | undefined;\n /**\n * Cut tool outputs this mount reads at once; the rest wait for one to\n * settle. One mounted message can hold a whole agent run — a hundred tool\n * steps — and the host rate-limits a client's frames, so a reader that sends\n * them all together gets refusals back for most of them. Omitted: every\n * mounted output is asked for together (renderers whose mounted set is\n * already small).\n */\n maxConcurrentToolOutputs?: number;\n maxChainedLoads?: number;\n /** Pages adding no converted message loaded per request before it stops; see {@link DEEP_AGENT_MAX_UNSEEN_HISTORY_LOADS}. */\n maxUnseenLoads?: number;\n /** A page interest resolved (telemetry). */\n onHistoryLoad?: ((event: DeepAgentHistoryLoadEvent) => void) | undefined;\n /** A read failed; the sync itself recovers (the pending marks are released). */\n onError?: ((error: unknown, command: 'athena/interest' | 'history/tool_output') => void) | undefined;\n now?: () => number;\n}\n\nexport interface DeepAgentWindowSyncState {\n /** The joined window, or `null` while the head interest has not resolved (both halves are inert). */\n window: DeepAgentMessageWindow | null;\n /** Whether the host holds messages older than the loaded window. */\n hasOlder: boolean;\n /**\n * Whether the next page can be asked for: older messages exist and the\n * view has room for them. `hasOlder && !canLoadOlder` is the depth limit —\n * an edge row says so instead of offering a dead button. The host bounds\n * one attach's view twice: `MAX_INTERESTS` requests, and an interest\n * document that lists every loaded message within a byte budget (about a\n * thousand messages). The second is found when the host refuses a page as\n * `oversized-interest` ({@link isOversizedInterestRejection}).\n */\n canLoadOlder: boolean;\n /** A page of older history is in flight. */\n loading: boolean;\n /** Ask for the next page of older history now (an edge row's button, a keypress). A no-op before the head resolved, at the beginning, or once `canLoadOlder` is false. */\n loadOlder: () => void;\n}\n\n/**\n * Whether an `athena/interest` `set` came back refused because the windows it\n * states resolve past what one attach's interest document carries: the view\n * is as deep as this connection can load, and asking again only gets the same\n * refusal.\n */\nexport function isOversizedInterestRejection(error: unknown): boolean {\n const failure = (error as { failure?: { fate?: unknown; code?: unknown } } | null | undefined)\n ?.failure;\n return failure?.fate === 'rejected' && failure.code === DEEP_AGENT_OVERSIZED_INTEREST_CODE;\n}\n\nconst everyMessageMounted = (): boolean => true;\nconst defaultNow = (): number => Date.now();\nconst noopSubscribe = () => () => {};\n\n/** The statewire extras of the nearest aui thread; throws on a non-statewire thread like the library's own hooks. */\nconst useStatewireExtras = (): StatewireThread.Extras =>\n useAuiState((s) => {\n if (!hasStatewireThreadExtras(s.thread.extras)) {\n throw new Error('useDeepAgentWindowSync requires a statewire thread');\n }\n return s.thread.extras as StatewireThread.Extras;\n });\n\n/** The joined interest window of the nearest statewire thread. */\nexport function useDeepAgentMessageWindow(): DeepAgentMessageWindow | null {\n const documents = useStatewireExtras().documents;\n const mounted = useSyncExternalStore(\n documents?.subscribe ?? noopSubscribe,\n () => documents?.list(),\n () => documents?.list()\n );\n return useMemo(() => readDeepAgentMessageWindow(mounted), [mounted]);\n}\n\n/**\n * The client half of the interest window, shared by every surface — mounted\n * once per runtime, inside its aui provider.\n *\n * - **Older history.** While the host renderer reports the top of the loaded\n * history near (`nearTop`) and the window says older messages exist, this\n * re-states the interests with one more page (anchored at the window's\n * first message) — the host mounts its documents for this client alone —\n * chaining at most `maxChainedLoads` pages per approach; `loadOlder` asks\n * for one page on demand (an edge row's button). Never two loads in flight.\n * A re-attach's hello carries the view the client declares on the attach\n * (`useDeepAgentThread`), every page in it however it was loaded (this\n * hook, a handover's or a search's reveal, an edit's parent). A hello that\n * does not carry the view the last connected session held — a host that\n * reads no declaration and has no memo of the client id — gets it stated\n * again ({@link restatedInterestRequests}).\n * - **Cut tool outputs.** Tool cards whose output the wire carried cut\n * (`artifact._athena_content_truncated`) get it fetched whole through\n * `history/tool_output` once their message is mounted, at most\n * `maxConcurrentToolOutputs` at a time; the converter puts it back\n * (`toolOutputs`), so the card re-renders complete. An output the host\n * replaced since (the marker's `chars` changed) is fetched again.\n */\n/**\n * Whether an older page put a new converted message ahead of the thread's\n * first one. Older history only ever merges into or lands before the first\n * converted message, so the second one is a stable landmark: it moved down iff\n * the page showed something. A new tail message does not move it. Without a\n * second message (or once it is gone) the converted count decides.\n */\nfunction pageShowedEarlierMessage(\n from: { count: number; secondId: string | undefined },\n messages: readonly DeepAgentWindowSyncMessage[]\n): boolean {\n if (from.secondId !== undefined) {\n const index = messages.findIndex((message) => message.id === from.secondId);\n if (index !== -1) return index > 1;\n }\n return messages.length > from.count;\n}\n\n/** Whether a converted message is a turn's reply rather than its prompt (unknown without a role). */\nfunction isReply(message: DeepAgentWindowSyncMessage | undefined): boolean {\n return message?.role !== undefined && message.role !== 'user';\n}\n\n/**\n * Whether the loaded thread still opens without context: it should open on a\n * prompt and hold at least one earlier turn, so the newest turn shows with the\n * one before it (a turn is a prompt followed by its reply). Queued and live\n * prompts land at the tail with no reply yet, so they never count; prompts\n * alone need nothing. Without roles, a lone converted message stands in for it.\n */\nfunction opensMidTurn(messages: readonly DeepAgentWindowSyncMessage[]): boolean {\n const first = messages[0];\n if (first === undefined) return false;\n if (messages.every((message) => message.role === undefined)) return messages.length < 2;\n if (first.role !== 'user') return true;\n if (messages.every((message) => message.role === 'user')) return false;\n let turns = 0;\n for (let index = 1; index < messages.length; index += 1) {\n if (messages[index - 1]!.role === 'user' && messages[index]!.role !== 'user') turns += 1;\n }\n return turns < 2;\n}\n\nexport function useDeepAgentWindowSync({\n messages,\n pageSize,\n toolOutputs,\n nearTop = false,\n nearTopEpoch = 0,\n isMessageMounted = everyMessageMounted,\n maxConcurrentToolOutputs = Number.POSITIVE_INFINITY,\n maxChainedLoads = DEEP_AGENT_MAX_CHAINED_HISTORY_LOADS,\n maxUnseenLoads = DEEP_AGENT_MAX_UNSEEN_HISTORY_LOADS,\n onHistoryLoad,\n onError,\n now = defaultNow,\n}: UseDeepAgentWindowSyncOptions): DeepAgentWindowSyncState {\n const { sendCommand, invoke, connection } = useStatewireExtras();\n const window = useDeepAgentMessageWindow();\n const [loading, setLoading] = useState(false);\n // The view the host refused a page over as deeper than one attach holds\n // (`oversized-interest`): its conversation and how many messages it covered.\n // A view at least that large gets the same refusal (a run's appends only\n // grow it); one that shrank — an edit rewound the transcript, a re-attach\n // came back smaller — may fit a page again.\n const [spentDepth, setSpentDepth] = useState<{ threadId: string | undefined; loaded: number } | null>(\n null\n );\n const depthSpent =\n spentDepth !== null &&\n window !== null &&\n spentDepth.threadId === window.threadId &&\n window.chain.length >= spentDepth.loaded;\n const [requests, setRequests] = useState(0);\n const servedRequestsRef = useRef(0);\n const inflightRef = useRef(false);\n const chainRef = useRef({ epoch: 0, loads: 0, unseen: 0, requested: false });\n // Pages loaded to show the opening turn's prompt; `null` once it shows, the\n // user took over, or a page failed — never re-armed for this mount.\n const openingFillRef = useRef<number | null>(0);\n // The converted thread when the in-flight page was stated; `null` once its\n // settle was judged (or it failed).\n const pageFromRef = useRef<{ count: number; secondId: string | undefined } | null>(null);\n const aliveRef = useRef(true);\n // The window the last connected session held, whoever paged it in, and the\n // session whose hello it was last checked against (a hello resets the\n // documents to what the host seeded).\n const heldRef = useRef<DeepAgentMessageWindow | null>(null);\n const restatedConnectionRef = useRef<unknown>(null);\n // Outputs already asked for and answered by this mount — fetched, refused,\n // or failed — by the `chars` they were asked for. The converter re-runs on\n // every wire change, and the store is a bounded LRU: without this memory a\n // refused read would leave on every pass, and once a transcript holds more\n // cut outputs than the store retains, each fetch would evict a still-loaded\n // output and the next pass would fetch it again, forever. An evicted output\n // renders its cut text (the REST display limit) rather than re-entering the\n // loop; a marker whose `chars` moved (the host replaced the output) is asked\n // for again.\n const answeredRef = useRef(new Map<string, number>());\n // Reads this mount has in flight, and whether a pass left outputs waiting\n // for one to settle (`maxConcurrentToolOutputs`): only then does a settle\n // re-run the pass, so an unbounded mount renders no more than before.\n const toolOutputsInFlightRef = useRef(0);\n const toolOutputsWaitingRef = useRef(false);\n const [toolOutputSettles, setToolOutputSettles] = useState(0);\n const onHistoryLoadRef = useRef(onHistoryLoad);\n onHistoryLoadRef.current = onHistoryLoad;\n const onErrorRef = useRef(onError);\n onErrorRef.current = onError;\n const nowRef = useRef(now);\n nowRef.current = now;\n useEffect(() => {\n aliveRef.current = true;\n return () => {\n aliveRef.current = false;\n };\n }, []);\n\n useEffect(() => {\n if (connection.status !== 'connected' || window === null) return;\n if (restatedConnectionRef.current === connection) {\n heldRef.current = window;\n return;\n }\n // Decided once per connected session, on the hello's interest document.\n restatedConnectionRef.current = connection;\n const held = heldRef.current;\n heldRef.current = window;\n // A page still in flight was stated over the held window: it restates it.\n if (held === null || inflightRef.current || messageWindowCarries({ hello: window, held })) return;\n // Another conversation's hello (the runtime switched threads under this mount).\n if (held.threadId !== undefined && window.threadId !== undefined && held.threadId !== window.threadId) {\n return;\n }\n // Held like a page, so no page is stated over it while it is in flight.\n inflightRef.current = true;\n setLoading(true);\n invoke(DEEP_AGENT_INTEREST_PROTOCOL, 'set', [restatedInterestRequests({ held, hello: window })])\n .catch((error: unknown) => {\n onErrorRef.current?.(error, 'athena/interest');\n })\n .finally(() => {\n inflightRef.current = false;\n if (aliveRef.current) setLoading(false);\n });\n }, [connection, invoke, window]);\n\n useEffect(() => {\n if (window === null || inflightRef.current) return;\n if (depthSpent) {\n // An ask now would get the same refusal: answer it here, so it never\n // reaches another conversation this mount switches to.\n servedRequestsRef.current = requests;\n pageFromRef.current = null;\n return;\n }\n const from = pageFromRef.current;\n pageFromRef.current = null;\n const lastPageUnseen =\n from !== null && from.count > 0 && !pageShowedEarlierMessage(from, messages);\n const pageRequests = pageInterestRequests(window, pageSize);\n if (pageRequests === null) return;\n const requested = requests !== servedRequestsRef.current;\n const chain = chainRef.current;\n let fillPage = false;\n if (\n openingFillRef.current !== null &&\n (requested || nearTop || (messages.length > 0 && !opensMidTurn(messages)))\n ) {\n openingFillRef.current = null;\n }\n if (requested) {\n // An explicit ask re-arms the chain for this approach.\n servedRequestsRef.current = requests;\n chainRef.current = { epoch: nearTopEpoch, loads: 1, unseen: 0, requested: true };\n } else if (lastPageUnseen && (chain.requested || (nearTop && chain.epoch === nearTopEpoch))) {\n // The page landed inside the first loaded turn: keep going until one shows.\n if (chain.unseen >= maxUnseenLoads) return;\n chain.unseen += 1;\n } else if (openingFillRef.current !== null && opensMidTurn(messages)) {\n if (openingFillRef.current >= maxUnseenLoads) return;\n openingFillRef.current += 1;\n fillPage = true;\n } else {\n if (!nearTop) return;\n if (chain.epoch !== nearTopEpoch) {\n chainRef.current = { epoch: nearTopEpoch, loads: 0, unseen: 0, requested: false };\n }\n if (chainRef.current.loads >= maxChainedLoads) return;\n chainRef.current.loads += 1;\n }\n // The page continues the oldest loaded turn (see DEEP_AGENT_MAX_UNSEEN_HISTORY_LOADS).\n const insideTurn = lastPageUnseen || isReply(messages[0]);\n const stated =\n (insideTurn && pageInterestRequests(window, DEEP_AGENT_MAX_MESSAGE_WINDOW)) || pageRequests;\n pageFromRef.current = { count: messages.length, secondId: messages[1]?.id };\n const page = window.pages + 1;\n const request = stated[stated.length - 1] as DeepAgentInterestRequest;\n inflightRef.current = true;\n setLoading(true);\n const startedAt = nowRef.current();\n invoke(DEEP_AGENT_INTEREST_PROTOCOL, 'set', [stated])\n .then(() => {\n onHistoryLoadRef.current?.({\n page,\n request,\n durationMs: Math.max(0, Math.round(nowRef.current() - startedAt)),\n });\n })\n .catch((error: unknown) => {\n pageFromRef.current = null;\n if (fillPage) openingFillRef.current = null;\n if (isOversizedInterestRejection(error) && aliveRef.current) {\n setSpentDepth({ threadId: window.threadId, loaded: window.chain.length });\n }\n onErrorRef.current?.(error, 'athena/interest');\n })\n .finally(() => {\n inflightRef.current = false;\n if (aliveRef.current) setLoading(false);\n });\n // `loading` re-arms the chain once a page settled after its documents moved the window.\n }, [\n nearTop,\n nearTopEpoch,\n requests,\n window,\n loading,\n messages,\n invoke,\n maxChainedLoads,\n maxUnseenLoads,\n pageSize,\n depthSpent,\n ]);\n\n useEffect(() => {\n if (window === null || toolOutputs === undefined) return;\n toolOutputsWaitingRef.current = false;\n for (const message of messages) {\n if (!isMessageMounted(message.id)) continue;\n for (const part of message.content) {\n if (part.type !== 'tool-call') continue;\n const marker = readTruncatedToolOutputArtifact(part.artifact);\n if (marker === null || toolOutputs.isPending(marker.messageId)) continue;\n // Fetched for this very length already; a different length means the\n // host replaced the output under the same id since.\n if (toolOutputs.entry(marker.messageId)?.chars === marker.chars) continue;\n if (answeredRef.current.get(marker.messageId) === marker.chars) continue;\n if (toolOutputsInFlightRef.current >= maxConcurrentToolOutputs) {\n toolOutputsWaitingRef.current = true;\n return;\n }\n const { messageId, chars } = marker;\n toolOutputsInFlightRef.current += 1;\n toolOutputs.markPending(messageId);\n Promise.resolve(sendCommand(buildToolOutputCommand({ messageId })))\n .then((payload) => {\n answeredRef.current.set(messageId, chars);\n const result = readToolOutputResult(payload);\n if (result === null) {\n toolOutputs.clearPending(messageId);\n return;\n }\n toolOutputs.set(result.messageId, { content: result.content, chars });\n })\n .catch((error: unknown) => {\n answeredRef.current.set(messageId, chars);\n toolOutputs.clearPending(messageId);\n onErrorRef.current?.(error, 'history/tool_output');\n })\n .finally(() => {\n toolOutputsInFlightRef.current -= 1;\n if (toolOutputsWaitingRef.current && aliveRef.current) {\n setToolOutputSettles((count) => count + 1);\n }\n });\n }\n }\n }, [\n messages,\n isMessageMounted,\n window,\n sendCommand,\n toolOutputs,\n maxConcurrentToolOutputs,\n toolOutputSettles,\n ]);\n\n const loadOlder = useCallback(() => {\n setRequests((count) => count + 1);\n }, []);\n\n const canLoadOlder = useMemo(\n () => window !== null && !depthSpent && pageInterestRequests(window, pageSize) !== null,\n [window, pageSize, depthSpent]\n );\n\n return { window, hasOlder: window?.hasOlder ?? false, canLoadOlder, loading, loadOlder };\n}\n"],"names":["DEEP_AGENT_OVERSIZED_INTEREST_CODE","useAuiState","hasStatewireThreadExtras","useSyncExternalStore","useMemo","readDeepAgentMessageWindow","useState","useRef","useEffect","messageWindowCarries","DEEP_AGENT_INTEREST_PROTOCOL","restatedInterestRequests","pageInterestRequests","DEEP_AGENT_MAX_MESSAGE_WINDOW","_a","readTruncatedToolOutputArtifact","buildToolOutputCommand","readToolOutputResult","useCallback"],"mappings":"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"}
1
+ {"version":3,"file":"use-deep-agent-window-sync.cjs","sources":["../../../../agent-runtime/src/use-deep-agent-window-sync.ts"],"sourcesContent":["import type { StatewireThread } from '@assistant-ui/react-statewire';\nimport { useAuiState } from '@assistant-ui/store';\nimport { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';\nimport {\n DEEP_AGENT_HISTORY_PAGE_PROTOCOL_VERSION,\n DEEP_AGENT_INTEREST_PROTOCOL,\n DEEP_AGENT_MAX_MESSAGE_WINDOW,\n DEEP_AGENT_OVERSIZED_INTEREST_CODE,\n} from './generated/runconfig-contract';\nimport {\n DEEP_AGENT_HISTORY_PAGE_TURNS,\n type DeepAgentHistorySnapshot,\n type DeepAgentHistoryStore,\n} from './history-pages';\nimport {\n buildToolOutputCommand,\n type DeepAgentInterestRequest,\n type DeepAgentMessageWindow,\n messageWindowCarries,\n pageInterestRequests,\n readDeepAgentMessageWindow,\n readToolOutputResult,\n readTruncatedToolOutputArtifact,\n restatedInterestRequests,\n} from './message-window';\nimport { readDeepAgentProtocol } from './protocol';\nimport { hasStatewireThreadExtras } from './thread-extras';\nimport type { DeepAgentToolOutputStore } from './tool-outputs';\n\n/**\n * Pages of older history chained per approach to the top. A renderer lowers\n * its near-top level once prepended content has pushed the first message out\n * of its band; this bounds the case where it cannot (no viewport to scroll,\n * a list too short to overflow) so a 2,000-message thread never streams whole.\n */\nexport const DEEP_AGENT_MAX_CHAINED_HISTORY_LOADS = 3;\n\n/**\n * Pages of older history that add no converted message — every message on\n * them folds into the first loaded turn, such as one agent run's hundreds of\n * tool steps — loaded on top of a request (or an approach's chain) before it\n * stops. Such a page changes nothing the user can see, so it neither answers\n * an ask nor counts against the per-approach chain.\n *\n * A page loaded while the oldest loaded message is a reply lands inside that\n * reply's turn, so it asks for the host's largest window\n * (`DEEP_AGENT_MAX_MESSAGE_WINDOW`) instead of the thread's: a run of 373\n * tool calls is about 750 messages, nineteen round trips at the default\n * window and four at the largest, and the turn shows nothing new until its\n * prompt does. A page above a prompt keeps the thread's window.\n */\nexport const DEEP_AGENT_MAX_UNSEEN_HISTORY_LOADS = 25;\n\n/** The converted thread messages the tool-output half scans (web and native `ThreadMessage` both satisfy it). */\nexport interface DeepAgentWindowSyncMessage {\n readonly id: string;\n /** The converted role; lets the opening fill tell a turn's prompt from its reply. */\n readonly role?: string;\n readonly content: readonly { readonly type: string; readonly artifact?: unknown }[];\n}\n\n/** One page interest round trip, for the host's telemetry. */\nexport interface DeepAgentHistoryLoadEvent {\n /** The page's ordinal (`1` extends the head). */\n page: number;\n request: DeepAgentInterestRequest;\n durationMs: number;\n}\n\n/** One `history/page` round trip, for the host's telemetry. */\nexport interface DeepAgentHistoryPageEvent {\n /** The message the page was asked before. */\n before: string;\n /** Turns asked for, and answered. */\n turns: number;\n answered: number;\n durationMs: number;\n}\n\nexport interface UseDeepAgentWindowSyncOptions {\n /**\n * The converted thread messages — read by the host through its platform\n * `useAuiState((s) => s.thread.messages)`, so this hook imports neither\n * `@assistant-ui/react` nor `@assistant-ui/react-native`.\n */\n messages: readonly DeepAgentWindowSyncMessage[];\n /** Messages per page; the thread's window size by default. */\n pageSize?: number;\n /**\n * Full tool outputs fetched back for cut tool messages; the same store the\n * thread converts with (`useDeepAgentThread({ toolOutputs })`). Omitted, the\n * tool-output half is inert (a surface that never shows a cut output whole).\n */\n toolOutputs?: DeepAgentToolOutputStore | undefined;\n /**\n * The renderer's report that the top of the LOADED history is near — a\n * level, not an edge: while it holds and older messages exist, pages load\n * one after another (bounded by `maxChainedLoads` per approach). Omitted for\n * hosts with only a manual affordance (`loadOlder`).\n */\n nearTop?: boolean;\n /** Bumped on every rise of `nearTop` (and every explicit request), so the chain bound is per approach. */\n nearTopEpoch?: number;\n /**\n * Whether a message's content is mounted right now, so lazy fetches (cut\n * tool outputs) target what the user can see rather than the whole loaded\n * history. Omitted: every message counts as mounted (the flat renderers).\n */\n isMessageMounted?: ((messageId: string) => boolean) | undefined;\n /**\n * Cut tool outputs this mount reads at once; the rest wait for one to\n * settle. One mounted message can hold a whole agent run — a hundred tool\n * steps — and the host rate-limits a client's frames, so a reader that sends\n * them all together gets refusals back for most of them. Omitted: every\n * mounted output is asked for together (renderers whose mounted set is\n * already small).\n */\n maxConcurrentToolOutputs?: number;\n maxChainedLoads?: number;\n /** Pages adding no converted message loaded per request before it stops; see {@link DEEP_AGENT_MAX_UNSEEN_HISTORY_LOADS}. */\n maxUnseenLoads?: number;\n /** A page interest resolved (telemetry). */\n onHistoryLoad?: ((event: DeepAgentHistoryLoadEvent) => void) | undefined;\n /**\n * Older turns paged by command (`history/page`, protocol 3) into this store:\n * the same one the thread joins in front of its live window\n * (`useDeepAgentThread({ historyStore })`). Used only against a host that\n * advertises `DEEP_AGENT_HISTORY_PAGE_PROTOCOL_VERSION`; there every older\n * page (the button, the near-top chain, the opening fill) is a `history/page`\n * of whole turns and no interest page is stated, so the attach's view stays\n * the head however far back the reader goes. Omitted, or an older host:\n * interest pages, as before.\n */\n historyPages?: DeepAgentHistoryStore | undefined;\n /** Turns one history page asks for. */\n historyPageTurns?: number;\n /** A history page resolved (telemetry). */\n onHistoryPage?: ((event: DeepAgentHistoryPageEvent) => void) | undefined;\n /** A read failed; the sync itself recovers (the pending marks are released). */\n onError?:\n | ((error: unknown, command: 'athena/interest' | 'history/page' | 'history/tool_output') => void)\n | undefined;\n now?: () => number;\n}\n\nexport interface DeepAgentWindowSyncState {\n /** The joined window, or `null` while the head interest has not resolved (both halves are inert). */\n window: DeepAgentMessageWindow | null;\n /** Whether the host holds messages older than the loaded window. */\n hasOlder: boolean;\n /**\n * Whether the next page can be asked for: older messages exist and the\n * view has room for them. `hasOlder && !canLoadOlder` is the depth limit —\n * an edge row says so instead of offering a dead button. The host bounds\n * one attach's view twice: `MAX_INTERESTS` requests, and an interest\n * document that lists every loaded message within a byte budget (about a\n * thousand messages). The second is found when the host refuses a page as\n * `oversized-interest` ({@link isOversizedInterestRejection}).\n */\n canLoadOlder: boolean;\n /** A page of older history is in flight. */\n loading: boolean;\n /** Ask for the next page of older history now (an edge row's button, a keypress). A no-op before the head resolved, at the beginning, or once `canLoadOlder` is false. */\n loadOlder: () => void;\n}\n\n/**\n * Whether an `athena/interest` `set` came back refused because the windows it\n * states resolve past what one attach's interest document carries: the view\n * is as deep as this connection can load, and asking again only gets the same\n * refusal.\n */\nexport function isOversizedInterestRejection(error: unknown): boolean {\n const failure = (error as { failure?: { fate?: unknown; code?: unknown } } | null | undefined)\n ?.failure;\n return failure?.fate === 'rejected' && failure.code === DEEP_AGENT_OVERSIZED_INTEREST_CODE;\n}\n\nconst everyMessageMounted = (): boolean => true;\nconst defaultNow = (): number => Date.now();\nconst noopSubscribe = () => () => {};\n\n/** The statewire extras of the nearest aui thread; throws on a non-statewire thread like the library's own hooks. */\nconst useStatewireExtras = (): StatewireThread.Extras =>\n useAuiState((s) => {\n if (!hasStatewireThreadExtras(s.thread.extras)) {\n throw new Error('useDeepAgentWindowSync requires a statewire thread');\n }\n return s.thread.extras as StatewireThread.Extras;\n });\n\n/** The joined interest window of the nearest statewire thread. */\nexport function useDeepAgentMessageWindow(): DeepAgentMessageWindow | null {\n const documents = useStatewireExtras().documents;\n const mounted = useSyncExternalStore(\n documents?.subscribe ?? noopSubscribe,\n () => documents?.list(),\n () => documents?.list()\n );\n return useMemo(() => readDeepAgentMessageWindow(mounted), [mounted]);\n}\n\n/**\n * The client half of the interest window, shared by every surface — mounted\n * once per runtime, inside its aui provider.\n *\n * - **Older history.** While the host renderer reports the top of the loaded\n * history near (`nearTop`) and the window says older messages exist, this\n * re-states the interests with one more page (anchored at the window's\n * first message) — the host mounts its documents for this client alone —\n * chaining at most `maxChainedLoads` pages per approach; `loadOlder` asks\n * for one page on demand (an edge row's button). Never two loads in flight.\n * A re-attach's hello carries the view the client declares on the attach\n * (`useDeepAgentThread`), every page in it however it was loaded (this\n * hook, a handover's or a search's reveal, an edit's parent). A hello that\n * does not carry the view the last connected session held — a host that\n * reads no declaration and has no memo of the client id — gets it stated\n * again ({@link restatedInterestRequests}).\n * - **Cut tool outputs.** Tool cards whose output the wire carried cut\n * (`artifact._athena_content_truncated`) get it fetched whole through\n * `history/tool_output` once their message is mounted, at most\n * `maxConcurrentToolOutputs` at a time; the converter puts it back\n * (`toolOutputs`), so the card re-renders complete. An output the host\n * replaced since (the marker's `chars` changed) is fetched again.\n */\n/**\n * Whether an older page put a new converted message ahead of the thread's\n * first one. Older history only ever merges into or lands before the first\n * converted message, so the second one is a stable landmark: it moved down iff\n * the page showed something. A new tail message does not move it. Without a\n * second message (or once it is gone) the converted count decides.\n */\nfunction pageShowedEarlierMessage(\n from: { count: number; secondId: string | undefined },\n messages: readonly DeepAgentWindowSyncMessage[]\n): boolean {\n if (from.secondId !== undefined) {\n const index = messages.findIndex((message) => message.id === from.secondId);\n if (index !== -1) return index > 1;\n }\n return messages.length > from.count;\n}\n\n/** Whether a converted message is a turn's reply rather than its prompt (unknown without a role). */\nfunction isReply(message: DeepAgentWindowSyncMessage | undefined): boolean {\n return message?.role !== undefined && message.role !== 'user';\n}\n\n/**\n * Whether the loaded thread still opens without context: it should open on a\n * prompt and hold at least one earlier turn, so the newest turn shows with the\n * one before it (a turn is a prompt followed by its reply). Queued and live\n * prompts land at the tail with no reply yet, so they never count; prompts\n * alone need nothing. Without roles, a lone converted message stands in for it.\n */\nfunction opensMidTurn(messages: readonly DeepAgentWindowSyncMessage[]): boolean {\n const first = messages[0];\n if (first === undefined) return false;\n if (messages.every((message) => message.role === undefined)) return messages.length < 2;\n if (first.role !== 'user') return true;\n if (messages.every((message) => message.role === 'user')) return false;\n let turns = 0;\n for (let index = 1; index < messages.length; index += 1) {\n if (messages[index - 1]!.role === 'user' && messages[index]!.role !== 'user') turns += 1;\n }\n return turns < 2;\n}\n\nexport function useDeepAgentWindowSync({\n messages,\n pageSize,\n toolOutputs,\n nearTop = false,\n nearTopEpoch = 0,\n isMessageMounted = everyMessageMounted,\n maxConcurrentToolOutputs = Number.POSITIVE_INFINITY,\n maxChainedLoads = DEEP_AGENT_MAX_CHAINED_HISTORY_LOADS,\n maxUnseenLoads = DEEP_AGENT_MAX_UNSEEN_HISTORY_LOADS,\n onHistoryLoad,\n historyPages,\n historyPageTurns = DEEP_AGENT_HISTORY_PAGE_TURNS,\n onHistoryPage,\n onError,\n now = defaultNow,\n}: UseDeepAgentWindowSyncOptions): DeepAgentWindowSyncState {\n const { sendCommand, invoke, connection } = useStatewireExtras();\n const window = useDeepAgentMessageWindow();\n const hostVersion = useAuiState((s) => {\n const extras = s.thread.extras as Partial<StatewireThread.Extras> | undefined;\n return readDeepAgentProtocol(extras?.state)?.version ?? 0;\n });\n // Older history comes by command when the surface keeps a store for it and\n // the host answers it; the view then stays the head.\n const pagesByCommand =\n historyPages !== undefined && hostVersion >= DEEP_AGENT_HISTORY_PAGE_PROTOCOL_VERSION;\n const historySnapshot = useSyncExternalStore(\n historyPages?.subscribe ?? noopSubscribe,\n () => historyPages?.getSnapshot() ?? null,\n () => historyPages?.getSnapshot() ?? null\n );\n const liveIds = useMemo(() => new Set(window?.chain.map((entry) => entry.id) ?? []), [window]);\n // The pages that still meet the live window; a detached store is cleared below.\n const history: DeepAgentHistorySnapshot | null =\n historySnapshot !== null &&\n window !== null &&\n historyPages !== undefined &&\n !historyPages.isDetached({ threadId: window.threadId, liveIds })\n ? historySnapshot\n : null;\n const onHistoryPageRef = useRef(onHistoryPage);\n onHistoryPageRef.current = onHistoryPage;\n const [loading, setLoading] = useState(false);\n // The view the host refused a page over as deeper than one attach holds\n // (`oversized-interest`): its conversation and how many messages it covered.\n // A view at least that large gets the same refusal (a run's appends only\n // grow it); one that shrank — an edit rewound the transcript, a re-attach\n // came back smaller — may fit a page again.\n const [spentDepth, setSpentDepth] = useState<{ threadId: string | undefined; loaded: number } | null>(\n null\n );\n const depthSpent =\n spentDepth !== null &&\n window !== null &&\n spentDepth.threadId === window.threadId &&\n window.chain.length >= spentDepth.loaded;\n const [requests, setRequests] = useState(0);\n const servedRequestsRef = useRef(0);\n const inflightRef = useRef(false);\n const chainRef = useRef({ epoch: 0, loads: 0, unseen: 0, requested: false });\n // Pages loaded to show the opening turn's prompt; `null` once it shows, the\n // user took over, or a page failed — never re-armed for this mount.\n const openingFillRef = useRef<number | null>(0);\n // The converted thread when the in-flight page was stated; `null` once its\n // settle was judged (or it failed).\n const pageFromRef = useRef<{ count: number; secondId: string | undefined } | null>(null);\n const aliveRef = useRef(true);\n // The window the last connected session held, whoever paged it in, and the\n // session whose hello it was last checked against (a hello resets the\n // documents to what the host seeded).\n const heldRef = useRef<DeepAgentMessageWindow | null>(null);\n const restatedConnectionRef = useRef<unknown>(null);\n // Outputs already asked for and answered by this mount — fetched, refused,\n // or failed — by the `chars` they were asked for. The converter re-runs on\n // every wire change, and the store is a bounded LRU: without this memory a\n // refused read would leave on every pass, and once a transcript holds more\n // cut outputs than the store retains, each fetch would evict a still-loaded\n // output and the next pass would fetch it again, forever. An evicted output\n // renders its cut text (the REST display limit) rather than re-entering the\n // loop; a marker whose `chars` moved (the host replaced the output) is asked\n // for again.\n const answeredRef = useRef(new Map<string, number>());\n // Reads this mount has in flight, and whether a pass left outputs waiting\n // for one to settle (`maxConcurrentToolOutputs`): only then does a settle\n // re-run the pass, so an unbounded mount renders no more than before.\n const toolOutputsInFlightRef = useRef(0);\n const toolOutputsWaitingRef = useRef(false);\n const [toolOutputSettles, setToolOutputSettles] = useState(0);\n const onHistoryLoadRef = useRef(onHistoryLoad);\n onHistoryLoadRef.current = onHistoryLoad;\n const onErrorRef = useRef(onError);\n onErrorRef.current = onError;\n const nowRef = useRef(now);\n nowRef.current = now;\n useEffect(() => {\n aliveRef.current = true;\n return () => {\n aliveRef.current = false;\n };\n }, []);\n\n // Pages that no longer meet the live window (another conversation, or an\n // edit rewound past their anchor) describe another transcript: drop them,\n // and the edge offers older history again from the window's first message.\n useEffect(() => {\n if (historyPages === undefined || historySnapshot === null || window === null) return;\n if (window.chain.length === 0) return;\n if (historyPages.isDetached({ threadId: window.threadId, liveIds })) historyPages.clear();\n }, [historyPages, historySnapshot, window, liveIds]);\n\n useEffect(() => {\n if (connection.status !== 'connected' || window === null) return;\n if (restatedConnectionRef.current === connection) {\n heldRef.current = window;\n return;\n }\n // Decided once per connected session, on the hello's interest document.\n restatedConnectionRef.current = connection;\n const held = heldRef.current;\n heldRef.current = window;\n // A page still in flight was stated over the held window: it restates it.\n if (held === null || inflightRef.current || messageWindowCarries({ hello: window, held })) return;\n // Another conversation's hello (the runtime switched threads under this mount).\n if (held.threadId !== undefined && window.threadId !== undefined && held.threadId !== window.threadId) {\n return;\n }\n // Held like a page, so no page is stated over it while it is in flight.\n inflightRef.current = true;\n setLoading(true);\n invoke(DEEP_AGENT_INTEREST_PROTOCOL, 'set', [restatedInterestRequests({ held, hello: window })])\n .catch((error: unknown) => {\n onErrorRef.current?.(error, 'athena/interest');\n })\n .finally(() => {\n inflightRef.current = false;\n if (aliveRef.current) setLoading(false);\n });\n }, [connection, invoke, window]);\n\n useEffect(() => {\n if (window === null || inflightRef.current) return;\n // Older turns by command when this surface keeps a store and the host\n // answers it (whole turns before the oldest message held, outside the\n // view); interest pages otherwise. The asks are the same either way.\n const store = pagesByCommand ? historyPages : undefined;\n if (depthSpent && store === undefined) {\n // An ask now would get the same refusal: answer it here, so it never\n // reaches another conversation this mount switches to.\n servedRequestsRef.current = requests;\n pageFromRef.current = null;\n return;\n }\n const from = pageFromRef.current;\n pageFromRef.current = null;\n // A page by command always shows a turn's prompt (it never sets `from`).\n const lastPageUnseen =\n from !== null && from.count > 0 && !pageShowedEarlierMessage(from, messages);\n const pageRequests = store === undefined ? pageInterestRequests(window, pageSize) : null;\n if (store === undefined ? pageRequests === null : !(history?.more ?? window.hasOlder)) return;\n const requested = requests !== servedRequestsRef.current;\n const chain = chainRef.current;\n let fillPage = false;\n if (\n openingFillRef.current !== null &&\n (requested || nearTop || (messages.length > 0 && !opensMidTurn(messages)))\n ) {\n openingFillRef.current = null;\n }\n if (requested) {\n // An explicit ask re-arms the chain for this approach.\n servedRequestsRef.current = requests;\n chainRef.current = { epoch: nearTopEpoch, loads: 1, unseen: 0, requested: true };\n } else if (lastPageUnseen && (chain.requested || (nearTop && chain.epoch === nearTopEpoch))) {\n // The page landed inside the first loaded turn: keep going until one shows.\n if (chain.unseen >= maxUnseenLoads) return;\n chain.unseen += 1;\n } else if (openingFillRef.current !== null && opensMidTurn(messages)) {\n if (openingFillRef.current >= maxUnseenLoads) return;\n openingFillRef.current += 1;\n fillPage = true;\n } else {\n if (!nearTop) return;\n if (chain.epoch !== nearTopEpoch) {\n chainRef.current = { epoch: nearTopEpoch, loads: 0, unseen: 0, requested: false };\n }\n if (chainRef.current.loads >= maxChainedLoads) return;\n chainRef.current.loads += 1;\n }\n const startedAt = nowRef.current();\n const durationMs = () => Math.max(0, Math.round(nowRef.current() - startedAt));\n const settle = () => {\n inflightRef.current = false;\n if (aliveRef.current) setLoading(false);\n };\n if (store !== undefined) {\n const before = history?.turns[0]?.id ?? window.chain[0]?.id;\n if (before === undefined) return;\n inflightRef.current = true;\n setLoading(true);\n store\n .requestOlder({\n send: sendCommand,\n threadId: window.threadId ?? '',\n before,\n beforeIsPrompt:\n history !== null || window.messages.find((message) => message.id === before)?.type === 'human',\n turns: historyPageTurns,\n })\n .then((answered) => {\n onHistoryPageRef.current?.({ before, turns: historyPageTurns, answered, durationMs: durationMs() });\n })\n .catch((error: unknown) => {\n if (fillPage) openingFillRef.current = null;\n onErrorRef.current?.(error, 'history/page');\n })\n .finally(settle);\n return;\n }\n if (pageRequests === null) return;\n // The page continues the oldest loaded turn (see DEEP_AGENT_MAX_UNSEEN_HISTORY_LOADS).\n const insideTurn = lastPageUnseen || isReply(messages[0]);\n const stated =\n (insideTurn && pageInterestRequests(window, DEEP_AGENT_MAX_MESSAGE_WINDOW)) || pageRequests;\n pageFromRef.current = { count: messages.length, secondId: messages[1]?.id };\n const page = window.pages + 1;\n const request = stated[stated.length - 1] as DeepAgentInterestRequest;\n inflightRef.current = true;\n setLoading(true);\n invoke(DEEP_AGENT_INTEREST_PROTOCOL, 'set', [stated])\n .then(() => {\n onHistoryLoadRef.current?.({ page, request, durationMs: durationMs() });\n })\n .catch((error: unknown) => {\n pageFromRef.current = null;\n if (fillPage) openingFillRef.current = null;\n if (isOversizedInterestRejection(error) && aliveRef.current) {\n setSpentDepth({ threadId: window.threadId, loaded: window.chain.length });\n }\n onErrorRef.current?.(error, 'athena/interest');\n })\n .finally(settle);\n // `loading` re-arms the chain once a page settled after its documents moved the window.\n }, [\n nearTop,\n nearTopEpoch,\n requests,\n window,\n loading,\n messages,\n invoke,\n maxChainedLoads,\n maxUnseenLoads,\n pageSize,\n depthSpent,\n pagesByCommand,\n historyPages,\n history,\n historyPageTurns,\n sendCommand,\n ]);\n\n useEffect(() => {\n if (window === null || toolOutputs === undefined) return;\n toolOutputsWaitingRef.current = false;\n for (const message of messages) {\n if (!isMessageMounted(message.id)) continue;\n for (const part of message.content) {\n if (part.type !== 'tool-call') continue;\n const marker = readTruncatedToolOutputArtifact(part.artifact);\n if (marker === null || toolOutputs.isPending(marker.messageId)) continue;\n // Fetched for this very length already; a different length means the\n // host replaced the output under the same id since.\n if (toolOutputs.entry(marker.messageId)?.chars === marker.chars) continue;\n if (answeredRef.current.get(marker.messageId) === marker.chars) continue;\n if (toolOutputsInFlightRef.current >= maxConcurrentToolOutputs) {\n toolOutputsWaitingRef.current = true;\n return;\n }\n const { messageId, chars } = marker;\n toolOutputsInFlightRef.current += 1;\n toolOutputs.markPending(messageId);\n Promise.resolve(sendCommand(buildToolOutputCommand({ messageId })))\n .then((payload) => {\n answeredRef.current.set(messageId, chars);\n const result = readToolOutputResult(payload);\n if (result === null) {\n toolOutputs.clearPending(messageId);\n return;\n }\n toolOutputs.set(result.messageId, { content: result.content, chars });\n })\n .catch((error: unknown) => {\n answeredRef.current.set(messageId, chars);\n toolOutputs.clearPending(messageId);\n onErrorRef.current?.(error, 'history/tool_output');\n })\n .finally(() => {\n toolOutputsInFlightRef.current -= 1;\n if (toolOutputsWaitingRef.current && aliveRef.current) {\n setToolOutputSettles((count) => count + 1);\n }\n });\n }\n }\n }, [\n messages,\n isMessageMounted,\n window,\n sendCommand,\n toolOutputs,\n maxConcurrentToolOutputs,\n toolOutputSettles,\n ]);\n\n const loadOlder = useCallback(() => {\n setRequests((count) => count + 1);\n }, []);\n\n // By command, the host bounds nothing but the transcript's start: older\n // turns exist while the pages say so (the window's own edge before any).\n const hasOlder = pagesByCommand\n ? history !== null\n ? history.more\n : (window?.hasOlder ?? false)\n : (window?.hasOlder ?? false);\n const canLoadOlder = useMemo(\n () =>\n pagesByCommand\n ? window !== null && hasOlder\n : window !== null && !depthSpent && pageInterestRequests(window, pageSize) !== null,\n [pagesByCommand, hasOlder, window, pageSize, depthSpent]\n );\n\n return { window, hasOlder, canLoadOlder, loading, loadOlder };\n}\n"],"names":["DEEP_AGENT_OVERSIZED_INTEREST_CODE","useAuiState","hasStatewireThreadExtras","useSyncExternalStore","useMemo","readDeepAgentMessageWindow","historyPages","DEEP_AGENT_HISTORY_PAGE_TURNS","readDeepAgentProtocol","DEEP_AGENT_HISTORY_PAGE_PROTOCOL_VERSION","useRef","useState","useEffect","messageWindowCarries","DEEP_AGENT_INTEREST_PROTOCOL","restatedInterestRequests","store","pageInterestRequests","_a","DEEP_AGENT_MAX_MESSAGE_WINDOW","readTruncatedToolOutputArtifact","buildToolOutputCommand","readToolOutputResult","useCallback"],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { type DeepAgentHistoryStore } from './history-pages';
1
2
  import { type DeepAgentInterestRequest, type DeepAgentMessageWindow } from './message-window';
2
3
  import type { DeepAgentToolOutputStore } from './tool-outputs';
3
4
  /**
@@ -39,6 +40,15 @@ export interface DeepAgentHistoryLoadEvent {
39
40
  request: DeepAgentInterestRequest;
40
41
  durationMs: number;
41
42
  }
43
+ /** One `history/page` round trip, for the host's telemetry. */
44
+ export interface DeepAgentHistoryPageEvent {
45
+ /** The message the page was asked before. */
46
+ before: string;
47
+ /** Turns asked for, and answered. */
48
+ turns: number;
49
+ answered: number;
50
+ durationMs: number;
51
+ }
42
52
  export interface UseDeepAgentWindowSyncOptions {
43
53
  /**
44
54
  * The converted thread messages — read by the host through its platform
@@ -83,8 +93,23 @@ export interface UseDeepAgentWindowSyncOptions {
83
93
  maxUnseenLoads?: number;
84
94
  /** A page interest resolved (telemetry). */
85
95
  onHistoryLoad?: ((event: DeepAgentHistoryLoadEvent) => void) | undefined;
96
+ /**
97
+ * Older turns paged by command (`history/page`, protocol 3) into this store:
98
+ * the same one the thread joins in front of its live window
99
+ * (`useDeepAgentThread({ historyStore })`). Used only against a host that
100
+ * advertises `DEEP_AGENT_HISTORY_PAGE_PROTOCOL_VERSION`; there every older
101
+ * page (the button, the near-top chain, the opening fill) is a `history/page`
102
+ * of whole turns and no interest page is stated, so the attach's view stays
103
+ * the head however far back the reader goes. Omitted, or an older host:
104
+ * interest pages, as before.
105
+ */
106
+ historyPages?: DeepAgentHistoryStore | undefined;
107
+ /** Turns one history page asks for. */
108
+ historyPageTurns?: number;
109
+ /** A history page resolved (telemetry). */
110
+ onHistoryPage?: ((event: DeepAgentHistoryPageEvent) => void) | undefined;
86
111
  /** A read failed; the sync itself recovers (the pending marks are released). */
87
- onError?: ((error: unknown, command: 'athena/interest' | 'history/tool_output') => void) | undefined;
112
+ onError?: ((error: unknown, command: 'athena/interest' | 'history/page' | 'history/tool_output') => void) | undefined;
88
113
  now?: () => number;
89
114
  }
90
115
  export interface DeepAgentWindowSyncState {
@@ -116,4 +141,4 @@ export interface DeepAgentWindowSyncState {
116
141
  export declare function isOversizedInterestRejection(error: unknown): boolean;
117
142
  /** The joined interest window of the nearest statewire thread. */
118
143
  export declare function useDeepAgentMessageWindow(): DeepAgentMessageWindow | null;
119
- export declare function useDeepAgentWindowSync({ messages, pageSize, toolOutputs, nearTop, nearTopEpoch, isMessageMounted, maxConcurrentToolOutputs, maxChainedLoads, maxUnseenLoads, onHistoryLoad, onError, now, }: UseDeepAgentWindowSyncOptions): DeepAgentWindowSyncState;
144
+ export declare function useDeepAgentWindowSync({ messages, pageSize, toolOutputs, nearTop, nearTopEpoch, isMessageMounted, maxConcurrentToolOutputs, maxChainedLoads, maxUnseenLoads, onHistoryLoad, historyPages, historyPageTurns, onHistoryPage, onError, now, }: UseDeepAgentWindowSyncOptions): DeepAgentWindowSyncState;
@@ -1,7 +1,9 @@
1
1
  import { useAuiState } from "@assistant-ui/store";
2
- import { useState, useRef, useEffect, useCallback, useMemo, useSyncExternalStore } from "react";
3
- import { DEEP_AGENT_INTEREST_PROTOCOL, DEEP_AGENT_OVERSIZED_INTEREST_CODE, DEEP_AGENT_MAX_MESSAGE_WINDOW } from "./generated/runconfig-contract.js";
2
+ import { useSyncExternalStore, useMemo, useRef, useState, useEffect, useCallback } from "react";
3
+ import { DEEP_AGENT_INTEREST_PROTOCOL, DEEP_AGENT_OVERSIZED_INTEREST_CODE, DEEP_AGENT_HISTORY_PAGE_PROTOCOL_VERSION, DEEP_AGENT_MAX_MESSAGE_WINDOW } from "./generated/runconfig-contract.js";
4
+ import { DEEP_AGENT_HISTORY_PAGE_TURNS } from "./history-pages.js";
4
5
  import { messageWindowCarries, restatedInterestRequests, pageInterestRequests, readTruncatedToolOutputArtifact, buildToolOutputCommand, readToolOutputResult, readDeepAgentMessageWindow } from "./message-window.js";
6
+ import { readDeepAgentProtocol } from "./protocol.js";
5
7
  import { hasStatewireThreadExtras } from "./thread-extras.js";
6
8
  const DEEP_AGENT_MAX_CHAINED_HISTORY_LOADS = 3;
7
9
  const DEEP_AGENT_MAX_UNSEEN_HISTORY_LOADS = 25;
@@ -61,11 +63,29 @@ function useDeepAgentWindowSync({
61
63
  maxChainedLoads = DEEP_AGENT_MAX_CHAINED_HISTORY_LOADS,
62
64
  maxUnseenLoads = DEEP_AGENT_MAX_UNSEEN_HISTORY_LOADS,
63
65
  onHistoryLoad,
66
+ historyPages,
67
+ historyPageTurns = DEEP_AGENT_HISTORY_PAGE_TURNS,
68
+ onHistoryPage,
64
69
  onError,
65
70
  now = defaultNow
66
71
  }) {
67
72
  const { sendCommand, invoke, connection } = useStatewireExtras();
68
73
  const window = useDeepAgentMessageWindow();
74
+ const hostVersion = useAuiState((s) => {
75
+ var _a;
76
+ const extras = s.thread.extras;
77
+ return ((_a = readDeepAgentProtocol(extras == null ? void 0 : extras.state)) == null ? void 0 : _a.version) ?? 0;
78
+ });
79
+ const pagesByCommand = historyPages !== void 0 && hostVersion >= DEEP_AGENT_HISTORY_PAGE_PROTOCOL_VERSION;
80
+ const historySnapshot = useSyncExternalStore(
81
+ (historyPages == null ? void 0 : historyPages.subscribe) ?? noopSubscribe,
82
+ () => (historyPages == null ? void 0 : historyPages.getSnapshot()) ?? null,
83
+ () => (historyPages == null ? void 0 : historyPages.getSnapshot()) ?? null
84
+ );
85
+ const liveIds = useMemo(() => new Set((window == null ? void 0 : window.chain.map((entry) => entry.id)) ?? []), [window]);
86
+ const history = historySnapshot !== null && window !== null && historyPages !== void 0 && !historyPages.isDetached({ threadId: window.threadId, liveIds }) ? historySnapshot : null;
87
+ const onHistoryPageRef = useRef(onHistoryPage);
88
+ onHistoryPageRef.current = onHistoryPage;
69
89
  const [loading, setLoading] = useState(false);
70
90
  const [spentDepth, setSpentDepth] = useState(
71
91
  null
@@ -96,6 +116,11 @@ function useDeepAgentWindowSync({
96
116
  aliveRef.current = false;
97
117
  };
98
118
  }, []);
119
+ useEffect(() => {
120
+ if (historyPages === void 0 || historySnapshot === null || window === null) return;
121
+ if (window.chain.length === 0) return;
122
+ if (historyPages.isDetached({ threadId: window.threadId, liveIds })) historyPages.clear();
123
+ }, [historyPages, historySnapshot, window, liveIds]);
99
124
  useEffect(() => {
100
125
  if (connection.status !== "connected" || window === null) return;
101
126
  if (restatedConnectionRef.current === connection) {
@@ -120,9 +145,10 @@ function useDeepAgentWindowSync({
120
145
  });
121
146
  }, [connection, invoke, window]);
122
147
  useEffect(() => {
123
- var _a;
148
+ var _a, _b, _c, _d;
124
149
  if (window === null || inflightRef.current) return;
125
- if (depthSpent) {
150
+ const store = pagesByCommand ? historyPages : void 0;
151
+ if (depthSpent && store === void 0) {
126
152
  servedRequestsRef.current = requests;
127
153
  pageFromRef.current = null;
128
154
  return;
@@ -130,8 +156,8 @@ function useDeepAgentWindowSync({
130
156
  const from = pageFromRef.current;
131
157
  pageFromRef.current = null;
132
158
  const lastPageUnseen = from !== null && from.count > 0 && !pageShowedEarlierMessage(from, messages);
133
- const pageRequests = pageInterestRequests(window, pageSize);
134
- if (pageRequests === null) return;
159
+ const pageRequests = store === void 0 ? pageInterestRequests(window, pageSize) : null;
160
+ if (store === void 0 ? pageRequests === null : !((history == null ? void 0 : history.more) ?? window.hasOlder)) return;
135
161
  const requested = requests !== servedRequestsRef.current;
136
162
  const chain = chainRef.current;
137
163
  let fillPage = false;
@@ -156,21 +182,44 @@ function useDeepAgentWindowSync({
156
182
  if (chainRef.current.loads >= maxChainedLoads) return;
157
183
  chainRef.current.loads += 1;
158
184
  }
185
+ const startedAt = nowRef.current();
186
+ const durationMs = () => Math.max(0, Math.round(nowRef.current() - startedAt));
187
+ const settle = () => {
188
+ inflightRef.current = false;
189
+ if (aliveRef.current) setLoading(false);
190
+ };
191
+ if (store !== void 0) {
192
+ const before = ((_a = history == null ? void 0 : history.turns[0]) == null ? void 0 : _a.id) ?? ((_b = window.chain[0]) == null ? void 0 : _b.id);
193
+ if (before === void 0) return;
194
+ inflightRef.current = true;
195
+ setLoading(true);
196
+ store.requestOlder({
197
+ send: sendCommand,
198
+ threadId: window.threadId ?? "",
199
+ before,
200
+ beforeIsPrompt: history !== null || ((_c = window.messages.find((message) => message.id === before)) == null ? void 0 : _c.type) === "human",
201
+ turns: historyPageTurns
202
+ }).then((answered) => {
203
+ var _a2;
204
+ (_a2 = onHistoryPageRef.current) == null ? void 0 : _a2.call(onHistoryPageRef, { before, turns: historyPageTurns, answered, durationMs: durationMs() });
205
+ }).catch((error) => {
206
+ var _a2;
207
+ if (fillPage) openingFillRef.current = null;
208
+ (_a2 = onErrorRef.current) == null ? void 0 : _a2.call(onErrorRef, error, "history/page");
209
+ }).finally(settle);
210
+ return;
211
+ }
212
+ if (pageRequests === null) return;
159
213
  const insideTurn = lastPageUnseen || isReply(messages[0]);
160
214
  const stated = insideTurn && pageInterestRequests(window, DEEP_AGENT_MAX_MESSAGE_WINDOW) || pageRequests;
161
- pageFromRef.current = { count: messages.length, secondId: (_a = messages[1]) == null ? void 0 : _a.id };
215
+ pageFromRef.current = { count: messages.length, secondId: (_d = messages[1]) == null ? void 0 : _d.id };
162
216
  const page = window.pages + 1;
163
217
  const request = stated[stated.length - 1];
164
218
  inflightRef.current = true;
165
219
  setLoading(true);
166
- const startedAt = nowRef.current();
167
220
  invoke(DEEP_AGENT_INTEREST_PROTOCOL, "set", [stated]).then(() => {
168
221
  var _a2;
169
- (_a2 = onHistoryLoadRef.current) == null ? void 0 : _a2.call(onHistoryLoadRef, {
170
- page,
171
- request,
172
- durationMs: Math.max(0, Math.round(nowRef.current() - startedAt))
173
- });
222
+ (_a2 = onHistoryLoadRef.current) == null ? void 0 : _a2.call(onHistoryLoadRef, { page, request, durationMs: durationMs() });
174
223
  }).catch((error) => {
175
224
  var _a2;
176
225
  pageFromRef.current = null;
@@ -179,10 +228,7 @@ function useDeepAgentWindowSync({
179
228
  setSpentDepth({ threadId: window.threadId, loaded: window.chain.length });
180
229
  }
181
230
  (_a2 = onErrorRef.current) == null ? void 0 : _a2.call(onErrorRef, error, "athena/interest");
182
- }).finally(() => {
183
- inflightRef.current = false;
184
- if (aliveRef.current) setLoading(false);
185
- });
231
+ }).finally(settle);
186
232
  }, [
187
233
  nearTop,
188
234
  nearTopEpoch,
@@ -194,7 +240,12 @@ function useDeepAgentWindowSync({
194
240
  maxChainedLoads,
195
241
  maxUnseenLoads,
196
242
  pageSize,
197
- depthSpent
243
+ depthSpent,
244
+ pagesByCommand,
245
+ historyPages,
246
+ history,
247
+ historyPageTurns,
248
+ sendCommand
198
249
  ]);
199
250
  useEffect(() => {
200
251
  var _a;
@@ -248,11 +299,12 @@ function useDeepAgentWindowSync({
248
299
  const loadOlder = useCallback(() => {
249
300
  setRequests((count) => count + 1);
250
301
  }, []);
302
+ const hasOlder = pagesByCommand ? history !== null ? history.more : (window == null ? void 0 : window.hasOlder) ?? false : (window == null ? void 0 : window.hasOlder) ?? false;
251
303
  const canLoadOlder = useMemo(
252
- () => window !== null && !depthSpent && pageInterestRequests(window, pageSize) !== null,
253
- [window, pageSize, depthSpent]
304
+ () => pagesByCommand ? window !== null && hasOlder : window !== null && !depthSpent && pageInterestRequests(window, pageSize) !== null,
305
+ [pagesByCommand, hasOlder, window, pageSize, depthSpent]
254
306
  );
255
- return { window, hasOlder: (window == null ? void 0 : window.hasOlder) ?? false, canLoadOlder, loading, loadOlder };
307
+ return { window, hasOlder, canLoadOlder, loading, loadOlder };
256
308
  }
257
309
  export {
258
310
  DEEP_AGENT_MAX_CHAINED_HISTORY_LOADS,
@@ -1 +1 @@
1
- {"version":3,"file":"use-deep-agent-window-sync.js","sources":["../../../../agent-runtime/src/use-deep-agent-window-sync.ts"],"sourcesContent":["import type { StatewireThread } from '@assistant-ui/react-statewire';\nimport { useAuiState } from '@assistant-ui/store';\nimport { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';\nimport {\n DEEP_AGENT_INTEREST_PROTOCOL,\n DEEP_AGENT_MAX_MESSAGE_WINDOW,\n DEEP_AGENT_OVERSIZED_INTEREST_CODE,\n} from './generated/runconfig-contract';\nimport {\n buildToolOutputCommand,\n type DeepAgentInterestRequest,\n type DeepAgentMessageWindow,\n messageWindowCarries,\n pageInterestRequests,\n readDeepAgentMessageWindow,\n readToolOutputResult,\n readTruncatedToolOutputArtifact,\n restatedInterestRequests,\n} from './message-window';\nimport { hasStatewireThreadExtras } from './thread-extras';\nimport type { DeepAgentToolOutputStore } from './tool-outputs';\n\n/**\n * Pages of older history chained per approach to the top. A renderer lowers\n * its near-top level once prepended content has pushed the first message out\n * of its band; this bounds the case where it cannot (no viewport to scroll,\n * a list too short to overflow) so a 2,000-message thread never streams whole.\n */\nexport const DEEP_AGENT_MAX_CHAINED_HISTORY_LOADS = 3;\n\n/**\n * Pages of older history that add no converted message — every message on\n * them folds into the first loaded turn, such as one agent run's hundreds of\n * tool steps — loaded on top of a request (or an approach's chain) before it\n * stops. Such a page changes nothing the user can see, so it neither answers\n * an ask nor counts against the per-approach chain.\n *\n * A page loaded while the oldest loaded message is a reply lands inside that\n * reply's turn, so it asks for the host's largest window\n * (`DEEP_AGENT_MAX_MESSAGE_WINDOW`) instead of the thread's: a run of 373\n * tool calls is about 750 messages, nineteen round trips at the default\n * window and four at the largest, and the turn shows nothing new until its\n * prompt does. A page above a prompt keeps the thread's window.\n */\nexport const DEEP_AGENT_MAX_UNSEEN_HISTORY_LOADS = 25;\n\n/** The converted thread messages the tool-output half scans (web and native `ThreadMessage` both satisfy it). */\nexport interface DeepAgentWindowSyncMessage {\n readonly id: string;\n /** The converted role; lets the opening fill tell a turn's prompt from its reply. */\n readonly role?: string;\n readonly content: readonly { readonly type: string; readonly artifact?: unknown }[];\n}\n\n/** One page interest round trip, for the host's telemetry. */\nexport interface DeepAgentHistoryLoadEvent {\n /** The page's ordinal (`1` extends the head). */\n page: number;\n request: DeepAgentInterestRequest;\n durationMs: number;\n}\n\nexport interface UseDeepAgentWindowSyncOptions {\n /**\n * The converted thread messages — read by the host through its platform\n * `useAuiState((s) => s.thread.messages)`, so this hook imports neither\n * `@assistant-ui/react` nor `@assistant-ui/react-native`.\n */\n messages: readonly DeepAgentWindowSyncMessage[];\n /** Messages per page; the thread's window size by default. */\n pageSize?: number;\n /**\n * Full tool outputs fetched back for cut tool messages; the same store the\n * thread converts with (`useDeepAgentThread({ toolOutputs })`). Omitted, the\n * tool-output half is inert (a surface that never shows a cut output whole).\n */\n toolOutputs?: DeepAgentToolOutputStore | undefined;\n /**\n * The renderer's report that the top of the LOADED history is near — a\n * level, not an edge: while it holds and older messages exist, pages load\n * one after another (bounded by `maxChainedLoads` per approach). Omitted for\n * hosts with only a manual affordance (`loadOlder`).\n */\n nearTop?: boolean;\n /** Bumped on every rise of `nearTop` (and every explicit request), so the chain bound is per approach. */\n nearTopEpoch?: number;\n /**\n * Whether a message's content is mounted right now, so lazy fetches (cut\n * tool outputs) target what the user can see rather than the whole loaded\n * history. Omitted: every message counts as mounted (the flat renderers).\n */\n isMessageMounted?: ((messageId: string) => boolean) | undefined;\n /**\n * Cut tool outputs this mount reads at once; the rest wait for one to\n * settle. One mounted message can hold a whole agent run — a hundred tool\n * steps — and the host rate-limits a client's frames, so a reader that sends\n * them all together gets refusals back for most of them. Omitted: every\n * mounted output is asked for together (renderers whose mounted set is\n * already small).\n */\n maxConcurrentToolOutputs?: number;\n maxChainedLoads?: number;\n /** Pages adding no converted message loaded per request before it stops; see {@link DEEP_AGENT_MAX_UNSEEN_HISTORY_LOADS}. */\n maxUnseenLoads?: number;\n /** A page interest resolved (telemetry). */\n onHistoryLoad?: ((event: DeepAgentHistoryLoadEvent) => void) | undefined;\n /** A read failed; the sync itself recovers (the pending marks are released). */\n onError?: ((error: unknown, command: 'athena/interest' | 'history/tool_output') => void) | undefined;\n now?: () => number;\n}\n\nexport interface DeepAgentWindowSyncState {\n /** The joined window, or `null` while the head interest has not resolved (both halves are inert). */\n window: DeepAgentMessageWindow | null;\n /** Whether the host holds messages older than the loaded window. */\n hasOlder: boolean;\n /**\n * Whether the next page can be asked for: older messages exist and the\n * view has room for them. `hasOlder && !canLoadOlder` is the depth limit —\n * an edge row says so instead of offering a dead button. The host bounds\n * one attach's view twice: `MAX_INTERESTS` requests, and an interest\n * document that lists every loaded message within a byte budget (about a\n * thousand messages). The second is found when the host refuses a page as\n * `oversized-interest` ({@link isOversizedInterestRejection}).\n */\n canLoadOlder: boolean;\n /** A page of older history is in flight. */\n loading: boolean;\n /** Ask for the next page of older history now (an edge row's button, a keypress). A no-op before the head resolved, at the beginning, or once `canLoadOlder` is false. */\n loadOlder: () => void;\n}\n\n/**\n * Whether an `athena/interest` `set` came back refused because the windows it\n * states resolve past what one attach's interest document carries: the view\n * is as deep as this connection can load, and asking again only gets the same\n * refusal.\n */\nexport function isOversizedInterestRejection(error: unknown): boolean {\n const failure = (error as { failure?: { fate?: unknown; code?: unknown } } | null | undefined)\n ?.failure;\n return failure?.fate === 'rejected' && failure.code === DEEP_AGENT_OVERSIZED_INTEREST_CODE;\n}\n\nconst everyMessageMounted = (): boolean => true;\nconst defaultNow = (): number => Date.now();\nconst noopSubscribe = () => () => {};\n\n/** The statewire extras of the nearest aui thread; throws on a non-statewire thread like the library's own hooks. */\nconst useStatewireExtras = (): StatewireThread.Extras =>\n useAuiState((s) => {\n if (!hasStatewireThreadExtras(s.thread.extras)) {\n throw new Error('useDeepAgentWindowSync requires a statewire thread');\n }\n return s.thread.extras as StatewireThread.Extras;\n });\n\n/** The joined interest window of the nearest statewire thread. */\nexport function useDeepAgentMessageWindow(): DeepAgentMessageWindow | null {\n const documents = useStatewireExtras().documents;\n const mounted = useSyncExternalStore(\n documents?.subscribe ?? noopSubscribe,\n () => documents?.list(),\n () => documents?.list()\n );\n return useMemo(() => readDeepAgentMessageWindow(mounted), [mounted]);\n}\n\n/**\n * The client half of the interest window, shared by every surface — mounted\n * once per runtime, inside its aui provider.\n *\n * - **Older history.** While the host renderer reports the top of the loaded\n * history near (`nearTop`) and the window says older messages exist, this\n * re-states the interests with one more page (anchored at the window's\n * first message) — the host mounts its documents for this client alone —\n * chaining at most `maxChainedLoads` pages per approach; `loadOlder` asks\n * for one page on demand (an edge row's button). Never two loads in flight.\n * A re-attach's hello carries the view the client declares on the attach\n * (`useDeepAgentThread`), every page in it however it was loaded (this\n * hook, a handover's or a search's reveal, an edit's parent). A hello that\n * does not carry the view the last connected session held — a host that\n * reads no declaration and has no memo of the client id — gets it stated\n * again ({@link restatedInterestRequests}).\n * - **Cut tool outputs.** Tool cards whose output the wire carried cut\n * (`artifact._athena_content_truncated`) get it fetched whole through\n * `history/tool_output` once their message is mounted, at most\n * `maxConcurrentToolOutputs` at a time; the converter puts it back\n * (`toolOutputs`), so the card re-renders complete. An output the host\n * replaced since (the marker's `chars` changed) is fetched again.\n */\n/**\n * Whether an older page put a new converted message ahead of the thread's\n * first one. Older history only ever merges into or lands before the first\n * converted message, so the second one is a stable landmark: it moved down iff\n * the page showed something. A new tail message does not move it. Without a\n * second message (or once it is gone) the converted count decides.\n */\nfunction pageShowedEarlierMessage(\n from: { count: number; secondId: string | undefined },\n messages: readonly DeepAgentWindowSyncMessage[]\n): boolean {\n if (from.secondId !== undefined) {\n const index = messages.findIndex((message) => message.id === from.secondId);\n if (index !== -1) return index > 1;\n }\n return messages.length > from.count;\n}\n\n/** Whether a converted message is a turn's reply rather than its prompt (unknown without a role). */\nfunction isReply(message: DeepAgentWindowSyncMessage | undefined): boolean {\n return message?.role !== undefined && message.role !== 'user';\n}\n\n/**\n * Whether the loaded thread still opens without context: it should open on a\n * prompt and hold at least one earlier turn, so the newest turn shows with the\n * one before it (a turn is a prompt followed by its reply). Queued and live\n * prompts land at the tail with no reply yet, so they never count; prompts\n * alone need nothing. Without roles, a lone converted message stands in for it.\n */\nfunction opensMidTurn(messages: readonly DeepAgentWindowSyncMessage[]): boolean {\n const first = messages[0];\n if (first === undefined) return false;\n if (messages.every((message) => message.role === undefined)) return messages.length < 2;\n if (first.role !== 'user') return true;\n if (messages.every((message) => message.role === 'user')) return false;\n let turns = 0;\n for (let index = 1; index < messages.length; index += 1) {\n if (messages[index - 1]!.role === 'user' && messages[index]!.role !== 'user') turns += 1;\n }\n return turns < 2;\n}\n\nexport function useDeepAgentWindowSync({\n messages,\n pageSize,\n toolOutputs,\n nearTop = false,\n nearTopEpoch = 0,\n isMessageMounted = everyMessageMounted,\n maxConcurrentToolOutputs = Number.POSITIVE_INFINITY,\n maxChainedLoads = DEEP_AGENT_MAX_CHAINED_HISTORY_LOADS,\n maxUnseenLoads = DEEP_AGENT_MAX_UNSEEN_HISTORY_LOADS,\n onHistoryLoad,\n onError,\n now = defaultNow,\n}: UseDeepAgentWindowSyncOptions): DeepAgentWindowSyncState {\n const { sendCommand, invoke, connection } = useStatewireExtras();\n const window = useDeepAgentMessageWindow();\n const [loading, setLoading] = useState(false);\n // The view the host refused a page over as deeper than one attach holds\n // (`oversized-interest`): its conversation and how many messages it covered.\n // A view at least that large gets the same refusal (a run's appends only\n // grow it); one that shrank — an edit rewound the transcript, a re-attach\n // came back smaller — may fit a page again.\n const [spentDepth, setSpentDepth] = useState<{ threadId: string | undefined; loaded: number } | null>(\n null\n );\n const depthSpent =\n spentDepth !== null &&\n window !== null &&\n spentDepth.threadId === window.threadId &&\n window.chain.length >= spentDepth.loaded;\n const [requests, setRequests] = useState(0);\n const servedRequestsRef = useRef(0);\n const inflightRef = useRef(false);\n const chainRef = useRef({ epoch: 0, loads: 0, unseen: 0, requested: false });\n // Pages loaded to show the opening turn's prompt; `null` once it shows, the\n // user took over, or a page failed — never re-armed for this mount.\n const openingFillRef = useRef<number | null>(0);\n // The converted thread when the in-flight page was stated; `null` once its\n // settle was judged (or it failed).\n const pageFromRef = useRef<{ count: number; secondId: string | undefined } | null>(null);\n const aliveRef = useRef(true);\n // The window the last connected session held, whoever paged it in, and the\n // session whose hello it was last checked against (a hello resets the\n // documents to what the host seeded).\n const heldRef = useRef<DeepAgentMessageWindow | null>(null);\n const restatedConnectionRef = useRef<unknown>(null);\n // Outputs already asked for and answered by this mount — fetched, refused,\n // or failed — by the `chars` they were asked for. The converter re-runs on\n // every wire change, and the store is a bounded LRU: without this memory a\n // refused read would leave on every pass, and once a transcript holds more\n // cut outputs than the store retains, each fetch would evict a still-loaded\n // output and the next pass would fetch it again, forever. An evicted output\n // renders its cut text (the REST display limit) rather than re-entering the\n // loop; a marker whose `chars` moved (the host replaced the output) is asked\n // for again.\n const answeredRef = useRef(new Map<string, number>());\n // Reads this mount has in flight, and whether a pass left outputs waiting\n // for one to settle (`maxConcurrentToolOutputs`): only then does a settle\n // re-run the pass, so an unbounded mount renders no more than before.\n const toolOutputsInFlightRef = useRef(0);\n const toolOutputsWaitingRef = useRef(false);\n const [toolOutputSettles, setToolOutputSettles] = useState(0);\n const onHistoryLoadRef = useRef(onHistoryLoad);\n onHistoryLoadRef.current = onHistoryLoad;\n const onErrorRef = useRef(onError);\n onErrorRef.current = onError;\n const nowRef = useRef(now);\n nowRef.current = now;\n useEffect(() => {\n aliveRef.current = true;\n return () => {\n aliveRef.current = false;\n };\n }, []);\n\n useEffect(() => {\n if (connection.status !== 'connected' || window === null) return;\n if (restatedConnectionRef.current === connection) {\n heldRef.current = window;\n return;\n }\n // Decided once per connected session, on the hello's interest document.\n restatedConnectionRef.current = connection;\n const held = heldRef.current;\n heldRef.current = window;\n // A page still in flight was stated over the held window: it restates it.\n if (held === null || inflightRef.current || messageWindowCarries({ hello: window, held })) return;\n // Another conversation's hello (the runtime switched threads under this mount).\n if (held.threadId !== undefined && window.threadId !== undefined && held.threadId !== window.threadId) {\n return;\n }\n // Held like a page, so no page is stated over it while it is in flight.\n inflightRef.current = true;\n setLoading(true);\n invoke(DEEP_AGENT_INTEREST_PROTOCOL, 'set', [restatedInterestRequests({ held, hello: window })])\n .catch((error: unknown) => {\n onErrorRef.current?.(error, 'athena/interest');\n })\n .finally(() => {\n inflightRef.current = false;\n if (aliveRef.current) setLoading(false);\n });\n }, [connection, invoke, window]);\n\n useEffect(() => {\n if (window === null || inflightRef.current) return;\n if (depthSpent) {\n // An ask now would get the same refusal: answer it here, so it never\n // reaches another conversation this mount switches to.\n servedRequestsRef.current = requests;\n pageFromRef.current = null;\n return;\n }\n const from = pageFromRef.current;\n pageFromRef.current = null;\n const lastPageUnseen =\n from !== null && from.count > 0 && !pageShowedEarlierMessage(from, messages);\n const pageRequests = pageInterestRequests(window, pageSize);\n if (pageRequests === null) return;\n const requested = requests !== servedRequestsRef.current;\n const chain = chainRef.current;\n let fillPage = false;\n if (\n openingFillRef.current !== null &&\n (requested || nearTop || (messages.length > 0 && !opensMidTurn(messages)))\n ) {\n openingFillRef.current = null;\n }\n if (requested) {\n // An explicit ask re-arms the chain for this approach.\n servedRequestsRef.current = requests;\n chainRef.current = { epoch: nearTopEpoch, loads: 1, unseen: 0, requested: true };\n } else if (lastPageUnseen && (chain.requested || (nearTop && chain.epoch === nearTopEpoch))) {\n // The page landed inside the first loaded turn: keep going until one shows.\n if (chain.unseen >= maxUnseenLoads) return;\n chain.unseen += 1;\n } else if (openingFillRef.current !== null && opensMidTurn(messages)) {\n if (openingFillRef.current >= maxUnseenLoads) return;\n openingFillRef.current += 1;\n fillPage = true;\n } else {\n if (!nearTop) return;\n if (chain.epoch !== nearTopEpoch) {\n chainRef.current = { epoch: nearTopEpoch, loads: 0, unseen: 0, requested: false };\n }\n if (chainRef.current.loads >= maxChainedLoads) return;\n chainRef.current.loads += 1;\n }\n // The page continues the oldest loaded turn (see DEEP_AGENT_MAX_UNSEEN_HISTORY_LOADS).\n const insideTurn = lastPageUnseen || isReply(messages[0]);\n const stated =\n (insideTurn && pageInterestRequests(window, DEEP_AGENT_MAX_MESSAGE_WINDOW)) || pageRequests;\n pageFromRef.current = { count: messages.length, secondId: messages[1]?.id };\n const page = window.pages + 1;\n const request = stated[stated.length - 1] as DeepAgentInterestRequest;\n inflightRef.current = true;\n setLoading(true);\n const startedAt = nowRef.current();\n invoke(DEEP_AGENT_INTEREST_PROTOCOL, 'set', [stated])\n .then(() => {\n onHistoryLoadRef.current?.({\n page,\n request,\n durationMs: Math.max(0, Math.round(nowRef.current() - startedAt)),\n });\n })\n .catch((error: unknown) => {\n pageFromRef.current = null;\n if (fillPage) openingFillRef.current = null;\n if (isOversizedInterestRejection(error) && aliveRef.current) {\n setSpentDepth({ threadId: window.threadId, loaded: window.chain.length });\n }\n onErrorRef.current?.(error, 'athena/interest');\n })\n .finally(() => {\n inflightRef.current = false;\n if (aliveRef.current) setLoading(false);\n });\n // `loading` re-arms the chain once a page settled after its documents moved the window.\n }, [\n nearTop,\n nearTopEpoch,\n requests,\n window,\n loading,\n messages,\n invoke,\n maxChainedLoads,\n maxUnseenLoads,\n pageSize,\n depthSpent,\n ]);\n\n useEffect(() => {\n if (window === null || toolOutputs === undefined) return;\n toolOutputsWaitingRef.current = false;\n for (const message of messages) {\n if (!isMessageMounted(message.id)) continue;\n for (const part of message.content) {\n if (part.type !== 'tool-call') continue;\n const marker = readTruncatedToolOutputArtifact(part.artifact);\n if (marker === null || toolOutputs.isPending(marker.messageId)) continue;\n // Fetched for this very length already; a different length means the\n // host replaced the output under the same id since.\n if (toolOutputs.entry(marker.messageId)?.chars === marker.chars) continue;\n if (answeredRef.current.get(marker.messageId) === marker.chars) continue;\n if (toolOutputsInFlightRef.current >= maxConcurrentToolOutputs) {\n toolOutputsWaitingRef.current = true;\n return;\n }\n const { messageId, chars } = marker;\n toolOutputsInFlightRef.current += 1;\n toolOutputs.markPending(messageId);\n Promise.resolve(sendCommand(buildToolOutputCommand({ messageId })))\n .then((payload) => {\n answeredRef.current.set(messageId, chars);\n const result = readToolOutputResult(payload);\n if (result === null) {\n toolOutputs.clearPending(messageId);\n return;\n }\n toolOutputs.set(result.messageId, { content: result.content, chars });\n })\n .catch((error: unknown) => {\n answeredRef.current.set(messageId, chars);\n toolOutputs.clearPending(messageId);\n onErrorRef.current?.(error, 'history/tool_output');\n })\n .finally(() => {\n toolOutputsInFlightRef.current -= 1;\n if (toolOutputsWaitingRef.current && aliveRef.current) {\n setToolOutputSettles((count) => count + 1);\n }\n });\n }\n }\n }, [\n messages,\n isMessageMounted,\n window,\n sendCommand,\n toolOutputs,\n maxConcurrentToolOutputs,\n toolOutputSettles,\n ]);\n\n const loadOlder = useCallback(() => {\n setRequests((count) => count + 1);\n }, []);\n\n const canLoadOlder = useMemo(\n () => window !== null && !depthSpent && pageInterestRequests(window, pageSize) !== null,\n [window, pageSize, depthSpent]\n );\n\n return { window, hasOlder: window?.hasOlder ?? false, canLoadOlder, loading, loadOlder };\n}\n"],"names":["_a"],"mappings":";;;;;AA4BO,MAAM,uCAAuC;AAgB7C,MAAM,sCAAsC;AA8F5C,SAAS,6BAA6B,OAAyB;AACpE,QAAM,UAAW,+BACb;AACJ,UAAO,mCAAS,UAAS,cAAc,QAAQ,SAAS;AAC1D;AAEA,MAAM,sBAAsB,MAAe;AAC3C,MAAM,aAAa,MAAc,KAAK,IAAA;AACtC,MAAM,gBAAgB,MAAM,MAAM;AAAC;AAGnC,MAAM,qBAAqB,MACzB,YAAY,CAAC,MAAM;AACjB,MAAI,CAAC,yBAAyB,EAAE,OAAO,MAAM,GAAG;AAC9C,UAAM,IAAI,MAAM,oDAAoD;AAAA,EACtE;AACA,SAAO,EAAE,OAAO;AAClB,CAAC;AAGI,SAAS,4BAA2D;AACzE,QAAM,YAAY,qBAAqB;AACvC,QAAM,UAAU;AAAA,KACd,uCAAW,cAAa;AAAA,IACxB,MAAM,uCAAW;AAAA,IACjB,MAAM,uCAAW;AAAA,EAAK;AAExB,SAAO,QAAQ,MAAM,2BAA2B,OAAO,GAAG,CAAC,OAAO,CAAC;AACrE;AAgCA,SAAS,yBACP,MACA,UACS;AACT,MAAI,KAAK,aAAa,QAAW;AAC/B,UAAM,QAAQ,SAAS,UAAU,CAAC,YAAY,QAAQ,OAAO,KAAK,QAAQ;AAC1E,QAAI,UAAU,GAAI,QAAO,QAAQ;AAAA,EACnC;AACA,SAAO,SAAS,SAAS,KAAK;AAChC;AAGA,SAAS,QAAQ,SAA0D;AACzE,UAAO,mCAAS,UAAS,UAAa,QAAQ,SAAS;AACzD;AASA,SAAS,aAAa,UAA0D;AAC9E,QAAM,QAAQ,SAAS,CAAC;AACxB,MAAI,UAAU,OAAW,QAAO;AAChC,MAAI,SAAS,MAAM,CAAC,YAAY,QAAQ,SAAS,MAAS,EAAG,QAAO,SAAS,SAAS;AACtF,MAAI,MAAM,SAAS,OAAQ,QAAO;AAClC,MAAI,SAAS,MAAM,CAAC,YAAY,QAAQ,SAAS,MAAM,EAAG,QAAO;AACjE,MAAI,QAAQ;AACZ,WAAS,QAAQ,GAAG,QAAQ,SAAS,QAAQ,SAAS,GAAG;AACvD,QAAI,SAAS,QAAQ,CAAC,EAAG,SAAS,UAAU,SAAS,KAAK,EAAG,SAAS,OAAQ,UAAS;AAAA,EACzF;AACA,SAAO,QAAQ;AACjB;AAEO,SAAS,uBAAuB;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,eAAe;AAAA,EACf,mBAAmB;AAAA,EACnB,2BAA2B,OAAO;AAAA,EAClC,kBAAkB;AAAA,EAClB,iBAAiB;AAAA,EACjB;AAAA,EACA;AAAA,EACA,MAAM;AACR,GAA4D;AAC1D,QAAM,EAAE,aAAa,QAAQ,WAAA,IAAe,mBAAA;AAC5C,QAAM,SAAS,0BAAA;AACf,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,KAAK;AAM5C,QAAM,CAAC,YAAY,aAAa,IAAI;AAAA,IAClC;AAAA,EAAA;AAEF,QAAM,aACJ,eAAe,QACf,WAAW,QACX,WAAW,aAAa,OAAO,YAC/B,OAAO,MAAM,UAAU,WAAW;AACpC,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,CAAC;AAC1C,QAAM,oBAAoB,OAAO,CAAC;AAClC,QAAM,cAAc,OAAO,KAAK;AAChC,QAAM,WAAW,OAAO,EAAE,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,MAAA,CAAO;AAG3E,QAAM,iBAAiB,OAAsB,CAAC;AAG9C,QAAM,cAAc,OAA+D,IAAI;AACvF,QAAM,WAAW,OAAO,IAAI;AAI5B,QAAM,UAAU,OAAsC,IAAI;AAC1D,QAAM,wBAAwB,OAAgB,IAAI;AAUlD,QAAM,cAAc,OAAO,oBAAI,KAAqB;AAIpD,QAAM,yBAAyB,OAAO,CAAC;AACvC,QAAM,wBAAwB,OAAO,KAAK;AAC1C,QAAM,CAAC,mBAAmB,oBAAoB,IAAI,SAAS,CAAC;AAC5D,QAAM,mBAAmB,OAAO,aAAa;AAC7C,mBAAiB,UAAU;AAC3B,QAAM,aAAa,OAAO,OAAO;AACjC,aAAW,UAAU;AACrB,QAAM,SAAS,OAAO,GAAG;AACzB,SAAO,UAAU;AACjB,YAAU,MAAM;AACd,aAAS,UAAU;AACnB,WAAO,MAAM;AACX,eAAS,UAAU;AAAA,IACrB;AAAA,EACF,GAAG,CAAA,CAAE;AAEL,YAAU,MAAM;AACd,QAAI,WAAW,WAAW,eAAe,WAAW,KAAM;AAC1D,QAAI,sBAAsB,YAAY,YAAY;AAChD,cAAQ,UAAU;AAClB;AAAA,IACF;AAEA,0BAAsB,UAAU;AAChC,UAAM,OAAO,QAAQ;AACrB,YAAQ,UAAU;AAElB,QAAI,SAAS,QAAQ,YAAY,WAAW,qBAAqB,EAAE,OAAO,QAAQ,KAAA,CAAM,EAAG;AAE3F,QAAI,KAAK,aAAa,UAAa,OAAO,aAAa,UAAa,KAAK,aAAa,OAAO,UAAU;AACrG;AAAA,IACF;AAEA,gBAAY,UAAU;AACtB,eAAW,IAAI;AACf,WAAO,8BAA8B,OAAO,CAAC,yBAAyB,EAAE,MAAM,OAAO,OAAA,CAAQ,CAAC,CAAC,EAC5F,MAAM,CAAC,UAAmB;;AACzB,uBAAW,YAAX,oCAAqB,OAAO;AAAA,IAC9B,CAAC,EACA,QAAQ,MAAM;AACb,kBAAY,UAAU;AACtB,UAAI,SAAS,QAAS,YAAW,KAAK;AAAA,IACxC,CAAC;AAAA,EACL,GAAG,CAAC,YAAY,QAAQ,MAAM,CAAC;AAE/B,YAAU,MAAM;;AACd,QAAI,WAAW,QAAQ,YAAY,QAAS;AAC5C,QAAI,YAAY;AAGd,wBAAkB,UAAU;AAC5B,kBAAY,UAAU;AACtB;AAAA,IACF;AACA,UAAM,OAAO,YAAY;AACzB,gBAAY,UAAU;AACtB,UAAM,iBACJ,SAAS,QAAQ,KAAK,QAAQ,KAAK,CAAC,yBAAyB,MAAM,QAAQ;AAC7E,UAAM,eAAe,qBAAqB,QAAQ,QAAQ;AAC1D,QAAI,iBAAiB,KAAM;AAC3B,UAAM,YAAY,aAAa,kBAAkB;AACjD,UAAM,QAAQ,SAAS;AACvB,QAAI,WAAW;AACf,QACE,eAAe,YAAY,SAC1B,aAAa,WAAY,SAAS,SAAS,KAAK,CAAC,aAAa,QAAQ,IACvE;AACA,qBAAe,UAAU;AAAA,IAC3B;AACA,QAAI,WAAW;AAEb,wBAAkB,UAAU;AAC5B,eAAS,UAAU,EAAE,OAAO,cAAc,OAAO,GAAG,QAAQ,GAAG,WAAW,KAAA;AAAA,IAC5E,WAAW,mBAAmB,MAAM,aAAc,WAAW,MAAM,UAAU,eAAgB;AAE3F,UAAI,MAAM,UAAU,eAAgB;AACpC,YAAM,UAAU;AAAA,IAClB,WAAW,eAAe,YAAY,QAAQ,aAAa,QAAQ,GAAG;AACpE,UAAI,eAAe,WAAW,eAAgB;AAC9C,qBAAe,WAAW;AAC1B,iBAAW;AAAA,IACb,OAAO;AACL,UAAI,CAAC,QAAS;AACd,UAAI,MAAM,UAAU,cAAc;AAChC,iBAAS,UAAU,EAAE,OAAO,cAAc,OAAO,GAAG,QAAQ,GAAG,WAAW,MAAA;AAAA,MAC5E;AACA,UAAI,SAAS,QAAQ,SAAS,gBAAiB;AAC/C,eAAS,QAAQ,SAAS;AAAA,IAC5B;AAEA,UAAM,aAAa,kBAAkB,QAAQ,SAAS,CAAC,CAAC;AACxD,UAAM,SACH,cAAc,qBAAqB,QAAQ,6BAA6B,KAAM;AACjF,gBAAY,UAAU,EAAE,OAAO,SAAS,QAAQ,WAAU,cAAS,CAAC,MAAV,mBAAa,GAAA;AACvE,UAAM,OAAO,OAAO,QAAQ;AAC5B,UAAM,UAAU,OAAO,OAAO,SAAS,CAAC;AACxC,gBAAY,UAAU;AACtB,eAAW,IAAI;AACf,UAAM,YAAY,OAAO,QAAA;AACzB,WAAO,8BAA8B,OAAO,CAAC,MAAM,CAAC,EACjD,KAAK,MAAM;;AACV,OAAAA,MAAA,iBAAiB,YAAjB,gBAAAA,IAAA,uBAA2B;AAAA,QACzB;AAAA,QACA;AAAA,QACA,YAAY,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,QAAA,IAAY,SAAS,CAAC;AAAA,MAAA;AAAA,IAEpE,CAAC,EACA,MAAM,CAAC,UAAmB;;AACzB,kBAAY,UAAU;AACtB,UAAI,yBAAyB,UAAU;AACvC,UAAI,6BAA6B,KAAK,KAAK,SAAS,SAAS;AAC3D,sBAAc,EAAE,UAAU,OAAO,UAAU,QAAQ,OAAO,MAAM,QAAQ;AAAA,MAC1E;AACA,OAAAA,MAAA,WAAW,YAAX,gBAAAA,IAAA,iBAAqB,OAAO;AAAA,IAC9B,CAAC,EACA,QAAQ,MAAM;AACb,kBAAY,UAAU;AACtB,UAAI,SAAS,QAAS,YAAW,KAAK;AAAA,IACxC,CAAC;AAAA,EAEL,GAAG;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,CACD;AAED,YAAU,MAAM;;AACd,QAAI,WAAW,QAAQ,gBAAgB,OAAW;AAClD,0BAAsB,UAAU;AAChC,eAAW,WAAW,UAAU;AAC9B,UAAI,CAAC,iBAAiB,QAAQ,EAAE,EAAG;AACnC,iBAAW,QAAQ,QAAQ,SAAS;AAClC,YAAI,KAAK,SAAS,YAAa;AAC/B,cAAM,SAAS,gCAAgC,KAAK,QAAQ;AAC5D,YAAI,WAAW,QAAQ,YAAY,UAAU,OAAO,SAAS,EAAG;AAGhE,cAAI,iBAAY,MAAM,OAAO,SAAS,MAAlC,mBAAqC,WAAU,OAAO,MAAO;AACjE,YAAI,YAAY,QAAQ,IAAI,OAAO,SAAS,MAAM,OAAO,MAAO;AAChE,YAAI,uBAAuB,WAAW,0BAA0B;AAC9D,gCAAsB,UAAU;AAChC;AAAA,QACF;AACA,cAAM,EAAE,WAAW,MAAA,IAAU;AAC7B,+BAAuB,WAAW;AAClC,oBAAY,YAAY,SAAS;AACjC,gBAAQ,QAAQ,YAAY,uBAAuB,EAAE,UAAA,CAAW,CAAC,CAAC,EAC/D,KAAK,CAAC,YAAY;AACjB,sBAAY,QAAQ,IAAI,WAAW,KAAK;AACxC,gBAAM,SAAS,qBAAqB,OAAO;AAC3C,cAAI,WAAW,MAAM;AACnB,wBAAY,aAAa,SAAS;AAClC;AAAA,UACF;AACA,sBAAY,IAAI,OAAO,WAAW,EAAE,SAAS,OAAO,SAAS,OAAO;AAAA,QACtE,CAAC,EACA,MAAM,CAAC,UAAmB;;AACzB,sBAAY,QAAQ,IAAI,WAAW,KAAK;AACxC,sBAAY,aAAa,SAAS;AAClC,WAAAA,MAAA,WAAW,YAAX,gBAAAA,IAAA,iBAAqB,OAAO;AAAA,QAC9B,CAAC,EACA,QAAQ,MAAM;AACb,iCAAuB,WAAW;AAClC,cAAI,sBAAsB,WAAW,SAAS,SAAS;AACrD,iCAAqB,CAAC,UAAU,QAAQ,CAAC;AAAA,UAC3C;AAAA,QACF,CAAC;AAAA,MACL;AAAA,IACF;AAAA,EACF,GAAG;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,CACD;AAED,QAAM,YAAY,YAAY,MAAM;AAClC,gBAAY,CAAC,UAAU,QAAQ,CAAC;AAAA,EAClC,GAAG,CAAA,CAAE;AAEL,QAAM,eAAe;AAAA,IACnB,MAAM,WAAW,QAAQ,CAAC,cAAc,qBAAqB,QAAQ,QAAQ,MAAM;AAAA,IACnF,CAAC,QAAQ,UAAU,UAAU;AAAA,EAAA;AAG/B,SAAO,EAAE,QAAQ,WAAU,iCAAQ,aAAY,OAAO,cAAc,SAAS,UAAA;AAC/E;"}
1
+ {"version":3,"file":"use-deep-agent-window-sync.js","sources":["../../../../agent-runtime/src/use-deep-agent-window-sync.ts"],"sourcesContent":["import type { StatewireThread } from '@assistant-ui/react-statewire';\nimport { useAuiState } from '@assistant-ui/store';\nimport { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';\nimport {\n DEEP_AGENT_HISTORY_PAGE_PROTOCOL_VERSION,\n DEEP_AGENT_INTEREST_PROTOCOL,\n DEEP_AGENT_MAX_MESSAGE_WINDOW,\n DEEP_AGENT_OVERSIZED_INTEREST_CODE,\n} from './generated/runconfig-contract';\nimport {\n DEEP_AGENT_HISTORY_PAGE_TURNS,\n type DeepAgentHistorySnapshot,\n type DeepAgentHistoryStore,\n} from './history-pages';\nimport {\n buildToolOutputCommand,\n type DeepAgentInterestRequest,\n type DeepAgentMessageWindow,\n messageWindowCarries,\n pageInterestRequests,\n readDeepAgentMessageWindow,\n readToolOutputResult,\n readTruncatedToolOutputArtifact,\n restatedInterestRequests,\n} from './message-window';\nimport { readDeepAgentProtocol } from './protocol';\nimport { hasStatewireThreadExtras } from './thread-extras';\nimport type { DeepAgentToolOutputStore } from './tool-outputs';\n\n/**\n * Pages of older history chained per approach to the top. A renderer lowers\n * its near-top level once prepended content has pushed the first message out\n * of its band; this bounds the case where it cannot (no viewport to scroll,\n * a list too short to overflow) so a 2,000-message thread never streams whole.\n */\nexport const DEEP_AGENT_MAX_CHAINED_HISTORY_LOADS = 3;\n\n/**\n * Pages of older history that add no converted message — every message on\n * them folds into the first loaded turn, such as one agent run's hundreds of\n * tool steps — loaded on top of a request (or an approach's chain) before it\n * stops. Such a page changes nothing the user can see, so it neither answers\n * an ask nor counts against the per-approach chain.\n *\n * A page loaded while the oldest loaded message is a reply lands inside that\n * reply's turn, so it asks for the host's largest window\n * (`DEEP_AGENT_MAX_MESSAGE_WINDOW`) instead of the thread's: a run of 373\n * tool calls is about 750 messages, nineteen round trips at the default\n * window and four at the largest, and the turn shows nothing new until its\n * prompt does. A page above a prompt keeps the thread's window.\n */\nexport const DEEP_AGENT_MAX_UNSEEN_HISTORY_LOADS = 25;\n\n/** The converted thread messages the tool-output half scans (web and native `ThreadMessage` both satisfy it). */\nexport interface DeepAgentWindowSyncMessage {\n readonly id: string;\n /** The converted role; lets the opening fill tell a turn's prompt from its reply. */\n readonly role?: string;\n readonly content: readonly { readonly type: string; readonly artifact?: unknown }[];\n}\n\n/** One page interest round trip, for the host's telemetry. */\nexport interface DeepAgentHistoryLoadEvent {\n /** The page's ordinal (`1` extends the head). */\n page: number;\n request: DeepAgentInterestRequest;\n durationMs: number;\n}\n\n/** One `history/page` round trip, for the host's telemetry. */\nexport interface DeepAgentHistoryPageEvent {\n /** The message the page was asked before. */\n before: string;\n /** Turns asked for, and answered. */\n turns: number;\n answered: number;\n durationMs: number;\n}\n\nexport interface UseDeepAgentWindowSyncOptions {\n /**\n * The converted thread messages — read by the host through its platform\n * `useAuiState((s) => s.thread.messages)`, so this hook imports neither\n * `@assistant-ui/react` nor `@assistant-ui/react-native`.\n */\n messages: readonly DeepAgentWindowSyncMessage[];\n /** Messages per page; the thread's window size by default. */\n pageSize?: number;\n /**\n * Full tool outputs fetched back for cut tool messages; the same store the\n * thread converts with (`useDeepAgentThread({ toolOutputs })`). Omitted, the\n * tool-output half is inert (a surface that never shows a cut output whole).\n */\n toolOutputs?: DeepAgentToolOutputStore | undefined;\n /**\n * The renderer's report that the top of the LOADED history is near — a\n * level, not an edge: while it holds and older messages exist, pages load\n * one after another (bounded by `maxChainedLoads` per approach). Omitted for\n * hosts with only a manual affordance (`loadOlder`).\n */\n nearTop?: boolean;\n /** Bumped on every rise of `nearTop` (and every explicit request), so the chain bound is per approach. */\n nearTopEpoch?: number;\n /**\n * Whether a message's content is mounted right now, so lazy fetches (cut\n * tool outputs) target what the user can see rather than the whole loaded\n * history. Omitted: every message counts as mounted (the flat renderers).\n */\n isMessageMounted?: ((messageId: string) => boolean) | undefined;\n /**\n * Cut tool outputs this mount reads at once; the rest wait for one to\n * settle. One mounted message can hold a whole agent run — a hundred tool\n * steps — and the host rate-limits a client's frames, so a reader that sends\n * them all together gets refusals back for most of them. Omitted: every\n * mounted output is asked for together (renderers whose mounted set is\n * already small).\n */\n maxConcurrentToolOutputs?: number;\n maxChainedLoads?: number;\n /** Pages adding no converted message loaded per request before it stops; see {@link DEEP_AGENT_MAX_UNSEEN_HISTORY_LOADS}. */\n maxUnseenLoads?: number;\n /** A page interest resolved (telemetry). */\n onHistoryLoad?: ((event: DeepAgentHistoryLoadEvent) => void) | undefined;\n /**\n * Older turns paged by command (`history/page`, protocol 3) into this store:\n * the same one the thread joins in front of its live window\n * (`useDeepAgentThread({ historyStore })`). Used only against a host that\n * advertises `DEEP_AGENT_HISTORY_PAGE_PROTOCOL_VERSION`; there every older\n * page (the button, the near-top chain, the opening fill) is a `history/page`\n * of whole turns and no interest page is stated, so the attach's view stays\n * the head however far back the reader goes. Omitted, or an older host:\n * interest pages, as before.\n */\n historyPages?: DeepAgentHistoryStore | undefined;\n /** Turns one history page asks for. */\n historyPageTurns?: number;\n /** A history page resolved (telemetry). */\n onHistoryPage?: ((event: DeepAgentHistoryPageEvent) => void) | undefined;\n /** A read failed; the sync itself recovers (the pending marks are released). */\n onError?:\n | ((error: unknown, command: 'athena/interest' | 'history/page' | 'history/tool_output') => void)\n | undefined;\n now?: () => number;\n}\n\nexport interface DeepAgentWindowSyncState {\n /** The joined window, or `null` while the head interest has not resolved (both halves are inert). */\n window: DeepAgentMessageWindow | null;\n /** Whether the host holds messages older than the loaded window. */\n hasOlder: boolean;\n /**\n * Whether the next page can be asked for: older messages exist and the\n * view has room for them. `hasOlder && !canLoadOlder` is the depth limit —\n * an edge row says so instead of offering a dead button. The host bounds\n * one attach's view twice: `MAX_INTERESTS` requests, and an interest\n * document that lists every loaded message within a byte budget (about a\n * thousand messages). The second is found when the host refuses a page as\n * `oversized-interest` ({@link isOversizedInterestRejection}).\n */\n canLoadOlder: boolean;\n /** A page of older history is in flight. */\n loading: boolean;\n /** Ask for the next page of older history now (an edge row's button, a keypress). A no-op before the head resolved, at the beginning, or once `canLoadOlder` is false. */\n loadOlder: () => void;\n}\n\n/**\n * Whether an `athena/interest` `set` came back refused because the windows it\n * states resolve past what one attach's interest document carries: the view\n * is as deep as this connection can load, and asking again only gets the same\n * refusal.\n */\nexport function isOversizedInterestRejection(error: unknown): boolean {\n const failure = (error as { failure?: { fate?: unknown; code?: unknown } } | null | undefined)\n ?.failure;\n return failure?.fate === 'rejected' && failure.code === DEEP_AGENT_OVERSIZED_INTEREST_CODE;\n}\n\nconst everyMessageMounted = (): boolean => true;\nconst defaultNow = (): number => Date.now();\nconst noopSubscribe = () => () => {};\n\n/** The statewire extras of the nearest aui thread; throws on a non-statewire thread like the library's own hooks. */\nconst useStatewireExtras = (): StatewireThread.Extras =>\n useAuiState((s) => {\n if (!hasStatewireThreadExtras(s.thread.extras)) {\n throw new Error('useDeepAgentWindowSync requires a statewire thread');\n }\n return s.thread.extras as StatewireThread.Extras;\n });\n\n/** The joined interest window of the nearest statewire thread. */\nexport function useDeepAgentMessageWindow(): DeepAgentMessageWindow | null {\n const documents = useStatewireExtras().documents;\n const mounted = useSyncExternalStore(\n documents?.subscribe ?? noopSubscribe,\n () => documents?.list(),\n () => documents?.list()\n );\n return useMemo(() => readDeepAgentMessageWindow(mounted), [mounted]);\n}\n\n/**\n * The client half of the interest window, shared by every surface — mounted\n * once per runtime, inside its aui provider.\n *\n * - **Older history.** While the host renderer reports the top of the loaded\n * history near (`nearTop`) and the window says older messages exist, this\n * re-states the interests with one more page (anchored at the window's\n * first message) — the host mounts its documents for this client alone —\n * chaining at most `maxChainedLoads` pages per approach; `loadOlder` asks\n * for one page on demand (an edge row's button). Never two loads in flight.\n * A re-attach's hello carries the view the client declares on the attach\n * (`useDeepAgentThread`), every page in it however it was loaded (this\n * hook, a handover's or a search's reveal, an edit's parent). A hello that\n * does not carry the view the last connected session held — a host that\n * reads no declaration and has no memo of the client id — gets it stated\n * again ({@link restatedInterestRequests}).\n * - **Cut tool outputs.** Tool cards whose output the wire carried cut\n * (`artifact._athena_content_truncated`) get it fetched whole through\n * `history/tool_output` once their message is mounted, at most\n * `maxConcurrentToolOutputs` at a time; the converter puts it back\n * (`toolOutputs`), so the card re-renders complete. An output the host\n * replaced since (the marker's `chars` changed) is fetched again.\n */\n/**\n * Whether an older page put a new converted message ahead of the thread's\n * first one. Older history only ever merges into or lands before the first\n * converted message, so the second one is a stable landmark: it moved down iff\n * the page showed something. A new tail message does not move it. Without a\n * second message (or once it is gone) the converted count decides.\n */\nfunction pageShowedEarlierMessage(\n from: { count: number; secondId: string | undefined },\n messages: readonly DeepAgentWindowSyncMessage[]\n): boolean {\n if (from.secondId !== undefined) {\n const index = messages.findIndex((message) => message.id === from.secondId);\n if (index !== -1) return index > 1;\n }\n return messages.length > from.count;\n}\n\n/** Whether a converted message is a turn's reply rather than its prompt (unknown without a role). */\nfunction isReply(message: DeepAgentWindowSyncMessage | undefined): boolean {\n return message?.role !== undefined && message.role !== 'user';\n}\n\n/**\n * Whether the loaded thread still opens without context: it should open on a\n * prompt and hold at least one earlier turn, so the newest turn shows with the\n * one before it (a turn is a prompt followed by its reply). Queued and live\n * prompts land at the tail with no reply yet, so they never count; prompts\n * alone need nothing. Without roles, a lone converted message stands in for it.\n */\nfunction opensMidTurn(messages: readonly DeepAgentWindowSyncMessage[]): boolean {\n const first = messages[0];\n if (first === undefined) return false;\n if (messages.every((message) => message.role === undefined)) return messages.length < 2;\n if (first.role !== 'user') return true;\n if (messages.every((message) => message.role === 'user')) return false;\n let turns = 0;\n for (let index = 1; index < messages.length; index += 1) {\n if (messages[index - 1]!.role === 'user' && messages[index]!.role !== 'user') turns += 1;\n }\n return turns < 2;\n}\n\nexport function useDeepAgentWindowSync({\n messages,\n pageSize,\n toolOutputs,\n nearTop = false,\n nearTopEpoch = 0,\n isMessageMounted = everyMessageMounted,\n maxConcurrentToolOutputs = Number.POSITIVE_INFINITY,\n maxChainedLoads = DEEP_AGENT_MAX_CHAINED_HISTORY_LOADS,\n maxUnseenLoads = DEEP_AGENT_MAX_UNSEEN_HISTORY_LOADS,\n onHistoryLoad,\n historyPages,\n historyPageTurns = DEEP_AGENT_HISTORY_PAGE_TURNS,\n onHistoryPage,\n onError,\n now = defaultNow,\n}: UseDeepAgentWindowSyncOptions): DeepAgentWindowSyncState {\n const { sendCommand, invoke, connection } = useStatewireExtras();\n const window = useDeepAgentMessageWindow();\n const hostVersion = useAuiState((s) => {\n const extras = s.thread.extras as Partial<StatewireThread.Extras> | undefined;\n return readDeepAgentProtocol(extras?.state)?.version ?? 0;\n });\n // Older history comes by command when the surface keeps a store for it and\n // the host answers it; the view then stays the head.\n const pagesByCommand =\n historyPages !== undefined && hostVersion >= DEEP_AGENT_HISTORY_PAGE_PROTOCOL_VERSION;\n const historySnapshot = useSyncExternalStore(\n historyPages?.subscribe ?? noopSubscribe,\n () => historyPages?.getSnapshot() ?? null,\n () => historyPages?.getSnapshot() ?? null\n );\n const liveIds = useMemo(() => new Set(window?.chain.map((entry) => entry.id) ?? []), [window]);\n // The pages that still meet the live window; a detached store is cleared below.\n const history: DeepAgentHistorySnapshot | null =\n historySnapshot !== null &&\n window !== null &&\n historyPages !== undefined &&\n !historyPages.isDetached({ threadId: window.threadId, liveIds })\n ? historySnapshot\n : null;\n const onHistoryPageRef = useRef(onHistoryPage);\n onHistoryPageRef.current = onHistoryPage;\n const [loading, setLoading] = useState(false);\n // The view the host refused a page over as deeper than one attach holds\n // (`oversized-interest`): its conversation and how many messages it covered.\n // A view at least that large gets the same refusal (a run's appends only\n // grow it); one that shrank — an edit rewound the transcript, a re-attach\n // came back smaller — may fit a page again.\n const [spentDepth, setSpentDepth] = useState<{ threadId: string | undefined; loaded: number } | null>(\n null\n );\n const depthSpent =\n spentDepth !== null &&\n window !== null &&\n spentDepth.threadId === window.threadId &&\n window.chain.length >= spentDepth.loaded;\n const [requests, setRequests] = useState(0);\n const servedRequestsRef = useRef(0);\n const inflightRef = useRef(false);\n const chainRef = useRef({ epoch: 0, loads: 0, unseen: 0, requested: false });\n // Pages loaded to show the opening turn's prompt; `null` once it shows, the\n // user took over, or a page failed — never re-armed for this mount.\n const openingFillRef = useRef<number | null>(0);\n // The converted thread when the in-flight page was stated; `null` once its\n // settle was judged (or it failed).\n const pageFromRef = useRef<{ count: number; secondId: string | undefined } | null>(null);\n const aliveRef = useRef(true);\n // The window the last connected session held, whoever paged it in, and the\n // session whose hello it was last checked against (a hello resets the\n // documents to what the host seeded).\n const heldRef = useRef<DeepAgentMessageWindow | null>(null);\n const restatedConnectionRef = useRef<unknown>(null);\n // Outputs already asked for and answered by this mount — fetched, refused,\n // or failed — by the `chars` they were asked for. The converter re-runs on\n // every wire change, and the store is a bounded LRU: without this memory a\n // refused read would leave on every pass, and once a transcript holds more\n // cut outputs than the store retains, each fetch would evict a still-loaded\n // output and the next pass would fetch it again, forever. An evicted output\n // renders its cut text (the REST display limit) rather than re-entering the\n // loop; a marker whose `chars` moved (the host replaced the output) is asked\n // for again.\n const answeredRef = useRef(new Map<string, number>());\n // Reads this mount has in flight, and whether a pass left outputs waiting\n // for one to settle (`maxConcurrentToolOutputs`): only then does a settle\n // re-run the pass, so an unbounded mount renders no more than before.\n const toolOutputsInFlightRef = useRef(0);\n const toolOutputsWaitingRef = useRef(false);\n const [toolOutputSettles, setToolOutputSettles] = useState(0);\n const onHistoryLoadRef = useRef(onHistoryLoad);\n onHistoryLoadRef.current = onHistoryLoad;\n const onErrorRef = useRef(onError);\n onErrorRef.current = onError;\n const nowRef = useRef(now);\n nowRef.current = now;\n useEffect(() => {\n aliveRef.current = true;\n return () => {\n aliveRef.current = false;\n };\n }, []);\n\n // Pages that no longer meet the live window (another conversation, or an\n // edit rewound past their anchor) describe another transcript: drop them,\n // and the edge offers older history again from the window's first message.\n useEffect(() => {\n if (historyPages === undefined || historySnapshot === null || window === null) return;\n if (window.chain.length === 0) return;\n if (historyPages.isDetached({ threadId: window.threadId, liveIds })) historyPages.clear();\n }, [historyPages, historySnapshot, window, liveIds]);\n\n useEffect(() => {\n if (connection.status !== 'connected' || window === null) return;\n if (restatedConnectionRef.current === connection) {\n heldRef.current = window;\n return;\n }\n // Decided once per connected session, on the hello's interest document.\n restatedConnectionRef.current = connection;\n const held = heldRef.current;\n heldRef.current = window;\n // A page still in flight was stated over the held window: it restates it.\n if (held === null || inflightRef.current || messageWindowCarries({ hello: window, held })) return;\n // Another conversation's hello (the runtime switched threads under this mount).\n if (held.threadId !== undefined && window.threadId !== undefined && held.threadId !== window.threadId) {\n return;\n }\n // Held like a page, so no page is stated over it while it is in flight.\n inflightRef.current = true;\n setLoading(true);\n invoke(DEEP_AGENT_INTEREST_PROTOCOL, 'set', [restatedInterestRequests({ held, hello: window })])\n .catch((error: unknown) => {\n onErrorRef.current?.(error, 'athena/interest');\n })\n .finally(() => {\n inflightRef.current = false;\n if (aliveRef.current) setLoading(false);\n });\n }, [connection, invoke, window]);\n\n useEffect(() => {\n if (window === null || inflightRef.current) return;\n // Older turns by command when this surface keeps a store and the host\n // answers it (whole turns before the oldest message held, outside the\n // view); interest pages otherwise. The asks are the same either way.\n const store = pagesByCommand ? historyPages : undefined;\n if (depthSpent && store === undefined) {\n // An ask now would get the same refusal: answer it here, so it never\n // reaches another conversation this mount switches to.\n servedRequestsRef.current = requests;\n pageFromRef.current = null;\n return;\n }\n const from = pageFromRef.current;\n pageFromRef.current = null;\n // A page by command always shows a turn's prompt (it never sets `from`).\n const lastPageUnseen =\n from !== null && from.count > 0 && !pageShowedEarlierMessage(from, messages);\n const pageRequests = store === undefined ? pageInterestRequests(window, pageSize) : null;\n if (store === undefined ? pageRequests === null : !(history?.more ?? window.hasOlder)) return;\n const requested = requests !== servedRequestsRef.current;\n const chain = chainRef.current;\n let fillPage = false;\n if (\n openingFillRef.current !== null &&\n (requested || nearTop || (messages.length > 0 && !opensMidTurn(messages)))\n ) {\n openingFillRef.current = null;\n }\n if (requested) {\n // An explicit ask re-arms the chain for this approach.\n servedRequestsRef.current = requests;\n chainRef.current = { epoch: nearTopEpoch, loads: 1, unseen: 0, requested: true };\n } else if (lastPageUnseen && (chain.requested || (nearTop && chain.epoch === nearTopEpoch))) {\n // The page landed inside the first loaded turn: keep going until one shows.\n if (chain.unseen >= maxUnseenLoads) return;\n chain.unseen += 1;\n } else if (openingFillRef.current !== null && opensMidTurn(messages)) {\n if (openingFillRef.current >= maxUnseenLoads) return;\n openingFillRef.current += 1;\n fillPage = true;\n } else {\n if (!nearTop) return;\n if (chain.epoch !== nearTopEpoch) {\n chainRef.current = { epoch: nearTopEpoch, loads: 0, unseen: 0, requested: false };\n }\n if (chainRef.current.loads >= maxChainedLoads) return;\n chainRef.current.loads += 1;\n }\n const startedAt = nowRef.current();\n const durationMs = () => Math.max(0, Math.round(nowRef.current() - startedAt));\n const settle = () => {\n inflightRef.current = false;\n if (aliveRef.current) setLoading(false);\n };\n if (store !== undefined) {\n const before = history?.turns[0]?.id ?? window.chain[0]?.id;\n if (before === undefined) return;\n inflightRef.current = true;\n setLoading(true);\n store\n .requestOlder({\n send: sendCommand,\n threadId: window.threadId ?? '',\n before,\n beforeIsPrompt:\n history !== null || window.messages.find((message) => message.id === before)?.type === 'human',\n turns: historyPageTurns,\n })\n .then((answered) => {\n onHistoryPageRef.current?.({ before, turns: historyPageTurns, answered, durationMs: durationMs() });\n })\n .catch((error: unknown) => {\n if (fillPage) openingFillRef.current = null;\n onErrorRef.current?.(error, 'history/page');\n })\n .finally(settle);\n return;\n }\n if (pageRequests === null) return;\n // The page continues the oldest loaded turn (see DEEP_AGENT_MAX_UNSEEN_HISTORY_LOADS).\n const insideTurn = lastPageUnseen || isReply(messages[0]);\n const stated =\n (insideTurn && pageInterestRequests(window, DEEP_AGENT_MAX_MESSAGE_WINDOW)) || pageRequests;\n pageFromRef.current = { count: messages.length, secondId: messages[1]?.id };\n const page = window.pages + 1;\n const request = stated[stated.length - 1] as DeepAgentInterestRequest;\n inflightRef.current = true;\n setLoading(true);\n invoke(DEEP_AGENT_INTEREST_PROTOCOL, 'set', [stated])\n .then(() => {\n onHistoryLoadRef.current?.({ page, request, durationMs: durationMs() });\n })\n .catch((error: unknown) => {\n pageFromRef.current = null;\n if (fillPage) openingFillRef.current = null;\n if (isOversizedInterestRejection(error) && aliveRef.current) {\n setSpentDepth({ threadId: window.threadId, loaded: window.chain.length });\n }\n onErrorRef.current?.(error, 'athena/interest');\n })\n .finally(settle);\n // `loading` re-arms the chain once a page settled after its documents moved the window.\n }, [\n nearTop,\n nearTopEpoch,\n requests,\n window,\n loading,\n messages,\n invoke,\n maxChainedLoads,\n maxUnseenLoads,\n pageSize,\n depthSpent,\n pagesByCommand,\n historyPages,\n history,\n historyPageTurns,\n sendCommand,\n ]);\n\n useEffect(() => {\n if (window === null || toolOutputs === undefined) return;\n toolOutputsWaitingRef.current = false;\n for (const message of messages) {\n if (!isMessageMounted(message.id)) continue;\n for (const part of message.content) {\n if (part.type !== 'tool-call') continue;\n const marker = readTruncatedToolOutputArtifact(part.artifact);\n if (marker === null || toolOutputs.isPending(marker.messageId)) continue;\n // Fetched for this very length already; a different length means the\n // host replaced the output under the same id since.\n if (toolOutputs.entry(marker.messageId)?.chars === marker.chars) continue;\n if (answeredRef.current.get(marker.messageId) === marker.chars) continue;\n if (toolOutputsInFlightRef.current >= maxConcurrentToolOutputs) {\n toolOutputsWaitingRef.current = true;\n return;\n }\n const { messageId, chars } = marker;\n toolOutputsInFlightRef.current += 1;\n toolOutputs.markPending(messageId);\n Promise.resolve(sendCommand(buildToolOutputCommand({ messageId })))\n .then((payload) => {\n answeredRef.current.set(messageId, chars);\n const result = readToolOutputResult(payload);\n if (result === null) {\n toolOutputs.clearPending(messageId);\n return;\n }\n toolOutputs.set(result.messageId, { content: result.content, chars });\n })\n .catch((error: unknown) => {\n answeredRef.current.set(messageId, chars);\n toolOutputs.clearPending(messageId);\n onErrorRef.current?.(error, 'history/tool_output');\n })\n .finally(() => {\n toolOutputsInFlightRef.current -= 1;\n if (toolOutputsWaitingRef.current && aliveRef.current) {\n setToolOutputSettles((count) => count + 1);\n }\n });\n }\n }\n }, [\n messages,\n isMessageMounted,\n window,\n sendCommand,\n toolOutputs,\n maxConcurrentToolOutputs,\n toolOutputSettles,\n ]);\n\n const loadOlder = useCallback(() => {\n setRequests((count) => count + 1);\n }, []);\n\n // By command, the host bounds nothing but the transcript's start: older\n // turns exist while the pages say so (the window's own edge before any).\n const hasOlder = pagesByCommand\n ? history !== null\n ? history.more\n : (window?.hasOlder ?? false)\n : (window?.hasOlder ?? false);\n const canLoadOlder = useMemo(\n () =>\n pagesByCommand\n ? window !== null && hasOlder\n : window !== null && !depthSpent && pageInterestRequests(window, pageSize) !== null,\n [pagesByCommand, hasOlder, window, pageSize, depthSpent]\n );\n\n return { window, hasOlder, canLoadOlder, loading, loadOlder };\n}\n"],"names":["_a"],"mappings":"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"}