@athenaintel/react 0.32.0 → 0.35.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.
- package/README.md +99 -9
- package/dist/agent-runtime/src/composer-markdown.cjs.map +1 -1
- package/dist/agent-runtime/src/composer-markdown.js.map +1 -1
- package/dist/agent-runtime/src/deep-agent-transport-recipe.cjs +2 -3
- package/dist/agent-runtime/src/deep-agent-transport-recipe.cjs.map +1 -1
- package/dist/agent-runtime/src/deep-agent-transport-recipe.js +3 -4
- package/dist/agent-runtime/src/deep-agent-transport-recipe.js.map +1 -1
- package/dist/agent-runtime/src/edit-command.cjs.map +1 -1
- package/dist/agent-runtime/src/edit-command.js.map +1 -1
- package/dist/agent-runtime/src/messages.cjs +57 -2
- package/dist/agent-runtime/src/messages.cjs.map +1 -1
- package/dist/agent-runtime/src/messages.js +57 -2
- package/dist/agent-runtime/src/messages.js.map +1 -1
- package/dist/agent-runtime/src/quota-safe-local-storage-session.cjs +24 -3
- package/dist/agent-runtime/src/quota-safe-local-storage-session.cjs.map +1 -1
- package/dist/agent-runtime/src/quota-safe-local-storage-session.js +24 -3
- package/dist/agent-runtime/src/quota-safe-local-storage-session.js.map +1 -1
- package/dist/agent-runtime/src/statewire-transport.cjs +4 -13
- package/dist/agent-runtime/src/statewire-transport.cjs.map +1 -1
- package/dist/agent-runtime/src/statewire-transport.js +5 -14
- package/dist/agent-runtime/src/statewire-transport.js.map +1 -1
- package/dist/agent-runtime/src/tool-metadata.cjs +1 -1
- package/dist/agent-runtime/src/tool-metadata.cjs.map +1 -1
- package/dist/agent-runtime/src/tool-metadata.js +1 -1
- package/dist/agent-runtime/src/tool-metadata.js.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-thread.cjs.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-thread.js.map +1 -1
- package/dist/assets/AthenaAssetEmbed.cjs +3 -1
- package/dist/assets/AthenaAssetEmbed.cjs.map +1 -1
- package/dist/assets/AthenaAssetEmbed.d.ts +7 -0
- package/dist/assets/AthenaAssetEmbed.js +4 -2
- package/dist/assets/AthenaAssetEmbed.js.map +1 -1
- package/dist/assets/use-asset-embed.cjs +71 -71
- package/dist/assets/use-asset-embed.cjs.map +1 -1
- package/dist/assets/use-asset-embed.d.ts +20 -7
- package/dist/assets/use-asset-embed.js +72 -72
- package/dist/assets/use-asset-embed.js.map +1 -1
- package/dist/catalog/agent-model-options.cjs +122 -0
- package/dist/catalog/agent-model-options.cjs.map +1 -0
- package/dist/catalog/agent-model-options.d.ts +115 -0
- package/dist/catalog/agent-model-options.js +122 -0
- package/dist/catalog/agent-model-options.js.map +1 -0
- package/dist/catalog/chat-pickers.cjs +184 -4
- package/dist/catalog/chat-pickers.cjs.map +1 -1
- package/dist/catalog/chat-pickers.d.ts +27 -3
- package/dist/catalog/chat-pickers.js +187 -7
- package/dist/catalog/chat-pickers.js.map +1 -1
- package/dist/catalog/runtime-catalog.cjs +117 -3
- package/dist/catalog/runtime-catalog.cjs.map +1 -1
- package/dist/catalog/runtime-catalog.d.ts +104 -0
- package/dist/catalog/runtime-catalog.js +119 -5
- package/dist/catalog/runtime-catalog.js.map +1 -1
- package/dist/chat/AthenaChat.cjs.map +1 -1
- package/dist/chat/AthenaChat.d.ts +8 -7
- package/dist/chat/AthenaChat.js.map +1 -1
- package/dist/chat/super-grouping/SuperGroupingCard.cjs +30 -3
- package/dist/chat/super-grouping/SuperGroupingCard.cjs.map +1 -1
- package/dist/chat/super-grouping/SuperGroupingCard.d.ts +12 -9
- package/dist/chat/super-grouping/SuperGroupingCard.js +31 -4
- package/dist/chat/super-grouping/SuperGroupingCard.js.map +1 -1
- package/dist/chat/super-grouping/SuperGroupingTranscript.cjs.map +1 -1
- package/dist/chat/super-grouping/SuperGroupingTranscript.d.ts +8 -8
- package/dist/chat/super-grouping/SuperGroupingTranscript.js.map +1 -1
- package/dist/chat/super-grouping/activity-timeline.cjs.map +1 -1
- package/dist/chat/super-grouping/activity-timeline.d.ts +4 -4
- package/dist/chat/super-grouping/activity-timeline.js.map +1 -1
- package/dist/chat/super-grouping/index.d.ts +6 -5
- package/dist/chat/super-grouping/toolkit-map.cjs +3 -1
- package/dist/chat/super-grouping/toolkit-map.cjs.map +1 -1
- package/dist/chat/super-grouping/toolkit-map.js +3 -1
- package/dist/chat/super-grouping/toolkit-map.js.map +1 -1
- package/dist/chat-ui/index.cjs +1 -0
- package/dist/chat-ui/index.cjs.map +1 -1
- package/dist/chat-ui/index.d.ts +1 -1
- package/dist/chat-ui/index.js +2 -1
- package/dist/chat-ui/mentions/suggestions/data/api-client.cjs +33 -2
- package/dist/chat-ui/mentions/suggestions/data/api-client.cjs.map +1 -1
- package/dist/chat-ui/mentions/suggestions/data/api-client.d.ts +14 -0
- package/dist/chat-ui/mentions/suggestions/data/api-client.js +33 -2
- package/dist/chat-ui/mentions/suggestions/data/api-client.js.map +1 -1
- package/dist/index.cjs +1 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/lib/use-athena-rest-query.cjs +4 -0
- package/dist/lib/use-athena-rest-query.cjs.map +1 -1
- package/dist/lib/use-athena-rest-query.d.ts +6 -0
- package/dist/lib/use-athena-rest-query.js +4 -0
- package/dist/lib/use-athena-rest-query.js.map +1 -1
- package/dist/provider/AthenaProvider.cjs +29 -8
- package/dist/provider/AthenaProvider.cjs.map +1 -1
- package/dist/provider/AthenaProvider.d.ts +14 -4
- package/dist/provider/AthenaProvider.js +31 -10
- package/dist/provider/AthenaProvider.js.map +1 -1
- package/dist/provider/agent-selection-context.cjs +6 -0
- package/dist/provider/agent-selection-context.cjs.map +1 -0
- package/dist/provider/agent-selection-context.d.ts +14 -0
- package/dist/provider/agent-selection-context.js +6 -0
- package/dist/provider/agent-selection-context.js.map +1 -0
- package/dist/react/package.json.cjs +1 -1
- package/dist/react/package.json.js +1 -1
- package/dist/runtime/StatewireThreadConfigLaneSync.cjs +125 -0
- package/dist/runtime/StatewireThreadConfigLaneSync.cjs.map +1 -0
- package/dist/runtime/StatewireThreadConfigLaneSync.d.ts +54 -0
- package/dist/runtime/StatewireThreadConfigLaneSync.js +125 -0
- package/dist/runtime/StatewireThreadConfigLaneSync.js.map +1 -0
- package/dist/runtime/statewire-model-lane.cjs +0 -12
- package/dist/runtime/statewire-model-lane.cjs.map +1 -1
- package/dist/runtime/statewire-model-lane.d.ts +3 -1
- package/dist/runtime/statewire-model-lane.js +0 -12
- package/dist/runtime/statewire-model-lane.js.map +1 -1
- package/dist/runtime/statewire-server-lane-context.cjs.map +1 -1
- package/dist/runtime/statewire-server-lane-context.d.ts +2 -2
- package/dist/runtime/statewire-server-lane-context.js.map +1 -1
- package/dist/runtime/statewire-thread-config-lane.cjs +84 -0
- package/dist/runtime/statewire-thread-config-lane.cjs.map +1 -0
- package/dist/runtime/statewire-thread-config-lane.d.ts +94 -0
- package/dist/runtime/statewire-thread-config-lane.js +84 -0
- package/dist/runtime/statewire-thread-config-lane.js.map +1 -0
- package/dist/runtime/useAthenaStatewireRuntime.cjs +2 -2
- package/dist/runtime/useAthenaStatewireRuntime.cjs.map +1 -1
- package/dist/runtime/useAthenaStatewireRuntime.js +2 -2
- package/dist/runtime/useAthenaStatewireRuntime.js.map +1 -1
- package/dist/threads/use-athena-thread-model.cjs +11 -3
- package/dist/threads/use-athena-thread-model.cjs.map +1 -1
- package/dist/threads/use-athena-thread-model.d.ts +5 -0
- package/dist/threads/use-athena-thread-model.js +12 -4
- package/dist/threads/use-athena-thread-model.js.map +1 -1
- package/package.json +3 -3
- package/dist/runtime/StatewireModelLaneSync.cjs +0 -96
- package/dist/runtime/StatewireModelLaneSync.cjs.map +0 -1
- package/dist/runtime/StatewireModelLaneSync.d.ts +0 -45
- package/dist/runtime/StatewireModelLaneSync.js +0 -96
- package/dist/runtime/StatewireModelLaneSync.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAthenaStatewireRuntime.cjs","sources":["../../src/runtime/useAthenaStatewireRuntime.ts"],"sourcesContent":["import type { AssistantClient, Toolkit } from '@assistant-ui/react';\nimport { Tools, useAui } from '@assistant-ui/react';\nimport {\n DEEP_AGENT_DEFAULT_MESSAGE_WINDOW,\n type DeepAgentMessageWindowDeclaration,\n type DeepAgentStreamValues,\n type DeepAgentThreadElement,\n type DeepAgentThreadSnapshotCache,\n type DeepAgentToolOutputStore,\n isDeepAgentStateHydrated,\n isStatewireChannelTeardown,\n logUnhandledStatewireError,\n type RegisteredClientTool,\n useDeepAgentRuntime,\n type UseDeepAgentThreadOptions,\n withStagedComposerMarkdown,\n} from '@athenaintel/agent-runtime';\nimport { useContext, useMemo, useRef } from 'react';\nimport { AthenaAuthContext } from '../auth/AthenaAuthContext';\nimport {\n collectStatewireClientTools,\n statewireClientToolWireEntries,\n} from '../chat/statewire-client-tools';\nimport { athenaDiagnostics } from '../diagnostics/athena-diagnostics';\nimport {\n type AthenaStatewireTokenGetter,\n resolveStatewireAuthHeaders,\n} from './statewire-auth-headers';\nimport { statewireSessionKey } from './statewire-session-key';\n\ntype AthenaDiagnosticsSpanStarter = typeof athenaDiagnostics.span;\nimport {\n buildStatewireRunConfig,\n DEFAULT_STATEWIRE_MODEL,\n resolveAgentProp,\n} from './statewire-run-config';\n\nexport { DEFAULT_STATEWIRE_MODEL };\n\n/**\n * The head interest every SDK attach states (`athena/interest`): the host\n * mounts the transcript's tail as message documents instead of every message\n * — megabytes on long threads, over the 1 MiB frame limit of stock websocket\n * clients. Older pages load from the `StatewireHistoryEdge` row the chat UI\n * mounts above the messages, and cut tool outputs are fetched back for the\n * cards that show them.\n */\nexport const ATHENA_SDK_MESSAGE_WINDOW: DeepAgentMessageWindowDeclaration = {\n size: DEEP_AGENT_DEFAULT_MESSAGE_WINDOW,\n};\n\nexport interface AthenaStatewireRuntimeConfig {\n /** Base URL of the statewire sync host (the `…/v2/threads` mount). */\n syncUrl: string;\n /** Thread ID the statewire session attaches to. */\n threadId: string;\n /** True only for a never-started local chat. Existing (or pre-assigned)\n * threads load until their server snapshot arrives. */\n isNewChat?: boolean;\n /** API key for authentication. Used when no auth token is provided. */\n apiKey?: string;\n /** Auth token. Takes priority over apiKey. */\n token?: string | null;\n /** Resolve the latest token immediately before a request (for expiring\n * clients). Called with `{ force: true }` right after the host refused the\n * previous credential (the transport's `staleAuth`), so a caching getter\n * mints a fresh token instead of returning the refused one. */\n getToken?: AthenaStatewireTokenGetter;\n /**\n * Per-provider LRU of each thread's last wire state. A switch back to a\n * remembered thread mounts with that snapshot and projects ready on the\n * first frame instead of a loading treatment; the attach snapshot then\n * replaces it. Owned above the per-thread mount so it survives switches.\n */\n snapshotCache?: DeepAgentThreadSnapshotCache;\n /** The head interest stated on the attach (see {@link ATHENA_SDK_MESSAGE_WINDOW}, the default). */\n messageWindow?: DeepAgentMessageWindowDeclaration;\n /**\n * Full tool outputs fetched back for a windowed wire's cut tool messages;\n * the thread re-converts when one lands. `AthenaProvider` owns one per\n * provider and hands the same store to `StatewireHistoryEdge`.\n */\n toolOutputs?: DeepAgentToolOutputStore;\n /** LLM model identifier. Defaults to the deep-agent default model. */\n model?: string;\n /** Agent selector: only `collab_agent:<asset_id>` refs are accepted (the run\n * executes AS that published collab agent). Any other non-empty value\n * throws — see `resolveAgentProp`. */\n agent?: string;\n /** Collab-agent channel id selecting that channel's overrides. */\n channel?: string;\n /** Backend toolkit IDs to enable. */\n tools?: string[];\n /** Frontend tool IDs to include in enabled_tools. */\n frontendToolIds?: string[];\n /** Frontend tools registered on the assistant client. Tools with a local\n * `execute` are also declared as `client_tools` and executed through the\n * statewire client-tool bridge. */\n frontendToolkit?: Toolkit;\n /** Asset IDs to include in the workbench. */\n workbench?: string[];\n /** Knowledge base IDs for RAG context. */\n knowledgeBase?: string[];\n /** System prompt override. */\n systemPrompt?: string;\n /** Client app identifier for session filtering. Sent as app_id in runConfig.custom. */\n appId?: string;\n /** Additional fields merged into runConfig.custom. */\n extraRunConfig?: Record<string, unknown>;\n /** Called on statewire transport errors (send rejections, stream failures). */\n onError?: (error: Error) => void;\n /** Projected connection status changes (deduped; null on unmount). */\n onConnectionChange?: UseDeepAgentThreadOptions['onConnectionChange'];\n /** Raw connection observer — carries the transport's reconnect handle. */\n onRawConnectionChange?: UseDeepAgentThreadOptions['onRawConnectionChange'];\n /** Notified when a run starts/stops. */\n onRunningChange?: UseDeepAgentThreadOptions['onRunningChange'];\n /**\n * Observes the host's own state: called once the attach's hello has landed\n * (with the snapshot it carried) and then on every later frame — never with\n * the placeholder or remembered snapshot a thread renders before its hello.\n * Called per streamed token, so keep it cheap.\n */\n onServerState?: (state: DeepAgentStreamValues) => void;\n}\n\nconst EMPTY_TOOLKIT: Toolkit = {};\nconst EMPTY_TOOL_IDS: string[] = [];\n\n/** The client tools derived from the runtime's frontend toolkit — exposed so\n * the provider can mount the client-tool bridge with the same set. */\nexport function useStatewireClientTools(\n frontendToolkit: Toolkit | undefined,\n): RegisteredClientTool[] {\n return useMemo(\n () => collectStatewireClientTools(frontendToolkit ?? EMPTY_TOOLKIT),\n [frontendToolkit],\n );\n}\n\n/**\n * Statewire runtime for the Athena SDK — a thin shim over the shared\n * `@athenaintel/agent-runtime` `useDeepAgentRuntime`: it attaches to the\n * agora-hosted deep-agent thread at `${syncUrl}/${threadId}` and returns the\n * `AssistantClient` to mount via `AuiProvider` — the same integration shape as\n * the Olympus, Chrome extension, Office, and mobile statewire hosts.\n *\n * Auth: a token (resolved live through `getToken` when provided) rides\n * `Authorization: Bearer`, and an API key falls back to `X-API-KEY` — both\n * accepted by the statewire host.\n */\nexport function useAthenaStatewireRuntime(\n config: AthenaStatewireRuntimeConfig,\n): AssistantClient {\n const {\n syncUrl,\n threadId,\n isNewChat = false,\n apiKey,\n token,\n getToken,\n model,\n agent,\n channel,\n tools = EMPTY_TOOL_IDS,\n frontendToolIds = EMPTY_TOOL_IDS,\n frontendToolkit = EMPTY_TOOLKIT,\n workbench,\n knowledgeBase,\n systemPrompt,\n appId,\n extraRunConfig,\n snapshotCache,\n messageWindow = ATHENA_SDK_MESSAGE_WINDOW,\n toolOutputs,\n onError,\n onConnectionChange,\n onRawConnectionChange,\n onRunningChange,\n onServerState,\n } = config;\n\n // Fail loud at mount, not at first send: the statewire host always runs the\n // Athena deep agent, so an `agent` outside the collab_agent namespace can\n // never apply. Throwing here (render time) is what makes a misconfigured\n // integration visible instead of quietly answering as the wrong agent.\n resolveAgentProp({ agent });\n\n const clientTools = useStatewireClientTools(frontendToolkit);\n\n // The persisted command lane is keyed by WHO is signed in as well as by host\n // and thread (see `statewireSessionKey`).\n const authContext = useContext(AthenaAuthContext);\n const principal = authContext?.user?.userId ?? '';\n\n // Attach → first server snapshot span, keyed per thread attach.\n const attachSpanRef = useRef<ReturnType<AthenaDiagnosticsSpanStarter> | null>(null);\n const attachThreadRef = useRef<string | null>(null);\n if (attachThreadRef.current !== threadId) {\n attachThreadRef.current = threadId;\n attachSpanRef.current = athenaDiagnostics.span(\n `statewire.attach:${threadId}`,\n 'statewire.attach.start',\n { thread_id: threadId, is_new_chat: isNewChat },\n );\n }\n\n // Built every render so its JSON signature can key the model-context\n // registration below: consumers commonly pass fresh array literals per\n // render, which must not thrash registration, while a changed value (a new\n // `model`, channel, tool list, system prompt, …) must re-sync the context\n // before the next send. The core pulls this value only on model-context\n // notifications, which a key change triggers.\n const runConfigValue = buildStatewireRunConfig({\n model,\n agent,\n channel,\n tools,\n frontendToolIds,\n workbench,\n knowledgeBase,\n systemPrompt,\n appId,\n extraRunConfig,\n clientTools: statewireClientToolWireEntries(clientTools),\n });\n const runConfigKey = JSON.stringify(runConfigValue);\n\n // The host's state, told apart from what the thread renders before its\n // hello: statewire publishes the initial (placeholder or remembered) state\n // through `onStateChange` too, and reports `connected` in the same flush\n // right after publishing the state the hello carried. So the latest state\n // seen when the connection first turns `connected` is the hello's, and every\n // state after it is the host's. Refs are per attach: the runtime is mounted\n // per thread.\n const lastStateRef = useRef<DeepAgentStreamValues | null>(null);\n const helloLandedRef = useRef(false);\n\n const auiTools = useMemo(() => Tools({ toolkit: frontendToolkit }), [frontendToolkit]);\n // Web client for the shared runtime core (a `use*` name so the\n // rules-of-hooks lint sees the `useAui` call inside a custom hook; the core\n // invokes it unconditionally every render).\n const useSdkClient = (thread: DeepAgentThreadElement) => useAui({ thread, tools: auiTools });\n\n return useDeepAgentRuntime({\n surface: 'AthenaSDK',\n threadId,\n baseUrl: syncUrl,\n // Option closures are re-read per render by the core, so credential\n // rotation (new `token`/`getToken`/`apiKey` props) reaches the next\n // request without remounting the thread. The transport's `staleAuth`\n // (true right after a refused credential) is forwarded as `force`.\n headers: (ctx) => resolveStatewireAuthHeaders({ apiKey, token, getToken }, ctx),\n isNewChat,\n // Queued entries render in the SDK's queue panel, so only the steer lane\n // projects into the message tail.\n laneProjection: 'steer-only',\n // Durable session: a reload resumes the lane and resends whatever the\n // server has not marked durable. Scoped by host AND principal — a thread\n // id is only meaningful against the backend that minted it, and a stored\n // lane must never resend against another signed-in user. (Consumers\n // server-render this SDK; the core's availability probe keeps those\n // sessions process-local, as before the seam existed.)\n session: {\n kind: 'local-storage',\n key: (id) => statewireSessionKey({ syncUrl, principal, threadId: id }),\n },\n snapshotCache,\n // Head interest (see `ATHENA_SDK_MESSAGE_WINDOW`): the tail of the\n // transcript instead of every message; the chat UI's history edge pages\n // older ranges in and fetches cut tool outputs into `toolOutputs`.\n messageWindow,\n toolOutputs,\n capabilities: { edit: true, reload: true, continue: true },\n // The composer stages the draft each send came from right before it\n // sends; the message carries it as its composer markdown, so its bubble\n // shows the text exactly as typed (`UserMessageText`).\n getMessageMetadata: withStagedComposerMarkdown({ threadId }),\n useClient: useSdkClient,\n onStateChange: (state) => {\n // First snapshot for this attach ends the attach span (time-to-history).\n const endSpan = attachSpanRef.current;\n if (endSpan) {\n attachSpanRef.current = null;\n endSpan('statewire.snapshot', { data: { thread_id: threadId } });\n }\n // A replay boundary republishes the pre-stream default: not host state.\n if (!isDeepAgentStateHydrated(state)) return;\n lastStateRef.current = state;\n if (helloLandedRef.current) onServerState?.(state);\n },\n onConnectionChange: (connection) => {\n athenaDiagnostics.gauge('statewire.connection', connection?.status ?? null);\n if (connection?.status === 'stopped') {\n athenaDiagnostics.emit('statewire.connection', {\n level: 'warn',\n data: {\n status: connection.status,\n reason: connection.reason,\n message: connection.message,\n },\n });\n }\n onConnectionChange?.(connection);\n },\n onRawConnectionChange: (connection) => {\n if (connection.status === 'connected' && !helloLandedRef.current) {\n helloLandedRef.current = true;\n const helloState = lastStateRef.current;\n if (helloState) onServerState?.(helloState);\n }\n onRawConnectionChange?.(connection);\n },\n onRunningChange,\n // Each model-context notification pulls the latest getter through the\n // core's per-render ref; the value-stable key re-registers (and so\n // notifies) only when the built runConfig actually changed. On a thread\n // with a server config lane, agora keeps only the per-send keys of what\n // this carries; `StatewireModelLaneSync` writes a changed `model` to the\n // lane itself.\n runConfig: {\n get: () => runConfigValue,\n key: runConfigKey,\n },\n onError: (error) => {\n if (!isStatewireChannelTeardown(error)) {\n athenaDiagnostics.error(\n 'statewire.error',\n error,\n {\n code: 'statewire_rejected',\n message: 'The statewire host rejected a command',\n hint: 'Inspect error.detail — agora rejections carry a structured {code, message}.',\n },\n { thread_id: threadId },\n );\n }\n if (onError) {\n onError(error);\n return;\n }\n logUnhandledStatewireError('AthenaSDK', error);\n },\n });\n}\n"],"names":["DEEP_AGENT_DEFAULT_MESSAGE_WINDOW","useMemo","collectStatewireClientTools","resolveAgentProp","useContext","AthenaAuthContext","useRef","athenaDiagnostics","buildStatewireRunConfig","statewireClientToolWireEntries","Tools","useAui","useDeepAgentRuntime","resolveStatewireAuthHeaders","statewireSessionKey","withStagedComposerMarkdown","isDeepAgentStateHydrated","isStatewireChannelTeardown","logUnhandledStatewireError"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useAthenaStatewireRuntime.cjs","sources":["../../src/runtime/useAthenaStatewireRuntime.ts"],"sourcesContent":["import type { AssistantClient, Toolkit } from '@assistant-ui/react';\nimport { Tools, useAui } from '@assistant-ui/react';\nimport {\n DEEP_AGENT_DEFAULT_MESSAGE_WINDOW,\n type DeepAgentMessageWindowDeclaration,\n type DeepAgentStreamValues,\n type DeepAgentThreadElement,\n type DeepAgentThreadSnapshotCache,\n type DeepAgentToolOutputStore,\n isDeepAgentStateHydrated,\n isStatewireChannelTeardown,\n logUnhandledStatewireError,\n type RegisteredClientTool,\n useDeepAgentRuntime,\n type UseDeepAgentThreadOptions,\n withStagedComposerMarkdown,\n} from '@athenaintel/agent-runtime';\nimport { useContext, useMemo, useRef } from 'react';\nimport { AthenaAuthContext } from '../auth/AthenaAuthContext';\nimport {\n collectStatewireClientTools,\n statewireClientToolWireEntries,\n} from '../chat/statewire-client-tools';\nimport { athenaDiagnostics } from '../diagnostics/athena-diagnostics';\nimport {\n type AthenaStatewireTokenGetter,\n resolveStatewireAuthHeaders,\n} from './statewire-auth-headers';\nimport { statewireSessionKey } from './statewire-session-key';\n\ntype AthenaDiagnosticsSpanStarter = typeof athenaDiagnostics.span;\nimport {\n buildStatewireRunConfig,\n DEFAULT_STATEWIRE_MODEL,\n resolveAgentProp,\n} from './statewire-run-config';\n\nexport { DEFAULT_STATEWIRE_MODEL };\n\n/**\n * The head interest every SDK attach states (`athena/interest`): the host\n * mounts the transcript's tail as message documents instead of every message\n * — megabytes on long threads, over the 1 MiB frame limit of stock websocket\n * clients. Older pages load from the `StatewireHistoryEdge` row the chat UI\n * mounts above the messages, and cut tool outputs are fetched back for the\n * cards that show them.\n */\nexport const ATHENA_SDK_MESSAGE_WINDOW: DeepAgentMessageWindowDeclaration = {\n size: DEEP_AGENT_DEFAULT_MESSAGE_WINDOW,\n};\n\nexport interface AthenaStatewireRuntimeConfig {\n /** Base URL of the statewire sync host (the `…/v2/threads` mount). */\n syncUrl: string;\n /** Thread ID the statewire session attaches to. */\n threadId: string;\n /** True only for a never-started local chat. Existing (or pre-assigned)\n * threads load until their server snapshot arrives. */\n isNewChat?: boolean;\n /** API key for authentication. Used when no auth token is provided. */\n apiKey?: string;\n /** Auth token. Takes priority over apiKey. */\n token?: string | null;\n /** Resolve the latest token immediately before a request (for expiring\n * clients). Called with `{ force: true }` right after the host refused the\n * previous credential (the transport's `staleAuth`), so a caching getter\n * mints a fresh token instead of returning the refused one. */\n getToken?: AthenaStatewireTokenGetter;\n /**\n * Per-provider LRU of each thread's last wire state. A switch back to a\n * remembered thread mounts with that snapshot and projects ready on the\n * first frame instead of a loading treatment; the attach snapshot then\n * replaces it. Owned above the per-thread mount so it survives switches.\n */\n snapshotCache?: DeepAgentThreadSnapshotCache;\n /** The head interest stated on the attach (see {@link ATHENA_SDK_MESSAGE_WINDOW}, the default). */\n messageWindow?: DeepAgentMessageWindowDeclaration;\n /**\n * Full tool outputs fetched back for a windowed wire's cut tool messages;\n * the thread re-converts when one lands. `AthenaProvider` owns one per\n * provider and hands the same store to `StatewireHistoryEdge`.\n */\n toolOutputs?: DeepAgentToolOutputStore;\n /** LLM model identifier. Defaults to the deep-agent default model. */\n model?: string;\n /** Agent selector: only `collab_agent:<asset_id>` refs are accepted (the run\n * executes AS that published collab agent). Any other non-empty value\n * throws — see `resolveAgentProp`. */\n agent?: string;\n /** Collab-agent channel id selecting that channel's overrides. */\n channel?: string;\n /** Backend toolkit IDs to enable. */\n tools?: string[];\n /** Frontend tool IDs to include in enabled_tools. */\n frontendToolIds?: string[];\n /** Frontend tools registered on the assistant client. Tools with a local\n * `execute` are also declared as `client_tools` and executed through the\n * statewire client-tool bridge. */\n frontendToolkit?: Toolkit;\n /** Asset IDs to include in the workbench. */\n workbench?: string[];\n /** Knowledge base IDs for RAG context. */\n knowledgeBase?: string[];\n /** System prompt override. */\n systemPrompt?: string;\n /** Client app identifier for session filtering. Sent as app_id in runConfig.custom. */\n appId?: string;\n /** Additional fields merged into runConfig.custom. */\n extraRunConfig?: Record<string, unknown>;\n /** Called on statewire transport errors (send rejections, stream failures). */\n onError?: (error: Error) => void;\n /** Projected connection status changes (deduped; null on unmount). */\n onConnectionChange?: UseDeepAgentThreadOptions['onConnectionChange'];\n /** Raw connection observer — carries the transport's reconnect handle. */\n onRawConnectionChange?: UseDeepAgentThreadOptions['onRawConnectionChange'];\n /** Notified when a run starts/stops. */\n onRunningChange?: UseDeepAgentThreadOptions['onRunningChange'];\n /**\n * Observes the host's own state: called once the attach's hello has landed\n * (with the snapshot it carried) and then on every later frame — never with\n * the placeholder or remembered snapshot a thread renders before its hello.\n * Called per streamed token, so keep it cheap.\n */\n onServerState?: (state: DeepAgentStreamValues) => void;\n}\n\nconst EMPTY_TOOLKIT: Toolkit = {};\nconst EMPTY_TOOL_IDS: string[] = [];\n\n/** The client tools derived from the runtime's frontend toolkit — exposed so\n * the provider can mount the client-tool bridge with the same set. */\nexport function useStatewireClientTools(\n frontendToolkit: Toolkit | undefined,\n): RegisteredClientTool[] {\n return useMemo(\n () => collectStatewireClientTools(frontendToolkit ?? EMPTY_TOOLKIT),\n [frontendToolkit],\n );\n}\n\n/**\n * Statewire runtime for the Athena SDK — a thin shim over the shared\n * `@athenaintel/agent-runtime` `useDeepAgentRuntime`: it attaches to the\n * agora-hosted deep-agent thread at `${syncUrl}/${threadId}` and returns the\n * `AssistantClient` to mount via `AuiProvider` — the same integration shape as\n * the Olympus, Chrome extension, Office, and mobile statewire hosts.\n *\n * Auth: a token (resolved live through `getToken` when provided) rides\n * `Authorization: Bearer`, and an API key falls back to `X-API-KEY` — both\n * accepted by the statewire host.\n */\nexport function useAthenaStatewireRuntime(\n config: AthenaStatewireRuntimeConfig,\n): AssistantClient {\n const {\n syncUrl,\n threadId,\n isNewChat = false,\n apiKey,\n token,\n getToken,\n model,\n agent,\n channel,\n tools = EMPTY_TOOL_IDS,\n frontendToolIds = EMPTY_TOOL_IDS,\n frontendToolkit = EMPTY_TOOLKIT,\n workbench,\n knowledgeBase,\n systemPrompt,\n appId,\n extraRunConfig,\n snapshotCache,\n messageWindow = ATHENA_SDK_MESSAGE_WINDOW,\n toolOutputs,\n onError,\n onConnectionChange,\n onRawConnectionChange,\n onRunningChange,\n onServerState,\n } = config;\n\n // Fail loud at mount, not at first send: the statewire host always runs the\n // Athena deep agent, so an `agent` outside the collab_agent namespace can\n // never apply. Throwing here (render time) is what makes a misconfigured\n // integration visible instead of quietly answering as the wrong agent.\n resolveAgentProp({ agent });\n\n const clientTools = useStatewireClientTools(frontendToolkit);\n\n // The persisted command lane is keyed by WHO is signed in as well as by host\n // and thread (see `statewireSessionKey`).\n const authContext = useContext(AthenaAuthContext);\n const principal = authContext?.user?.userId ?? '';\n\n // Attach → first server snapshot span, keyed per thread attach.\n const attachSpanRef = useRef<ReturnType<AthenaDiagnosticsSpanStarter> | null>(null);\n const attachThreadRef = useRef<string | null>(null);\n if (attachThreadRef.current !== threadId) {\n attachThreadRef.current = threadId;\n attachSpanRef.current = athenaDiagnostics.span(\n `statewire.attach:${threadId}`,\n 'statewire.attach.start',\n { thread_id: threadId, is_new_chat: isNewChat },\n );\n }\n\n // Built every render so its JSON signature can key the model-context\n // registration below: consumers commonly pass fresh array literals per\n // render, which must not thrash registration, while a changed value (a new\n // `model`, channel, tool list, system prompt, …) must re-sync the context\n // before the next send. The core pulls this value only on model-context\n // notifications, which a key change triggers.\n const runConfigValue = buildStatewireRunConfig({\n model,\n agent,\n channel,\n tools,\n frontendToolIds,\n workbench,\n knowledgeBase,\n systemPrompt,\n appId,\n extraRunConfig,\n clientTools: statewireClientToolWireEntries(clientTools),\n });\n const runConfigKey = JSON.stringify(runConfigValue);\n\n // The host's state, told apart from what the thread renders before its\n // hello: statewire publishes the initial (placeholder or remembered) state\n // through `onStateChange` too, and reports `connected` in the same flush\n // right after publishing the state the hello carried. So the latest state\n // seen when the connection first turns `connected` is the hello's, and every\n // state after it is the host's. Refs are per attach: the runtime is mounted\n // per thread.\n const lastStateRef = useRef<DeepAgentStreamValues | null>(null);\n const helloLandedRef = useRef(false);\n\n const auiTools = useMemo(() => Tools({ toolkit: frontendToolkit }), [frontendToolkit]);\n // Web client for the shared runtime core (a `use*` name so the\n // rules-of-hooks lint sees the `useAui` call inside a custom hook; the core\n // invokes it unconditionally every render).\n const useSdkClient = (thread: DeepAgentThreadElement) => useAui({ thread, tools: auiTools });\n\n return useDeepAgentRuntime({\n surface: 'AthenaSDK',\n threadId,\n baseUrl: syncUrl,\n // Option closures are re-read per render by the core, so credential\n // rotation (new `token`/`getToken`/`apiKey` props) reaches the next\n // request without remounting the thread. The transport's `staleAuth`\n // (true right after a refused credential) is forwarded as `force`.\n headers: (ctx) => resolveStatewireAuthHeaders({ apiKey, token, getToken }, ctx),\n isNewChat,\n // Queued entries render in the SDK's queue panel, so only the steer lane\n // projects into the message tail.\n laneProjection: 'steer-only',\n // Durable session: a reload resumes the lane and resends whatever the\n // server has not marked durable. Scoped by host AND principal — a thread\n // id is only meaningful against the backend that minted it, and a stored\n // lane must never resend against another signed-in user. (Consumers\n // server-render this SDK; the core's availability probe keeps those\n // sessions process-local, as before the seam existed.)\n session: {\n kind: 'local-storage',\n key: (id) => statewireSessionKey({ syncUrl, principal, threadId: id }),\n },\n snapshotCache,\n // Head interest (see `ATHENA_SDK_MESSAGE_WINDOW`): the tail of the\n // transcript instead of every message; the chat UI's history edge pages\n // older ranges in and fetches cut tool outputs into `toolOutputs`.\n messageWindow,\n toolOutputs,\n capabilities: { edit: true, reload: true, continue: true },\n // The composer stages the draft each send came from right before it\n // sends; the message carries it as its composer markdown, so its bubble\n // shows the text exactly as typed (`UserMessageText`).\n getMessageMetadata: withStagedComposerMarkdown({ threadId }),\n useClient: useSdkClient,\n onStateChange: (state) => {\n // First snapshot for this attach ends the attach span (time-to-history).\n const endSpan = attachSpanRef.current;\n if (endSpan) {\n attachSpanRef.current = null;\n endSpan('statewire.snapshot', { data: { thread_id: threadId } });\n }\n // A replay boundary republishes the pre-stream default: not host state.\n if (!isDeepAgentStateHydrated(state)) return;\n lastStateRef.current = state;\n if (helloLandedRef.current) onServerState?.(state);\n },\n onConnectionChange: (connection) => {\n athenaDiagnostics.gauge('statewire.connection', connection?.status ?? null);\n if (connection?.status === 'stopped') {\n athenaDiagnostics.emit('statewire.connection', {\n level: 'warn',\n data: {\n status: connection.status,\n reason: connection.reason,\n message: connection.message,\n },\n });\n }\n onConnectionChange?.(connection);\n },\n onRawConnectionChange: (connection) => {\n if (connection.status === 'connected' && !helloLandedRef.current) {\n helloLandedRef.current = true;\n const helloState = lastStateRef.current;\n if (helloState) onServerState?.(helloState);\n }\n onRawConnectionChange?.(connection);\n },\n onRunningChange,\n // Each model-context notification pulls the latest getter through the\n // core's per-render ref; the value-stable key re-registers (and so\n // notifies) only when the built runConfig actually changed. On a thread\n // with a server config lane, agora keeps only the per-send keys of what\n // this carries; `StatewireThreadConfigLaneSync` writes a changed model,\n // system prompt, tool list, workbench or knowledge base to the lane itself.\n runConfig: {\n get: () => runConfigValue,\n key: runConfigKey,\n },\n onError: (error) => {\n if (!isStatewireChannelTeardown(error)) {\n athenaDiagnostics.error(\n 'statewire.error',\n error,\n {\n code: 'statewire_rejected',\n message: 'The statewire host rejected a command',\n hint: 'Inspect error.detail — agora rejections carry a structured {code, message}.',\n },\n { thread_id: threadId },\n );\n }\n if (onError) {\n onError(error);\n return;\n }\n logUnhandledStatewireError('AthenaSDK', error);\n },\n });\n}\n"],"names":["DEEP_AGENT_DEFAULT_MESSAGE_WINDOW","useMemo","collectStatewireClientTools","resolveAgentProp","useContext","AthenaAuthContext","useRef","athenaDiagnostics","buildStatewireRunConfig","statewireClientToolWireEntries","Tools","useAui","useDeepAgentRuntime","resolveStatewireAuthHeaders","statewireSessionKey","withStagedComposerMarkdown","isDeepAgentStateHydrated","isStatewireChannelTeardown","logUnhandledStatewireError"],"mappings":"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"}
|
|
@@ -156,8 +156,8 @@ function useAthenaStatewireRuntime(config) {
|
|
|
156
156
|
// core's per-render ref; the value-stable key re-registers (and so
|
|
157
157
|
// notifies) only when the built runConfig actually changed. On a thread
|
|
158
158
|
// with a server config lane, agora keeps only the per-send keys of what
|
|
159
|
-
// this carries; `
|
|
160
|
-
// lane itself.
|
|
159
|
+
// this carries; `StatewireThreadConfigLaneSync` writes a changed model,
|
|
160
|
+
// system prompt, tool list, workbench or knowledge base to the lane itself.
|
|
161
161
|
runConfig: {
|
|
162
162
|
get: () => runConfigValue,
|
|
163
163
|
key: runConfigKey
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAthenaStatewireRuntime.js","sources":["../../src/runtime/useAthenaStatewireRuntime.ts"],"sourcesContent":["import type { AssistantClient, Toolkit } from '@assistant-ui/react';\nimport { Tools, useAui } from '@assistant-ui/react';\nimport {\n DEEP_AGENT_DEFAULT_MESSAGE_WINDOW,\n type DeepAgentMessageWindowDeclaration,\n type DeepAgentStreamValues,\n type DeepAgentThreadElement,\n type DeepAgentThreadSnapshotCache,\n type DeepAgentToolOutputStore,\n isDeepAgentStateHydrated,\n isStatewireChannelTeardown,\n logUnhandledStatewireError,\n type RegisteredClientTool,\n useDeepAgentRuntime,\n type UseDeepAgentThreadOptions,\n withStagedComposerMarkdown,\n} from '@athenaintel/agent-runtime';\nimport { useContext, useMemo, useRef } from 'react';\nimport { AthenaAuthContext } from '../auth/AthenaAuthContext';\nimport {\n collectStatewireClientTools,\n statewireClientToolWireEntries,\n} from '../chat/statewire-client-tools';\nimport { athenaDiagnostics } from '../diagnostics/athena-diagnostics';\nimport {\n type AthenaStatewireTokenGetter,\n resolveStatewireAuthHeaders,\n} from './statewire-auth-headers';\nimport { statewireSessionKey } from './statewire-session-key';\n\ntype AthenaDiagnosticsSpanStarter = typeof athenaDiagnostics.span;\nimport {\n buildStatewireRunConfig,\n DEFAULT_STATEWIRE_MODEL,\n resolveAgentProp,\n} from './statewire-run-config';\n\nexport { DEFAULT_STATEWIRE_MODEL };\n\n/**\n * The head interest every SDK attach states (`athena/interest`): the host\n * mounts the transcript's tail as message documents instead of every message\n * — megabytes on long threads, over the 1 MiB frame limit of stock websocket\n * clients. Older pages load from the `StatewireHistoryEdge` row the chat UI\n * mounts above the messages, and cut tool outputs are fetched back for the\n * cards that show them.\n */\nexport const ATHENA_SDK_MESSAGE_WINDOW: DeepAgentMessageWindowDeclaration = {\n size: DEEP_AGENT_DEFAULT_MESSAGE_WINDOW,\n};\n\nexport interface AthenaStatewireRuntimeConfig {\n /** Base URL of the statewire sync host (the `…/v2/threads` mount). */\n syncUrl: string;\n /** Thread ID the statewire session attaches to. */\n threadId: string;\n /** True only for a never-started local chat. Existing (or pre-assigned)\n * threads load until their server snapshot arrives. */\n isNewChat?: boolean;\n /** API key for authentication. Used when no auth token is provided. */\n apiKey?: string;\n /** Auth token. Takes priority over apiKey. */\n token?: string | null;\n /** Resolve the latest token immediately before a request (for expiring\n * clients). Called with `{ force: true }` right after the host refused the\n * previous credential (the transport's `staleAuth`), so a caching getter\n * mints a fresh token instead of returning the refused one. */\n getToken?: AthenaStatewireTokenGetter;\n /**\n * Per-provider LRU of each thread's last wire state. A switch back to a\n * remembered thread mounts with that snapshot and projects ready on the\n * first frame instead of a loading treatment; the attach snapshot then\n * replaces it. Owned above the per-thread mount so it survives switches.\n */\n snapshotCache?: DeepAgentThreadSnapshotCache;\n /** The head interest stated on the attach (see {@link ATHENA_SDK_MESSAGE_WINDOW}, the default). */\n messageWindow?: DeepAgentMessageWindowDeclaration;\n /**\n * Full tool outputs fetched back for a windowed wire's cut tool messages;\n * the thread re-converts when one lands. `AthenaProvider` owns one per\n * provider and hands the same store to `StatewireHistoryEdge`.\n */\n toolOutputs?: DeepAgentToolOutputStore;\n /** LLM model identifier. Defaults to the deep-agent default model. */\n model?: string;\n /** Agent selector: only `collab_agent:<asset_id>` refs are accepted (the run\n * executes AS that published collab agent). Any other non-empty value\n * throws — see `resolveAgentProp`. */\n agent?: string;\n /** Collab-agent channel id selecting that channel's overrides. */\n channel?: string;\n /** Backend toolkit IDs to enable. */\n tools?: string[];\n /** Frontend tool IDs to include in enabled_tools. */\n frontendToolIds?: string[];\n /** Frontend tools registered on the assistant client. Tools with a local\n * `execute` are also declared as `client_tools` and executed through the\n * statewire client-tool bridge. */\n frontendToolkit?: Toolkit;\n /** Asset IDs to include in the workbench. */\n workbench?: string[];\n /** Knowledge base IDs for RAG context. */\n knowledgeBase?: string[];\n /** System prompt override. */\n systemPrompt?: string;\n /** Client app identifier for session filtering. Sent as app_id in runConfig.custom. */\n appId?: string;\n /** Additional fields merged into runConfig.custom. */\n extraRunConfig?: Record<string, unknown>;\n /** Called on statewire transport errors (send rejections, stream failures). */\n onError?: (error: Error) => void;\n /** Projected connection status changes (deduped; null on unmount). */\n onConnectionChange?: UseDeepAgentThreadOptions['onConnectionChange'];\n /** Raw connection observer — carries the transport's reconnect handle. */\n onRawConnectionChange?: UseDeepAgentThreadOptions['onRawConnectionChange'];\n /** Notified when a run starts/stops. */\n onRunningChange?: UseDeepAgentThreadOptions['onRunningChange'];\n /**\n * Observes the host's own state: called once the attach's hello has landed\n * (with the snapshot it carried) and then on every later frame — never with\n * the placeholder or remembered snapshot a thread renders before its hello.\n * Called per streamed token, so keep it cheap.\n */\n onServerState?: (state: DeepAgentStreamValues) => void;\n}\n\nconst EMPTY_TOOLKIT: Toolkit = {};\nconst EMPTY_TOOL_IDS: string[] = [];\n\n/** The client tools derived from the runtime's frontend toolkit — exposed so\n * the provider can mount the client-tool bridge with the same set. */\nexport function useStatewireClientTools(\n frontendToolkit: Toolkit | undefined,\n): RegisteredClientTool[] {\n return useMemo(\n () => collectStatewireClientTools(frontendToolkit ?? EMPTY_TOOLKIT),\n [frontendToolkit],\n );\n}\n\n/**\n * Statewire runtime for the Athena SDK — a thin shim over the shared\n * `@athenaintel/agent-runtime` `useDeepAgentRuntime`: it attaches to the\n * agora-hosted deep-agent thread at `${syncUrl}/${threadId}` and returns the\n * `AssistantClient` to mount via `AuiProvider` — the same integration shape as\n * the Olympus, Chrome extension, Office, and mobile statewire hosts.\n *\n * Auth: a token (resolved live through `getToken` when provided) rides\n * `Authorization: Bearer`, and an API key falls back to `X-API-KEY` — both\n * accepted by the statewire host.\n */\nexport function useAthenaStatewireRuntime(\n config: AthenaStatewireRuntimeConfig,\n): AssistantClient {\n const {\n syncUrl,\n threadId,\n isNewChat = false,\n apiKey,\n token,\n getToken,\n model,\n agent,\n channel,\n tools = EMPTY_TOOL_IDS,\n frontendToolIds = EMPTY_TOOL_IDS,\n frontendToolkit = EMPTY_TOOLKIT,\n workbench,\n knowledgeBase,\n systemPrompt,\n appId,\n extraRunConfig,\n snapshotCache,\n messageWindow = ATHENA_SDK_MESSAGE_WINDOW,\n toolOutputs,\n onError,\n onConnectionChange,\n onRawConnectionChange,\n onRunningChange,\n onServerState,\n } = config;\n\n // Fail loud at mount, not at first send: the statewire host always runs the\n // Athena deep agent, so an `agent` outside the collab_agent namespace can\n // never apply. Throwing here (render time) is what makes a misconfigured\n // integration visible instead of quietly answering as the wrong agent.\n resolveAgentProp({ agent });\n\n const clientTools = useStatewireClientTools(frontendToolkit);\n\n // The persisted command lane is keyed by WHO is signed in as well as by host\n // and thread (see `statewireSessionKey`).\n const authContext = useContext(AthenaAuthContext);\n const principal = authContext?.user?.userId ?? '';\n\n // Attach → first server snapshot span, keyed per thread attach.\n const attachSpanRef = useRef<ReturnType<AthenaDiagnosticsSpanStarter> | null>(null);\n const attachThreadRef = useRef<string | null>(null);\n if (attachThreadRef.current !== threadId) {\n attachThreadRef.current = threadId;\n attachSpanRef.current = athenaDiagnostics.span(\n `statewire.attach:${threadId}`,\n 'statewire.attach.start',\n { thread_id: threadId, is_new_chat: isNewChat },\n );\n }\n\n // Built every render so its JSON signature can key the model-context\n // registration below: consumers commonly pass fresh array literals per\n // render, which must not thrash registration, while a changed value (a new\n // `model`, channel, tool list, system prompt, …) must re-sync the context\n // before the next send. The core pulls this value only on model-context\n // notifications, which a key change triggers.\n const runConfigValue = buildStatewireRunConfig({\n model,\n agent,\n channel,\n tools,\n frontendToolIds,\n workbench,\n knowledgeBase,\n systemPrompt,\n appId,\n extraRunConfig,\n clientTools: statewireClientToolWireEntries(clientTools),\n });\n const runConfigKey = JSON.stringify(runConfigValue);\n\n // The host's state, told apart from what the thread renders before its\n // hello: statewire publishes the initial (placeholder or remembered) state\n // through `onStateChange` too, and reports `connected` in the same flush\n // right after publishing the state the hello carried. So the latest state\n // seen when the connection first turns `connected` is the hello's, and every\n // state after it is the host's. Refs are per attach: the runtime is mounted\n // per thread.\n const lastStateRef = useRef<DeepAgentStreamValues | null>(null);\n const helloLandedRef = useRef(false);\n\n const auiTools = useMemo(() => Tools({ toolkit: frontendToolkit }), [frontendToolkit]);\n // Web client for the shared runtime core (a `use*` name so the\n // rules-of-hooks lint sees the `useAui` call inside a custom hook; the core\n // invokes it unconditionally every render).\n const useSdkClient = (thread: DeepAgentThreadElement) => useAui({ thread, tools: auiTools });\n\n return useDeepAgentRuntime({\n surface: 'AthenaSDK',\n threadId,\n baseUrl: syncUrl,\n // Option closures are re-read per render by the core, so credential\n // rotation (new `token`/`getToken`/`apiKey` props) reaches the next\n // request without remounting the thread. The transport's `staleAuth`\n // (true right after a refused credential) is forwarded as `force`.\n headers: (ctx) => resolveStatewireAuthHeaders({ apiKey, token, getToken }, ctx),\n isNewChat,\n // Queued entries render in the SDK's queue panel, so only the steer lane\n // projects into the message tail.\n laneProjection: 'steer-only',\n // Durable session: a reload resumes the lane and resends whatever the\n // server has not marked durable. Scoped by host AND principal — a thread\n // id is only meaningful against the backend that minted it, and a stored\n // lane must never resend against another signed-in user. (Consumers\n // server-render this SDK; the core's availability probe keeps those\n // sessions process-local, as before the seam existed.)\n session: {\n kind: 'local-storage',\n key: (id) => statewireSessionKey({ syncUrl, principal, threadId: id }),\n },\n snapshotCache,\n // Head interest (see `ATHENA_SDK_MESSAGE_WINDOW`): the tail of the\n // transcript instead of every message; the chat UI's history edge pages\n // older ranges in and fetches cut tool outputs into `toolOutputs`.\n messageWindow,\n toolOutputs,\n capabilities: { edit: true, reload: true, continue: true },\n // The composer stages the draft each send came from right before it\n // sends; the message carries it as its composer markdown, so its bubble\n // shows the text exactly as typed (`UserMessageText`).\n getMessageMetadata: withStagedComposerMarkdown({ threadId }),\n useClient: useSdkClient,\n onStateChange: (state) => {\n // First snapshot for this attach ends the attach span (time-to-history).\n const endSpan = attachSpanRef.current;\n if (endSpan) {\n attachSpanRef.current = null;\n endSpan('statewire.snapshot', { data: { thread_id: threadId } });\n }\n // A replay boundary republishes the pre-stream default: not host state.\n if (!isDeepAgentStateHydrated(state)) return;\n lastStateRef.current = state;\n if (helloLandedRef.current) onServerState?.(state);\n },\n onConnectionChange: (connection) => {\n athenaDiagnostics.gauge('statewire.connection', connection?.status ?? null);\n if (connection?.status === 'stopped') {\n athenaDiagnostics.emit('statewire.connection', {\n level: 'warn',\n data: {\n status: connection.status,\n reason: connection.reason,\n message: connection.message,\n },\n });\n }\n onConnectionChange?.(connection);\n },\n onRawConnectionChange: (connection) => {\n if (connection.status === 'connected' && !helloLandedRef.current) {\n helloLandedRef.current = true;\n const helloState = lastStateRef.current;\n if (helloState) onServerState?.(helloState);\n }\n onRawConnectionChange?.(connection);\n },\n onRunningChange,\n // Each model-context notification pulls the latest getter through the\n // core's per-render ref; the value-stable key re-registers (and so\n // notifies) only when the built runConfig actually changed. On a thread\n // with a server config lane, agora keeps only the per-send keys of what\n // this carries; `StatewireModelLaneSync` writes a changed `model` to the\n // lane itself.\n runConfig: {\n get: () => runConfigValue,\n key: runConfigKey,\n },\n onError: (error) => {\n if (!isStatewireChannelTeardown(error)) {\n athenaDiagnostics.error(\n 'statewire.error',\n error,\n {\n code: 'statewire_rejected',\n message: 'The statewire host rejected a command',\n hint: 'Inspect error.detail — agora rejections carry a structured {code, message}.',\n },\n { thread_id: threadId },\n );\n }\n if (onError) {\n onError(error);\n return;\n }\n logUnhandledStatewireError('AthenaSDK', error);\n },\n });\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useAthenaStatewireRuntime.js","sources":["../../src/runtime/useAthenaStatewireRuntime.ts"],"sourcesContent":["import type { AssistantClient, Toolkit } from '@assistant-ui/react';\nimport { Tools, useAui } from '@assistant-ui/react';\nimport {\n DEEP_AGENT_DEFAULT_MESSAGE_WINDOW,\n type DeepAgentMessageWindowDeclaration,\n type DeepAgentStreamValues,\n type DeepAgentThreadElement,\n type DeepAgentThreadSnapshotCache,\n type DeepAgentToolOutputStore,\n isDeepAgentStateHydrated,\n isStatewireChannelTeardown,\n logUnhandledStatewireError,\n type RegisteredClientTool,\n useDeepAgentRuntime,\n type UseDeepAgentThreadOptions,\n withStagedComposerMarkdown,\n} from '@athenaintel/agent-runtime';\nimport { useContext, useMemo, useRef } from 'react';\nimport { AthenaAuthContext } from '../auth/AthenaAuthContext';\nimport {\n collectStatewireClientTools,\n statewireClientToolWireEntries,\n} from '../chat/statewire-client-tools';\nimport { athenaDiagnostics } from '../diagnostics/athena-diagnostics';\nimport {\n type AthenaStatewireTokenGetter,\n resolveStatewireAuthHeaders,\n} from './statewire-auth-headers';\nimport { statewireSessionKey } from './statewire-session-key';\n\ntype AthenaDiagnosticsSpanStarter = typeof athenaDiagnostics.span;\nimport {\n buildStatewireRunConfig,\n DEFAULT_STATEWIRE_MODEL,\n resolveAgentProp,\n} from './statewire-run-config';\n\nexport { DEFAULT_STATEWIRE_MODEL };\n\n/**\n * The head interest every SDK attach states (`athena/interest`): the host\n * mounts the transcript's tail as message documents instead of every message\n * — megabytes on long threads, over the 1 MiB frame limit of stock websocket\n * clients. Older pages load from the `StatewireHistoryEdge` row the chat UI\n * mounts above the messages, and cut tool outputs are fetched back for the\n * cards that show them.\n */\nexport const ATHENA_SDK_MESSAGE_WINDOW: DeepAgentMessageWindowDeclaration = {\n size: DEEP_AGENT_DEFAULT_MESSAGE_WINDOW,\n};\n\nexport interface AthenaStatewireRuntimeConfig {\n /** Base URL of the statewire sync host (the `…/v2/threads` mount). */\n syncUrl: string;\n /** Thread ID the statewire session attaches to. */\n threadId: string;\n /** True only for a never-started local chat. Existing (or pre-assigned)\n * threads load until their server snapshot arrives. */\n isNewChat?: boolean;\n /** API key for authentication. Used when no auth token is provided. */\n apiKey?: string;\n /** Auth token. Takes priority over apiKey. */\n token?: string | null;\n /** Resolve the latest token immediately before a request (for expiring\n * clients). Called with `{ force: true }` right after the host refused the\n * previous credential (the transport's `staleAuth`), so a caching getter\n * mints a fresh token instead of returning the refused one. */\n getToken?: AthenaStatewireTokenGetter;\n /**\n * Per-provider LRU of each thread's last wire state. A switch back to a\n * remembered thread mounts with that snapshot and projects ready on the\n * first frame instead of a loading treatment; the attach snapshot then\n * replaces it. Owned above the per-thread mount so it survives switches.\n */\n snapshotCache?: DeepAgentThreadSnapshotCache;\n /** The head interest stated on the attach (see {@link ATHENA_SDK_MESSAGE_WINDOW}, the default). */\n messageWindow?: DeepAgentMessageWindowDeclaration;\n /**\n * Full tool outputs fetched back for a windowed wire's cut tool messages;\n * the thread re-converts when one lands. `AthenaProvider` owns one per\n * provider and hands the same store to `StatewireHistoryEdge`.\n */\n toolOutputs?: DeepAgentToolOutputStore;\n /** LLM model identifier. Defaults to the deep-agent default model. */\n model?: string;\n /** Agent selector: only `collab_agent:<asset_id>` refs are accepted (the run\n * executes AS that published collab agent). Any other non-empty value\n * throws — see `resolveAgentProp`. */\n agent?: string;\n /** Collab-agent channel id selecting that channel's overrides. */\n channel?: string;\n /** Backend toolkit IDs to enable. */\n tools?: string[];\n /** Frontend tool IDs to include in enabled_tools. */\n frontendToolIds?: string[];\n /** Frontend tools registered on the assistant client. Tools with a local\n * `execute` are also declared as `client_tools` and executed through the\n * statewire client-tool bridge. */\n frontendToolkit?: Toolkit;\n /** Asset IDs to include in the workbench. */\n workbench?: string[];\n /** Knowledge base IDs for RAG context. */\n knowledgeBase?: string[];\n /** System prompt override. */\n systemPrompt?: string;\n /** Client app identifier for session filtering. Sent as app_id in runConfig.custom. */\n appId?: string;\n /** Additional fields merged into runConfig.custom. */\n extraRunConfig?: Record<string, unknown>;\n /** Called on statewire transport errors (send rejections, stream failures). */\n onError?: (error: Error) => void;\n /** Projected connection status changes (deduped; null on unmount). */\n onConnectionChange?: UseDeepAgentThreadOptions['onConnectionChange'];\n /** Raw connection observer — carries the transport's reconnect handle. */\n onRawConnectionChange?: UseDeepAgentThreadOptions['onRawConnectionChange'];\n /** Notified when a run starts/stops. */\n onRunningChange?: UseDeepAgentThreadOptions['onRunningChange'];\n /**\n * Observes the host's own state: called once the attach's hello has landed\n * (with the snapshot it carried) and then on every later frame — never with\n * the placeholder or remembered snapshot a thread renders before its hello.\n * Called per streamed token, so keep it cheap.\n */\n onServerState?: (state: DeepAgentStreamValues) => void;\n}\n\nconst EMPTY_TOOLKIT: Toolkit = {};\nconst EMPTY_TOOL_IDS: string[] = [];\n\n/** The client tools derived from the runtime's frontend toolkit — exposed so\n * the provider can mount the client-tool bridge with the same set. */\nexport function useStatewireClientTools(\n frontendToolkit: Toolkit | undefined,\n): RegisteredClientTool[] {\n return useMemo(\n () => collectStatewireClientTools(frontendToolkit ?? EMPTY_TOOLKIT),\n [frontendToolkit],\n );\n}\n\n/**\n * Statewire runtime for the Athena SDK — a thin shim over the shared\n * `@athenaintel/agent-runtime` `useDeepAgentRuntime`: it attaches to the\n * agora-hosted deep-agent thread at `${syncUrl}/${threadId}` and returns the\n * `AssistantClient` to mount via `AuiProvider` — the same integration shape as\n * the Olympus, Chrome extension, Office, and mobile statewire hosts.\n *\n * Auth: a token (resolved live through `getToken` when provided) rides\n * `Authorization: Bearer`, and an API key falls back to `X-API-KEY` — both\n * accepted by the statewire host.\n */\nexport function useAthenaStatewireRuntime(\n config: AthenaStatewireRuntimeConfig,\n): AssistantClient {\n const {\n syncUrl,\n threadId,\n isNewChat = false,\n apiKey,\n token,\n getToken,\n model,\n agent,\n channel,\n tools = EMPTY_TOOL_IDS,\n frontendToolIds = EMPTY_TOOL_IDS,\n frontendToolkit = EMPTY_TOOLKIT,\n workbench,\n knowledgeBase,\n systemPrompt,\n appId,\n extraRunConfig,\n snapshotCache,\n messageWindow = ATHENA_SDK_MESSAGE_WINDOW,\n toolOutputs,\n onError,\n onConnectionChange,\n onRawConnectionChange,\n onRunningChange,\n onServerState,\n } = config;\n\n // Fail loud at mount, not at first send: the statewire host always runs the\n // Athena deep agent, so an `agent` outside the collab_agent namespace can\n // never apply. Throwing here (render time) is what makes a misconfigured\n // integration visible instead of quietly answering as the wrong agent.\n resolveAgentProp({ agent });\n\n const clientTools = useStatewireClientTools(frontendToolkit);\n\n // The persisted command lane is keyed by WHO is signed in as well as by host\n // and thread (see `statewireSessionKey`).\n const authContext = useContext(AthenaAuthContext);\n const principal = authContext?.user?.userId ?? '';\n\n // Attach → first server snapshot span, keyed per thread attach.\n const attachSpanRef = useRef<ReturnType<AthenaDiagnosticsSpanStarter> | null>(null);\n const attachThreadRef = useRef<string | null>(null);\n if (attachThreadRef.current !== threadId) {\n attachThreadRef.current = threadId;\n attachSpanRef.current = athenaDiagnostics.span(\n `statewire.attach:${threadId}`,\n 'statewire.attach.start',\n { thread_id: threadId, is_new_chat: isNewChat },\n );\n }\n\n // Built every render so its JSON signature can key the model-context\n // registration below: consumers commonly pass fresh array literals per\n // render, which must not thrash registration, while a changed value (a new\n // `model`, channel, tool list, system prompt, …) must re-sync the context\n // before the next send. The core pulls this value only on model-context\n // notifications, which a key change triggers.\n const runConfigValue = buildStatewireRunConfig({\n model,\n agent,\n channel,\n tools,\n frontendToolIds,\n workbench,\n knowledgeBase,\n systemPrompt,\n appId,\n extraRunConfig,\n clientTools: statewireClientToolWireEntries(clientTools),\n });\n const runConfigKey = JSON.stringify(runConfigValue);\n\n // The host's state, told apart from what the thread renders before its\n // hello: statewire publishes the initial (placeholder or remembered) state\n // through `onStateChange` too, and reports `connected` in the same flush\n // right after publishing the state the hello carried. So the latest state\n // seen when the connection first turns `connected` is the hello's, and every\n // state after it is the host's. Refs are per attach: the runtime is mounted\n // per thread.\n const lastStateRef = useRef<DeepAgentStreamValues | null>(null);\n const helloLandedRef = useRef(false);\n\n const auiTools = useMemo(() => Tools({ toolkit: frontendToolkit }), [frontendToolkit]);\n // Web client for the shared runtime core (a `use*` name so the\n // rules-of-hooks lint sees the `useAui` call inside a custom hook; the core\n // invokes it unconditionally every render).\n const useSdkClient = (thread: DeepAgentThreadElement) => useAui({ thread, tools: auiTools });\n\n return useDeepAgentRuntime({\n surface: 'AthenaSDK',\n threadId,\n baseUrl: syncUrl,\n // Option closures are re-read per render by the core, so credential\n // rotation (new `token`/`getToken`/`apiKey` props) reaches the next\n // request without remounting the thread. The transport's `staleAuth`\n // (true right after a refused credential) is forwarded as `force`.\n headers: (ctx) => resolveStatewireAuthHeaders({ apiKey, token, getToken }, ctx),\n isNewChat,\n // Queued entries render in the SDK's queue panel, so only the steer lane\n // projects into the message tail.\n laneProjection: 'steer-only',\n // Durable session: a reload resumes the lane and resends whatever the\n // server has not marked durable. Scoped by host AND principal — a thread\n // id is only meaningful against the backend that minted it, and a stored\n // lane must never resend against another signed-in user. (Consumers\n // server-render this SDK; the core's availability probe keeps those\n // sessions process-local, as before the seam existed.)\n session: {\n kind: 'local-storage',\n key: (id) => statewireSessionKey({ syncUrl, principal, threadId: id }),\n },\n snapshotCache,\n // Head interest (see `ATHENA_SDK_MESSAGE_WINDOW`): the tail of the\n // transcript instead of every message; the chat UI's history edge pages\n // older ranges in and fetches cut tool outputs into `toolOutputs`.\n messageWindow,\n toolOutputs,\n capabilities: { edit: true, reload: true, continue: true },\n // The composer stages the draft each send came from right before it\n // sends; the message carries it as its composer markdown, so its bubble\n // shows the text exactly as typed (`UserMessageText`).\n getMessageMetadata: withStagedComposerMarkdown({ threadId }),\n useClient: useSdkClient,\n onStateChange: (state) => {\n // First snapshot for this attach ends the attach span (time-to-history).\n const endSpan = attachSpanRef.current;\n if (endSpan) {\n attachSpanRef.current = null;\n endSpan('statewire.snapshot', { data: { thread_id: threadId } });\n }\n // A replay boundary republishes the pre-stream default: not host state.\n if (!isDeepAgentStateHydrated(state)) return;\n lastStateRef.current = state;\n if (helloLandedRef.current) onServerState?.(state);\n },\n onConnectionChange: (connection) => {\n athenaDiagnostics.gauge('statewire.connection', connection?.status ?? null);\n if (connection?.status === 'stopped') {\n athenaDiagnostics.emit('statewire.connection', {\n level: 'warn',\n data: {\n status: connection.status,\n reason: connection.reason,\n message: connection.message,\n },\n });\n }\n onConnectionChange?.(connection);\n },\n onRawConnectionChange: (connection) => {\n if (connection.status === 'connected' && !helloLandedRef.current) {\n helloLandedRef.current = true;\n const helloState = lastStateRef.current;\n if (helloState) onServerState?.(helloState);\n }\n onRawConnectionChange?.(connection);\n },\n onRunningChange,\n // Each model-context notification pulls the latest getter through the\n // core's per-render ref; the value-stable key re-registers (and so\n // notifies) only when the built runConfig actually changed. On a thread\n // with a server config lane, agora keeps only the per-send keys of what\n // this carries; `StatewireThreadConfigLaneSync` writes a changed model,\n // system prompt, tool list, workbench or knowledge base to the lane itself.\n runConfig: {\n get: () => runConfigValue,\n key: runConfigKey,\n },\n onError: (error) => {\n if (!isStatewireChannelTeardown(error)) {\n athenaDiagnostics.error(\n 'statewire.error',\n error,\n {\n code: 'statewire_rejected',\n message: 'The statewire host rejected a command',\n hint: 'Inspect error.detail — agora rejections carry a structured {code, message}.',\n },\n { thread_id: threadId },\n );\n }\n if (onError) {\n onError(error);\n return;\n }\n logUnhandledStatewireError('AthenaSDK', error);\n },\n });\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -4,11 +4,19 @@ const react = require("react");
|
|
|
4
4
|
const statewireModelLane = require("../runtime/statewire-model-lane.cjs");
|
|
5
5
|
const statewireServerLaneContext = require("../runtime/statewire-server-lane-context.cjs");
|
|
6
6
|
function useAthenaThreadModel() {
|
|
7
|
-
const
|
|
8
|
-
if (
|
|
7
|
+
const threadModel = useOptionalAthenaThreadModel();
|
|
8
|
+
if (threadModel === null) {
|
|
9
9
|
throw new Error("[AthenaSDK] useAthenaThreadModel must be used within <AthenaProvider>");
|
|
10
10
|
}
|
|
11
|
-
return
|
|
11
|
+
return threadModel;
|
|
12
|
+
}
|
|
13
|
+
function useOptionalAthenaThreadModel() {
|
|
14
|
+
const lane = react.useContext(statewireServerLaneContext.AthenaStatewireServerLaneContext);
|
|
15
|
+
return react.useMemo(
|
|
16
|
+
() => lane === null ? null : statewireModelLane.threadModelFromLane({ lane: lane.config, ready: lane.landed }),
|
|
17
|
+
[lane]
|
|
18
|
+
);
|
|
12
19
|
}
|
|
13
20
|
exports.useAthenaThreadModel = useAthenaThreadModel;
|
|
21
|
+
exports.useOptionalAthenaThreadModel = useOptionalAthenaThreadModel;
|
|
14
22
|
//# sourceMappingURL=use-athena-thread-model.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-athena-thread-model.cjs","sources":["../../src/threads/use-athena-thread-model.ts"],"sourcesContent":["import { useContext, useMemo } from 'react';\nimport { type AthenaThreadModel, threadModelFromLane } from '../runtime/statewire-model-lane';\nimport { AthenaStatewireServerLaneContext } from '../runtime/statewire-server-lane-context';\n\nexport type { AthenaThreadModel };\n\n/**\n * The model the active thread's server config pins, read from the thread's\n * config lane (`state.config.custom.model`) as the statewire host sent it.\n *\n * Once a thread has run, its server config (not the provider's `model` prop)\n * decides which model each turn uses; another client (Olympus, a second tab)\n * can change it at any time. Read this hook when a session opens to show the\n * session's real model in your picker: `{ model: null, configured: true }` is\n * a thread that pins no model, so the collab agent's own model (or the\n * default) runs. Wait for `ready`: until the host's snapshot of the thread has\n * arrived every thread reads as unconfigured, and a never-started chat stays\n * not ready until its first send attaches it.\n *\n * The value is the server's: a model change the provider is still writing\n * shows here once the server has applied it. Seed the picker from it when the\n * thread opens, rather than mirroring it on every render.\n *\n * Must be used within `<AthenaProvider>`.\n */\nexport function useAthenaThreadModel(): AthenaThreadModel {\n const
|
|
1
|
+
{"version":3,"file":"use-athena-thread-model.cjs","sources":["../../src/threads/use-athena-thread-model.ts"],"sourcesContent":["import { useContext, useMemo } from 'react';\nimport { type AthenaThreadModel, threadModelFromLane } from '../runtime/statewire-model-lane';\nimport { AthenaStatewireServerLaneContext } from '../runtime/statewire-server-lane-context';\n\nexport type { AthenaThreadModel };\n\n/**\n * The model the active thread's server config pins, read from the thread's\n * config lane (`state.config.custom.model`) as the statewire host sent it.\n *\n * Once a thread has run, its server config (not the provider's `model` prop)\n * decides which model each turn uses; another client (Olympus, a second tab)\n * can change it at any time. Read this hook when a session opens to show the\n * session's real model in your picker: `{ model: null, configured: true }` is\n * a thread that pins no model, so the collab agent's own model (or the\n * default) runs. Wait for `ready`: until the host's snapshot of the thread has\n * arrived every thread reads as unconfigured, and a never-started chat stays\n * not ready until its first send attaches it.\n *\n * The value is the server's: a model change the provider is still writing\n * shows here once the server has applied it. Seed the picker from it when the\n * thread opens, rather than mirroring it on every render.\n *\n * Must be used within `<AthenaProvider>`.\n */\nexport function useAthenaThreadModel(): AthenaThreadModel {\n const threadModel = useOptionalAthenaThreadModel();\n if (threadModel === null) {\n throw new Error('[AthenaSDK] useAthenaThreadModel must be used within <AthenaProvider>');\n }\n return threadModel;\n}\n\n/**\n * {@link useAthenaThreadModel}, or `null` outside `<AthenaProvider>` (a picker\n * a host renders beside the chat rather than in it). Internal.\n */\nexport function useOptionalAthenaThreadModel(): AthenaThreadModel | null {\n const lane = useContext(AthenaStatewireServerLaneContext);\n return useMemo(\n () => (lane === null ? null : threadModelFromLane({ lane: lane.config, ready: lane.landed })),\n [lane],\n );\n}\n"],"names":["useContext","AthenaStatewireServerLaneContext","useMemo","threadModelFromLane"],"mappings":";;;;;AAyBO,SAAS,uBAA0C;AACxD,QAAM,cAAc,6BAAA;AACpB,MAAI,gBAAgB,MAAM;AACxB,UAAM,IAAI,MAAM,uEAAuE;AAAA,EACzF;AACA,SAAO;AACT;AAMO,SAAS,+BAAyD;AACvE,QAAM,OAAOA,MAAAA,WAAWC,2DAAgC;AACxD,SAAOC,MAAAA;AAAAA,IACL,MAAO,SAAS,OAAO,OAAOC,mBAAAA,oBAAoB,EAAE,MAAM,KAAK,QAAQ,OAAO,KAAK,OAAA,CAAQ;AAAA,IAC3F,CAAC,IAAI;AAAA,EAAA;AAET;;;"}
|
|
@@ -20,3 +20,8 @@ export type { AthenaThreadModel };
|
|
|
20
20
|
* Must be used within `<AthenaProvider>`.
|
|
21
21
|
*/
|
|
22
22
|
export declare function useAthenaThreadModel(): AthenaThreadModel;
|
|
23
|
+
/**
|
|
24
|
+
* {@link useAthenaThreadModel}, or `null` outside `<AthenaProvider>` (a picker
|
|
25
|
+
* a host renders beside the chat rather than in it). Internal.
|
|
26
|
+
*/
|
|
27
|
+
export declare function useOptionalAthenaThreadModel(): AthenaThreadModel | null;
|
|
@@ -2,13 +2,21 @@ import { useContext, useMemo } from "react";
|
|
|
2
2
|
import { threadModelFromLane } from "../runtime/statewire-model-lane.js";
|
|
3
3
|
import { AthenaStatewireServerLaneContext } from "../runtime/statewire-server-lane-context.js";
|
|
4
4
|
function useAthenaThreadModel() {
|
|
5
|
-
const
|
|
6
|
-
if (
|
|
5
|
+
const threadModel = useOptionalAthenaThreadModel();
|
|
6
|
+
if (threadModel === null) {
|
|
7
7
|
throw new Error("[AthenaSDK] useAthenaThreadModel must be used within <AthenaProvider>");
|
|
8
8
|
}
|
|
9
|
-
return
|
|
9
|
+
return threadModel;
|
|
10
|
+
}
|
|
11
|
+
function useOptionalAthenaThreadModel() {
|
|
12
|
+
const lane = useContext(AthenaStatewireServerLaneContext);
|
|
13
|
+
return useMemo(
|
|
14
|
+
() => lane === null ? null : threadModelFromLane({ lane: lane.config, ready: lane.landed }),
|
|
15
|
+
[lane]
|
|
16
|
+
);
|
|
10
17
|
}
|
|
11
18
|
export {
|
|
12
|
-
useAthenaThreadModel
|
|
19
|
+
useAthenaThreadModel,
|
|
20
|
+
useOptionalAthenaThreadModel
|
|
13
21
|
};
|
|
14
22
|
//# sourceMappingURL=use-athena-thread-model.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-athena-thread-model.js","sources":["../../src/threads/use-athena-thread-model.ts"],"sourcesContent":["import { useContext, useMemo } from 'react';\nimport { type AthenaThreadModel, threadModelFromLane } from '../runtime/statewire-model-lane';\nimport { AthenaStatewireServerLaneContext } from '../runtime/statewire-server-lane-context';\n\nexport type { AthenaThreadModel };\n\n/**\n * The model the active thread's server config pins, read from the thread's\n * config lane (`state.config.custom.model`) as the statewire host sent it.\n *\n * Once a thread has run, its server config (not the provider's `model` prop)\n * decides which model each turn uses; another client (Olympus, a second tab)\n * can change it at any time. Read this hook when a session opens to show the\n * session's real model in your picker: `{ model: null, configured: true }` is\n * a thread that pins no model, so the collab agent's own model (or the\n * default) runs. Wait for `ready`: until the host's snapshot of the thread has\n * arrived every thread reads as unconfigured, and a never-started chat stays\n * not ready until its first send attaches it.\n *\n * The value is the server's: a model change the provider is still writing\n * shows here once the server has applied it. Seed the picker from it when the\n * thread opens, rather than mirroring it on every render.\n *\n * Must be used within `<AthenaProvider>`.\n */\nexport function useAthenaThreadModel(): AthenaThreadModel {\n const
|
|
1
|
+
{"version":3,"file":"use-athena-thread-model.js","sources":["../../src/threads/use-athena-thread-model.ts"],"sourcesContent":["import { useContext, useMemo } from 'react';\nimport { type AthenaThreadModel, threadModelFromLane } from '../runtime/statewire-model-lane';\nimport { AthenaStatewireServerLaneContext } from '../runtime/statewire-server-lane-context';\n\nexport type { AthenaThreadModel };\n\n/**\n * The model the active thread's server config pins, read from the thread's\n * config lane (`state.config.custom.model`) as the statewire host sent it.\n *\n * Once a thread has run, its server config (not the provider's `model` prop)\n * decides which model each turn uses; another client (Olympus, a second tab)\n * can change it at any time. Read this hook when a session opens to show the\n * session's real model in your picker: `{ model: null, configured: true }` is\n * a thread that pins no model, so the collab agent's own model (or the\n * default) runs. Wait for `ready`: until the host's snapshot of the thread has\n * arrived every thread reads as unconfigured, and a never-started chat stays\n * not ready until its first send attaches it.\n *\n * The value is the server's: a model change the provider is still writing\n * shows here once the server has applied it. Seed the picker from it when the\n * thread opens, rather than mirroring it on every render.\n *\n * Must be used within `<AthenaProvider>`.\n */\nexport function useAthenaThreadModel(): AthenaThreadModel {\n const threadModel = useOptionalAthenaThreadModel();\n if (threadModel === null) {\n throw new Error('[AthenaSDK] useAthenaThreadModel must be used within <AthenaProvider>');\n }\n return threadModel;\n}\n\n/**\n * {@link useAthenaThreadModel}, or `null` outside `<AthenaProvider>` (a picker\n * a host renders beside the chat rather than in it). Internal.\n */\nexport function useOptionalAthenaThreadModel(): AthenaThreadModel | null {\n const lane = useContext(AthenaStatewireServerLaneContext);\n return useMemo(\n () => (lane === null ? null : threadModelFromLane({ lane: lane.config, ready: lane.landed })),\n [lane],\n );\n}\n"],"names":[],"mappings":";;;AAyBO,SAAS,uBAA0C;AACxD,QAAM,cAAc,6BAAA;AACpB,MAAI,gBAAgB,MAAM;AACxB,UAAM,IAAI,MAAM,uEAAuE;AAAA,EACzF;AACA,SAAO;AACT;AAMO,SAAS,+BAAyD;AACvE,QAAM,OAAO,WAAW,gCAAgC;AACxD,SAAO;AAAA,IACL,MAAO,SAAS,OAAO,OAAO,oBAAoB,EAAE,MAAM,KAAK,QAAQ,OAAO,KAAK,OAAA,CAAQ;AAAA,IAC3F,CAAC,IAAI;AAAA,EAAA;AAET;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@athenaintel/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.0",
|
|
4
4
|
"description": "Athena React SDK — Build AI-powered chat applications with the Athena platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
"@assistant-ui/react": "0.15.28",
|
|
100
100
|
"@assistant-ui/react-langgraph": "0.14.34",
|
|
101
101
|
"@assistant-ui/react-markdown": "0.14.21",
|
|
102
|
-
"@assistant-ui/react-statewire": "0.
|
|
102
|
+
"@assistant-ui/react-statewire": "0.31.0",
|
|
103
103
|
"@assistant-ui/store": "0.3.21",
|
|
104
104
|
"@assistant-ui/tap": "0.9.23",
|
|
105
105
|
"@floating-ui/react": "^0.27.7",
|
|
@@ -127,7 +127,7 @@
|
|
|
127
127
|
"lucide-react": "^0.575.0",
|
|
128
128
|
"radix-ui": "^1.4.3",
|
|
129
129
|
"remark-gfm": "^4.0.0",
|
|
130
|
-
"statewire": "0.
|
|
130
|
+
"statewire": "0.21.0",
|
|
131
131
|
"tiptap-markdown": "^0.9.0",
|
|
132
132
|
"zustand": "^5.0.11"
|
|
133
133
|
},
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const reactStatewire = require("@assistant-ui/react-statewire");
|
|
4
|
-
const react = require("react");
|
|
5
|
-
const athenaDiagnostics = require("../diagnostics/athena-diagnostics.cjs");
|
|
6
|
-
const statewireModelLane = require("./statewire-model-lane.cjs");
|
|
7
|
-
const useDeepAgentConfig = require("../agent-runtime/src/use-deep-agent-config.cjs");
|
|
8
|
-
const errors = require("../agent-runtime/src/errors.cjs");
|
|
9
|
-
const MAX_PATCH_ATTEMPTS_PER_CHANGE = 3;
|
|
10
|
-
const RETRY_BACKOFF_MS = [1e3, 4e3];
|
|
11
|
-
function StatewireModelLaneSync({
|
|
12
|
-
threadId,
|
|
13
|
-
model,
|
|
14
|
-
lane,
|
|
15
|
-
channelLive
|
|
16
|
-
}) {
|
|
17
|
-
const send = reactStatewire.useStatewireSendCommand();
|
|
18
|
-
const sendCommand = (command) => Promise.resolve(send(command));
|
|
19
|
-
const { configVersion, custom, patchConfig } = useDeepAgentConfig.useDeepAgentConfigController({
|
|
20
|
-
serverConfig: lane.config,
|
|
21
|
-
sendCommand
|
|
22
|
-
});
|
|
23
|
-
const seenModelRef = react.useRef(model);
|
|
24
|
-
const pendingRef = react.useRef(false);
|
|
25
|
-
const attemptsRef = react.useRef(0);
|
|
26
|
-
const closedRef = react.useRef(false);
|
|
27
|
-
const patchConfigRef = react.useRef(patchConfig);
|
|
28
|
-
patchConfigRef.current = patchConfig;
|
|
29
|
-
const [retryTick, setRetryTick] = react.useState(0);
|
|
30
|
-
const retryTimerRef = react.useRef(null);
|
|
31
|
-
react.useEffect(
|
|
32
|
-
() => () => {
|
|
33
|
-
if (retryTimerRef.current !== null) clearTimeout(retryTimerRef.current);
|
|
34
|
-
},
|
|
35
|
-
[]
|
|
36
|
-
);
|
|
37
|
-
const landed = lane.landed;
|
|
38
|
-
react.useEffect(() => {
|
|
39
|
-
if (model !== seenModelRef.current) {
|
|
40
|
-
seenModelRef.current = model;
|
|
41
|
-
pendingRef.current = true;
|
|
42
|
-
attemptsRef.current = 0;
|
|
43
|
-
}
|
|
44
|
-
if (!pendingRef.current || closedRef.current || !channelLive) return;
|
|
45
|
-
const decision = statewireModelLane.decideModelLanePatch({ desiredModel: model, landed, configVersion, custom });
|
|
46
|
-
if (decision.kind === "wait") return;
|
|
47
|
-
pendingRef.current = false;
|
|
48
|
-
if (decision.kind === "done") return;
|
|
49
|
-
const { patch } = decision;
|
|
50
|
-
const changeModel = model;
|
|
51
|
-
attemptsRef.current += 1;
|
|
52
|
-
patchConfigRef.current(patch).catch((error) => {
|
|
53
|
-
const data = { thread_id: threadId, model: patch.model };
|
|
54
|
-
if (useDeepAgentConfig.isConfigWriteForbiddenRejection(error) || useDeepAgentConfig.isConfigSyncUnavailableRejection(error)) {
|
|
55
|
-
closedRef.current = true;
|
|
56
|
-
athenaDiagnostics.athenaDiagnostics.emit("statewire.config", {
|
|
57
|
-
level: "warn",
|
|
58
|
-
data: {
|
|
59
|
-
...data,
|
|
60
|
-
reason: useDeepAgentConfig.isConfigWriteForbiddenRejection(error) ? "config_write_forbidden" : "config_sync_unavailable"
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
const outcome = errors.statewireTransportOutcome(error);
|
|
66
|
-
if (outcome !== null) {
|
|
67
|
-
athenaDiagnostics.athenaDiagnostics.emit("statewire.config", {
|
|
68
|
-
level: "debug",
|
|
69
|
-
data: { ...data, outcome }
|
|
70
|
-
});
|
|
71
|
-
if (outcome !== "unmount" && seenModelRef.current === changeModel && attemptsRef.current < MAX_PATCH_ATTEMPTS_PER_CHANGE) {
|
|
72
|
-
pendingRef.current = true;
|
|
73
|
-
const delay = RETRY_BACKOFF_MS[Math.min(attemptsRef.current - 1, RETRY_BACKOFF_MS.length - 1)];
|
|
74
|
-
retryTimerRef.current = setTimeout(() => {
|
|
75
|
-
retryTimerRef.current = null;
|
|
76
|
-
setRetryTick((tick) => tick + 1);
|
|
77
|
-
}, delay);
|
|
78
|
-
}
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
athenaDiagnostics.athenaDiagnostics.error(
|
|
82
|
-
"statewire.config",
|
|
83
|
-
error,
|
|
84
|
-
{
|
|
85
|
-
code: "statewire_rejected",
|
|
86
|
-
message: "The statewire host refused the model change",
|
|
87
|
-
hint: "Inspect error.detail: agora rejections carry a structured {code, message}. The thread keeps running with the model its server config holds."
|
|
88
|
-
},
|
|
89
|
-
data
|
|
90
|
-
);
|
|
91
|
-
});
|
|
92
|
-
}, [model, landed, configVersion, custom, channelLive, retryTick, threadId]);
|
|
93
|
-
return null;
|
|
94
|
-
}
|
|
95
|
-
exports.StatewireModelLaneSync = StatewireModelLaneSync;
|
|
96
|
-
//# sourceMappingURL=StatewireModelLaneSync.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"StatewireModelLaneSync.cjs","sources":["../../src/runtime/StatewireModelLaneSync.tsx"],"sourcesContent":["import { useStatewireSendCommand } from '@assistant-ui/react-statewire';\nimport {\n type DeepAgentConfigControllerInputs,\n isConfigSyncUnavailableRejection,\n isConfigWriteForbiddenRejection,\n statewireTransportOutcome,\n useDeepAgentConfigController,\n} from '@athenaintel/agent-runtime';\nimport { useEffect, useRef, useState } from 'react';\nimport { athenaDiagnostics } from '../diagnostics/athena-diagnostics';\nimport { decideModelLanePatch } from './statewire-model-lane';\nimport type { AthenaStatewireServerLane } from './statewire-server-lane-context';\n\n/** Writes of one model change, the first included, before the sync gives up on it. */\nconst MAX_PATCH_ATTEMPTS_PER_CHANGE = 3;\n\n/** Wait before re-writing a change the channel dropped: 1 s, then 4 s. */\nconst RETRY_BACKOFF_MS = [1_000, 4_000] as const;\n\n/**\n * Writes the provider's `model` prop to the thread's server config lane when\n * the host changes it during an attach.\n *\n * Once a thread has a lane (version > 0), agora composes every run from the\n * lane and drops the thread-config keys a send carries, `model` included, so\n * a model picked after the thread's first turn would never apply. A change of\n * the prop therefore becomes `config/patch {model}` (`null` when the prop\n * becomes `undefined`: the collab agent's own model, or the default, applies).\n *\n * Only a CHANGE is written. The prop's value when the attach starts is never\n * written, even when the lane holds another model: that model is another\n * client's (or an earlier session's) choice for this thread, and an SDK app's\n * global pick must not overwrite it just by opening the thread. Hosts that\n * want the picker to follow the thread read `useAthenaThreadModel`.\n *\n * Decisions read the lane the host itself sent on this attach (`lane`, from\n * the transport), never the rendered thread state: before the attach snapshot\n * lands that state is a placeholder without a lane, or a remembered snapshot\n * with an older one, and comparing a change against either would write over\n * the session's real model. A change made before the snapshot lands waits for\n * it.\n *\n * A version-0 thread needs no write: the next send carries the model and\n * agora's deferral seed persists it. The change stays pending until the lane\n * exists, so a lane an earlier send seeds with the previous model is\n * corrected when it lands. A refusal that can never succeed (a view-only\n * collaborator, a workspace without server config) stops the sync for the\n * attach and is reported on the diagnostics bus. A write the channel dropped\n * (a lost stream, the host's expired verdict) stays pending and is written\n * again after a backoff once the channel is live, at most\n * {@link MAX_PATCH_ATTEMPTS_PER_CHANGE} times per change.\n *\n * Mounted under the runtime's `AuiProvider`, keyed by the thread, so each\n * attach starts with an empty pending set.\n */\nexport function StatewireModelLaneSync({\n threadId,\n model,\n lane,\n channelLive,\n}: {\n threadId: string;\n /** The model this thread's sends carry, published with the thread (see the provider). */\n model: string | undefined;\n lane: AthenaStatewireServerLane;\n /** Whether the channel can take a command now; writes wait for it. */\n channelLive: boolean;\n}): null {\n const send = useStatewireSendCommand();\n const sendCommand: DeepAgentConfigControllerInputs['sendCommand'] = (command) =>\n Promise.resolve(send(command));\n const { configVersion, custom, patchConfig } = useDeepAgentConfigController({\n serverConfig: lane.config,\n sendCommand,\n });\n\n const seenModelRef = useRef(model);\n const pendingRef = useRef(false);\n const attemptsRef = useRef(0);\n const closedRef = useRef(false);\n const patchConfigRef = useRef(patchConfig);\n patchConfigRef.current = patchConfig;\n // Bumped (after a backoff) to re-run the effect for a write the channel dropped.\n const [retryTick, setRetryTick] = useState(0);\n const retryTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n useEffect(\n () => () => {\n if (retryTimerRef.current !== null) clearTimeout(retryTimerRef.current);\n },\n [],\n );\n const landed = lane.landed;\n\n useEffect(() => {\n if (model !== seenModelRef.current) {\n seenModelRef.current = model;\n pendingRef.current = true;\n attemptsRef.current = 0;\n }\n if (!pendingRef.current || closedRef.current || !channelLive) return;\n const decision = decideModelLanePatch({ desiredModel: model, landed, configVersion, custom });\n if (decision.kind === 'wait') return;\n pendingRef.current = false;\n if (decision.kind === 'done') return;\n\n const { patch } = decision;\n const changeModel = model;\n attemptsRef.current += 1;\n patchConfigRef.current(patch).catch((error: unknown) => {\n const data = { thread_id: threadId, model: patch.model };\n if (isConfigWriteForbiddenRejection(error) || isConfigSyncUnavailableRejection(error)) {\n closedRef.current = true;\n athenaDiagnostics.emit('statewire.config', {\n level: 'warn',\n data: {\n ...data,\n reason: isConfigWriteForbiddenRejection(error)\n ? 'config_write_forbidden'\n : 'config_sync_unavailable',\n },\n });\n return;\n }\n const outcome = statewireTransportOutcome(error);\n if (outcome !== null) {\n // Channel weather (a dropped stream, the host's expired verdict): keep\n // the change pending and write it again after a backoff, once the\n // channel is live, unless the prop has moved on or the attach is gone.\n athenaDiagnostics.emit('statewire.config', {\n level: 'debug',\n data: { ...data, outcome },\n });\n if (\n outcome !== 'unmount' &&\n seenModelRef.current === changeModel &&\n attemptsRef.current < MAX_PATCH_ATTEMPTS_PER_CHANGE\n ) {\n pendingRef.current = true;\n const delay =\n RETRY_BACKOFF_MS[Math.min(attemptsRef.current - 1, RETRY_BACKOFF_MS.length - 1)];\n retryTimerRef.current = setTimeout(() => {\n retryTimerRef.current = null;\n setRetryTick((tick) => tick + 1);\n }, delay);\n }\n return;\n }\n athenaDiagnostics.error(\n 'statewire.config',\n error,\n {\n code: 'statewire_rejected',\n message: 'The statewire host refused the model change',\n hint: 'Inspect error.detail: agora rejections carry a structured {code, message}. The thread keeps running with the model its server config holds.',\n },\n data,\n );\n });\n }, [model, landed, configVersion, custom, channelLive, retryTick, threadId]);\n\n return null;\n}\n"],"names":["useStatewireSendCommand","useDeepAgentConfigController","useRef","useState","useEffect","decideModelLanePatch","isConfigWriteForbiddenRejection","isConfigSyncUnavailableRejection","athenaDiagnostics","statewireTransportOutcome"],"mappings":"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"}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import type { AthenaStatewireServerLane } from './statewire-server-lane-context';
|
|
2
|
-
/**
|
|
3
|
-
* Writes the provider's `model` prop to the thread's server config lane when
|
|
4
|
-
* the host changes it during an attach.
|
|
5
|
-
*
|
|
6
|
-
* Once a thread has a lane (version > 0), agora composes every run from the
|
|
7
|
-
* lane and drops the thread-config keys a send carries, `model` included, so
|
|
8
|
-
* a model picked after the thread's first turn would never apply. A change of
|
|
9
|
-
* the prop therefore becomes `config/patch {model}` (`null` when the prop
|
|
10
|
-
* becomes `undefined`: the collab agent's own model, or the default, applies).
|
|
11
|
-
*
|
|
12
|
-
* Only a CHANGE is written. The prop's value when the attach starts is never
|
|
13
|
-
* written, even when the lane holds another model: that model is another
|
|
14
|
-
* client's (or an earlier session's) choice for this thread, and an SDK app's
|
|
15
|
-
* global pick must not overwrite it just by opening the thread. Hosts that
|
|
16
|
-
* want the picker to follow the thread read `useAthenaThreadModel`.
|
|
17
|
-
*
|
|
18
|
-
* Decisions read the lane the host itself sent on this attach (`lane`, from
|
|
19
|
-
* the transport), never the rendered thread state: before the attach snapshot
|
|
20
|
-
* lands that state is a placeholder without a lane, or a remembered snapshot
|
|
21
|
-
* with an older one, and comparing a change against either would write over
|
|
22
|
-
* the session's real model. A change made before the snapshot lands waits for
|
|
23
|
-
* it.
|
|
24
|
-
*
|
|
25
|
-
* A version-0 thread needs no write: the next send carries the model and
|
|
26
|
-
* agora's deferral seed persists it. The change stays pending until the lane
|
|
27
|
-
* exists, so a lane an earlier send seeds with the previous model is
|
|
28
|
-
* corrected when it lands. A refusal that can never succeed (a view-only
|
|
29
|
-
* collaborator, a workspace without server config) stops the sync for the
|
|
30
|
-
* attach and is reported on the diagnostics bus. A write the channel dropped
|
|
31
|
-
* (a lost stream, the host's expired verdict) stays pending and is written
|
|
32
|
-
* again after a backoff once the channel is live, at most
|
|
33
|
-
* {@link MAX_PATCH_ATTEMPTS_PER_CHANGE} times per change.
|
|
34
|
-
*
|
|
35
|
-
* Mounted under the runtime's `AuiProvider`, keyed by the thread, so each
|
|
36
|
-
* attach starts with an empty pending set.
|
|
37
|
-
*/
|
|
38
|
-
export declare function StatewireModelLaneSync({ threadId, model, lane, channelLive, }: {
|
|
39
|
-
threadId: string;
|
|
40
|
-
/** The model this thread's sends carry, published with the thread (see the provider). */
|
|
41
|
-
model: string | undefined;
|
|
42
|
-
lane: AthenaStatewireServerLane;
|
|
43
|
-
/** Whether the channel can take a command now; writes wait for it. */
|
|
44
|
-
channelLive: boolean;
|
|
45
|
-
}): null;
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { useStatewireSendCommand } from "@assistant-ui/react-statewire";
|
|
2
|
-
import { useRef, useState, useEffect } from "react";
|
|
3
|
-
import { athenaDiagnostics } from "../diagnostics/athena-diagnostics.js";
|
|
4
|
-
import { decideModelLanePatch } from "./statewire-model-lane.js";
|
|
5
|
-
import { useDeepAgentConfigController, isConfigWriteForbiddenRejection, isConfigSyncUnavailableRejection } from "../agent-runtime/src/use-deep-agent-config.js";
|
|
6
|
-
import { statewireTransportOutcome } from "../agent-runtime/src/errors.js";
|
|
7
|
-
const MAX_PATCH_ATTEMPTS_PER_CHANGE = 3;
|
|
8
|
-
const RETRY_BACKOFF_MS = [1e3, 4e3];
|
|
9
|
-
function StatewireModelLaneSync({
|
|
10
|
-
threadId,
|
|
11
|
-
model,
|
|
12
|
-
lane,
|
|
13
|
-
channelLive
|
|
14
|
-
}) {
|
|
15
|
-
const send = useStatewireSendCommand();
|
|
16
|
-
const sendCommand = (command) => Promise.resolve(send(command));
|
|
17
|
-
const { configVersion, custom, patchConfig } = useDeepAgentConfigController({
|
|
18
|
-
serverConfig: lane.config,
|
|
19
|
-
sendCommand
|
|
20
|
-
});
|
|
21
|
-
const seenModelRef = useRef(model);
|
|
22
|
-
const pendingRef = useRef(false);
|
|
23
|
-
const attemptsRef = useRef(0);
|
|
24
|
-
const closedRef = useRef(false);
|
|
25
|
-
const patchConfigRef = useRef(patchConfig);
|
|
26
|
-
patchConfigRef.current = patchConfig;
|
|
27
|
-
const [retryTick, setRetryTick] = useState(0);
|
|
28
|
-
const retryTimerRef = useRef(null);
|
|
29
|
-
useEffect(
|
|
30
|
-
() => () => {
|
|
31
|
-
if (retryTimerRef.current !== null) clearTimeout(retryTimerRef.current);
|
|
32
|
-
},
|
|
33
|
-
[]
|
|
34
|
-
);
|
|
35
|
-
const landed = lane.landed;
|
|
36
|
-
useEffect(() => {
|
|
37
|
-
if (model !== seenModelRef.current) {
|
|
38
|
-
seenModelRef.current = model;
|
|
39
|
-
pendingRef.current = true;
|
|
40
|
-
attemptsRef.current = 0;
|
|
41
|
-
}
|
|
42
|
-
if (!pendingRef.current || closedRef.current || !channelLive) return;
|
|
43
|
-
const decision = decideModelLanePatch({ desiredModel: model, landed, configVersion, custom });
|
|
44
|
-
if (decision.kind === "wait") return;
|
|
45
|
-
pendingRef.current = false;
|
|
46
|
-
if (decision.kind === "done") return;
|
|
47
|
-
const { patch } = decision;
|
|
48
|
-
const changeModel = model;
|
|
49
|
-
attemptsRef.current += 1;
|
|
50
|
-
patchConfigRef.current(patch).catch((error) => {
|
|
51
|
-
const data = { thread_id: threadId, model: patch.model };
|
|
52
|
-
if (isConfigWriteForbiddenRejection(error) || isConfigSyncUnavailableRejection(error)) {
|
|
53
|
-
closedRef.current = true;
|
|
54
|
-
athenaDiagnostics.emit("statewire.config", {
|
|
55
|
-
level: "warn",
|
|
56
|
-
data: {
|
|
57
|
-
...data,
|
|
58
|
-
reason: isConfigWriteForbiddenRejection(error) ? "config_write_forbidden" : "config_sync_unavailable"
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
const outcome = statewireTransportOutcome(error);
|
|
64
|
-
if (outcome !== null) {
|
|
65
|
-
athenaDiagnostics.emit("statewire.config", {
|
|
66
|
-
level: "debug",
|
|
67
|
-
data: { ...data, outcome }
|
|
68
|
-
});
|
|
69
|
-
if (outcome !== "unmount" && seenModelRef.current === changeModel && attemptsRef.current < MAX_PATCH_ATTEMPTS_PER_CHANGE) {
|
|
70
|
-
pendingRef.current = true;
|
|
71
|
-
const delay = RETRY_BACKOFF_MS[Math.min(attemptsRef.current - 1, RETRY_BACKOFF_MS.length - 1)];
|
|
72
|
-
retryTimerRef.current = setTimeout(() => {
|
|
73
|
-
retryTimerRef.current = null;
|
|
74
|
-
setRetryTick((tick) => tick + 1);
|
|
75
|
-
}, delay);
|
|
76
|
-
}
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
athenaDiagnostics.error(
|
|
80
|
-
"statewire.config",
|
|
81
|
-
error,
|
|
82
|
-
{
|
|
83
|
-
code: "statewire_rejected",
|
|
84
|
-
message: "The statewire host refused the model change",
|
|
85
|
-
hint: "Inspect error.detail: agora rejections carry a structured {code, message}. The thread keeps running with the model its server config holds."
|
|
86
|
-
},
|
|
87
|
-
data
|
|
88
|
-
);
|
|
89
|
-
});
|
|
90
|
-
}, [model, landed, configVersion, custom, channelLive, retryTick, threadId]);
|
|
91
|
-
return null;
|
|
92
|
-
}
|
|
93
|
-
export {
|
|
94
|
-
StatewireModelLaneSync
|
|
95
|
-
};
|
|
96
|
-
//# sourceMappingURL=StatewireModelLaneSync.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"StatewireModelLaneSync.js","sources":["../../src/runtime/StatewireModelLaneSync.tsx"],"sourcesContent":["import { useStatewireSendCommand } from '@assistant-ui/react-statewire';\nimport {\n type DeepAgentConfigControllerInputs,\n isConfigSyncUnavailableRejection,\n isConfigWriteForbiddenRejection,\n statewireTransportOutcome,\n useDeepAgentConfigController,\n} from '@athenaintel/agent-runtime';\nimport { useEffect, useRef, useState } from 'react';\nimport { athenaDiagnostics } from '../diagnostics/athena-diagnostics';\nimport { decideModelLanePatch } from './statewire-model-lane';\nimport type { AthenaStatewireServerLane } from './statewire-server-lane-context';\n\n/** Writes of one model change, the first included, before the sync gives up on it. */\nconst MAX_PATCH_ATTEMPTS_PER_CHANGE = 3;\n\n/** Wait before re-writing a change the channel dropped: 1 s, then 4 s. */\nconst RETRY_BACKOFF_MS = [1_000, 4_000] as const;\n\n/**\n * Writes the provider's `model` prop to the thread's server config lane when\n * the host changes it during an attach.\n *\n * Once a thread has a lane (version > 0), agora composes every run from the\n * lane and drops the thread-config keys a send carries, `model` included, so\n * a model picked after the thread's first turn would never apply. A change of\n * the prop therefore becomes `config/patch {model}` (`null` when the prop\n * becomes `undefined`: the collab agent's own model, or the default, applies).\n *\n * Only a CHANGE is written. The prop's value when the attach starts is never\n * written, even when the lane holds another model: that model is another\n * client's (or an earlier session's) choice for this thread, and an SDK app's\n * global pick must not overwrite it just by opening the thread. Hosts that\n * want the picker to follow the thread read `useAthenaThreadModel`.\n *\n * Decisions read the lane the host itself sent on this attach (`lane`, from\n * the transport), never the rendered thread state: before the attach snapshot\n * lands that state is a placeholder without a lane, or a remembered snapshot\n * with an older one, and comparing a change against either would write over\n * the session's real model. A change made before the snapshot lands waits for\n * it.\n *\n * A version-0 thread needs no write: the next send carries the model and\n * agora's deferral seed persists it. The change stays pending until the lane\n * exists, so a lane an earlier send seeds with the previous model is\n * corrected when it lands. A refusal that can never succeed (a view-only\n * collaborator, a workspace without server config) stops the sync for the\n * attach and is reported on the diagnostics bus. A write the channel dropped\n * (a lost stream, the host's expired verdict) stays pending and is written\n * again after a backoff once the channel is live, at most\n * {@link MAX_PATCH_ATTEMPTS_PER_CHANGE} times per change.\n *\n * Mounted under the runtime's `AuiProvider`, keyed by the thread, so each\n * attach starts with an empty pending set.\n */\nexport function StatewireModelLaneSync({\n threadId,\n model,\n lane,\n channelLive,\n}: {\n threadId: string;\n /** The model this thread's sends carry, published with the thread (see the provider). */\n model: string | undefined;\n lane: AthenaStatewireServerLane;\n /** Whether the channel can take a command now; writes wait for it. */\n channelLive: boolean;\n}): null {\n const send = useStatewireSendCommand();\n const sendCommand: DeepAgentConfigControllerInputs['sendCommand'] = (command) =>\n Promise.resolve(send(command));\n const { configVersion, custom, patchConfig } = useDeepAgentConfigController({\n serverConfig: lane.config,\n sendCommand,\n });\n\n const seenModelRef = useRef(model);\n const pendingRef = useRef(false);\n const attemptsRef = useRef(0);\n const closedRef = useRef(false);\n const patchConfigRef = useRef(patchConfig);\n patchConfigRef.current = patchConfig;\n // Bumped (after a backoff) to re-run the effect for a write the channel dropped.\n const [retryTick, setRetryTick] = useState(0);\n const retryTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n useEffect(\n () => () => {\n if (retryTimerRef.current !== null) clearTimeout(retryTimerRef.current);\n },\n [],\n );\n const landed = lane.landed;\n\n useEffect(() => {\n if (model !== seenModelRef.current) {\n seenModelRef.current = model;\n pendingRef.current = true;\n attemptsRef.current = 0;\n }\n if (!pendingRef.current || closedRef.current || !channelLive) return;\n const decision = decideModelLanePatch({ desiredModel: model, landed, configVersion, custom });\n if (decision.kind === 'wait') return;\n pendingRef.current = false;\n if (decision.kind === 'done') return;\n\n const { patch } = decision;\n const changeModel = model;\n attemptsRef.current += 1;\n patchConfigRef.current(patch).catch((error: unknown) => {\n const data = { thread_id: threadId, model: patch.model };\n if (isConfigWriteForbiddenRejection(error) || isConfigSyncUnavailableRejection(error)) {\n closedRef.current = true;\n athenaDiagnostics.emit('statewire.config', {\n level: 'warn',\n data: {\n ...data,\n reason: isConfigWriteForbiddenRejection(error)\n ? 'config_write_forbidden'\n : 'config_sync_unavailable',\n },\n });\n return;\n }\n const outcome = statewireTransportOutcome(error);\n if (outcome !== null) {\n // Channel weather (a dropped stream, the host's expired verdict): keep\n // the change pending and write it again after a backoff, once the\n // channel is live, unless the prop has moved on or the attach is gone.\n athenaDiagnostics.emit('statewire.config', {\n level: 'debug',\n data: { ...data, outcome },\n });\n if (\n outcome !== 'unmount' &&\n seenModelRef.current === changeModel &&\n attemptsRef.current < MAX_PATCH_ATTEMPTS_PER_CHANGE\n ) {\n pendingRef.current = true;\n const delay =\n RETRY_BACKOFF_MS[Math.min(attemptsRef.current - 1, RETRY_BACKOFF_MS.length - 1)];\n retryTimerRef.current = setTimeout(() => {\n retryTimerRef.current = null;\n setRetryTick((tick) => tick + 1);\n }, delay);\n }\n return;\n }\n athenaDiagnostics.error(\n 'statewire.config',\n error,\n {\n code: 'statewire_rejected',\n message: 'The statewire host refused the model change',\n hint: 'Inspect error.detail: agora rejections carry a structured {code, message}. The thread keeps running with the model its server config holds.',\n },\n data,\n );\n });\n }, [model, landed, configVersion, custom, channelLive, retryTick, threadId]);\n\n return null;\n}\n"],"names":[],"mappings":";;;;;;AAcA,MAAM,gCAAgC;AAGtC,MAAM,mBAAmB,CAAC,KAAO,GAAK;AAsC/B,SAAS,uBAAuB;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAOS;AACP,QAAM,OAAO,wBAAA;AACb,QAAM,cAA8D,CAAC,YACnE,QAAQ,QAAQ,KAAK,OAAO,CAAC;AAC/B,QAAM,EAAE,eAAe,QAAQ,YAAA,IAAgB,6BAA6B;AAAA,IAC1E,cAAc,KAAK;AAAA,IACnB;AAAA,EAAA,CACD;AAED,QAAM,eAAe,OAAO,KAAK;AACjC,QAAM,aAAa,OAAO,KAAK;AAC/B,QAAM,cAAc,OAAO,CAAC;AAC5B,QAAM,YAAY,OAAO,KAAK;AAC9B,QAAM,iBAAiB,OAAO,WAAW;AACzC,iBAAe,UAAU;AAEzB,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,CAAC;AAC5C,QAAM,gBAAgB,OAA6C,IAAI;AACvE;AAAA,IACE,MAAM,MAAM;AACV,UAAI,cAAc,YAAY,KAAM,cAAa,cAAc,OAAO;AAAA,IACxE;AAAA,IACA,CAAA;AAAA,EAAC;AAEH,QAAM,SAAS,KAAK;AAEpB,YAAU,MAAM;AACd,QAAI,UAAU,aAAa,SAAS;AAClC,mBAAa,UAAU;AACvB,iBAAW,UAAU;AACrB,kBAAY,UAAU;AAAA,IACxB;AACA,QAAI,CAAC,WAAW,WAAW,UAAU,WAAW,CAAC,YAAa;AAC9D,UAAM,WAAW,qBAAqB,EAAE,cAAc,OAAO,QAAQ,eAAe,QAAQ;AAC5F,QAAI,SAAS,SAAS,OAAQ;AAC9B,eAAW,UAAU;AACrB,QAAI,SAAS,SAAS,OAAQ;AAE9B,UAAM,EAAE,UAAU;AAClB,UAAM,cAAc;AACpB,gBAAY,WAAW;AACvB,mBAAe,QAAQ,KAAK,EAAE,MAAM,CAAC,UAAmB;AACtD,YAAM,OAAO,EAAE,WAAW,UAAU,OAAO,MAAM,MAAA;AACjD,UAAI,gCAAgC,KAAK,KAAK,iCAAiC,KAAK,GAAG;AACrF,kBAAU,UAAU;AACpB,0BAAkB,KAAK,oBAAoB;AAAA,UACzC,OAAO;AAAA,UACP,MAAM;AAAA,YACJ,GAAG;AAAA,YACH,QAAQ,gCAAgC,KAAK,IACzC,2BACA;AAAA,UAAA;AAAA,QACN,CACD;AACD;AAAA,MACF;AACA,YAAM,UAAU,0BAA0B,KAAK;AAC/C,UAAI,YAAY,MAAM;AAIpB,0BAAkB,KAAK,oBAAoB;AAAA,UACzC,OAAO;AAAA,UACP,MAAM,EAAE,GAAG,MAAM,QAAA;AAAA,QAAQ,CAC1B;AACD,YACE,YAAY,aACZ,aAAa,YAAY,eACzB,YAAY,UAAU,+BACtB;AACA,qBAAW,UAAU;AACrB,gBAAM,QACJ,iBAAiB,KAAK,IAAI,YAAY,UAAU,GAAG,iBAAiB,SAAS,CAAC,CAAC;AACjF,wBAAc,UAAU,WAAW,MAAM;AACvC,0BAAc,UAAU;AACxB,yBAAa,CAAC,SAAS,OAAO,CAAC;AAAA,UACjC,GAAG,KAAK;AAAA,QACV;AACA;AAAA,MACF;AACA,wBAAkB;AAAA,QAChB;AAAA,QACA;AAAA,QACA;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,MAAM;AAAA,QAAA;AAAA,QAER;AAAA,MAAA;AAAA,IAEJ,CAAC;AAAA,EACH,GAAG,CAAC,OAAO,QAAQ,eAAe,QAAQ,aAAa,WAAW,QAAQ,CAAC;AAE3E,SAAO;AACT;"}
|