@athenaintel/react 0.35.0 → 0.36.1
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 +53 -0
- package/dist/agent-runtime/src/generated/runconfig-contract.cjs +4 -0
- package/dist/agent-runtime/src/generated/runconfig-contract.cjs.map +1 -1
- package/dist/agent-runtime/src/generated/runconfig-contract.js +4 -0
- package/dist/agent-runtime/src/generated/runconfig-contract.js.map +1 -1
- package/dist/agent-runtime/src/tool-metadata.cjs +11 -0
- package/dist/agent-runtime/src/tool-metadata.cjs.map +1 -1
- package/dist/agent-runtime/src/tool-metadata.js +11 -0
- package/dist/agent-runtime/src/tool-metadata.js.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-window-sync.cjs +24 -1
- package/dist/agent-runtime/src/use-deep-agent-window-sync.cjs.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-window-sync.js +24 -1
- package/dist/agent-runtime/src/use-deep-agent-window-sync.js.map +1 -1
- package/dist/catalog/agent-mode-menu.cjs +115 -0
- package/dist/catalog/agent-mode-menu.cjs.map +1 -0
- package/dist/catalog/agent-mode-menu.d.ts +101 -0
- package/dist/catalog/agent-mode-menu.js +115 -0
- package/dist/catalog/agent-mode-menu.js.map +1 -0
- package/dist/catalog/chat-pickers.cjs +51 -0
- package/dist/catalog/chat-pickers.cjs.map +1 -1
- package/dist/catalog/chat-pickers.d.ts +28 -0
- package/dist/catalog/chat-pickers.js +52 -1
- package/dist/catalog/chat-pickers.js.map +1 -1
- package/dist/catalog/runtime-catalog.cjs +4 -0
- package/dist/catalog/runtime-catalog.cjs.map +1 -1
- package/dist/catalog/runtime-catalog.d.ts +16 -0
- package/dist/catalog/runtime-catalog.js +5 -1
- package/dist/catalog/runtime-catalog.js.map +1 -1
- package/dist/catalog/use-athena-chat-mode.cjs +139 -0
- package/dist/catalog/use-athena-chat-mode.cjs.map +1 -0
- package/dist/catalog/use-athena-chat-mode.d.ts +43 -0
- package/dist/catalog/use-athena-chat-mode.js +139 -0
- package/dist/catalog/use-athena-chat-mode.js.map +1 -0
- package/dist/chat/AthenaChat.cjs +2 -0
- package/dist/chat/AthenaChat.cjs.map +1 -1
- package/dist/chat/AthenaChat.js +2 -0
- package/dist/chat/AthenaChat.js.map +1 -1
- package/dist/chat-ui/statewire.cjs +3 -0
- package/dist/chat-ui/statewire.cjs.map +1 -1
- package/dist/chat-ui/statewire.d.ts +4 -2
- package/dist/chat-ui/statewire.js +3 -0
- package/dist/chat-ui/statewire.js.map +1 -1
- package/dist/chat-ui/thread/statewire-history-edge.cjs +149 -26
- package/dist/chat-ui/thread/statewire-history-edge.cjs.map +1 -1
- package/dist/chat-ui/thread/statewire-history-edge.d.ts +10 -3
- package/dist/chat-ui/thread/statewire-history-edge.js +152 -29
- package/dist/chat-ui/thread/statewire-history-edge.js.map +1 -1
- package/dist/index.cjs +3 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/provider/AthenaProvider.cjs +52 -1
- package/dist/provider/AthenaProvider.cjs.map +1 -1
- package/dist/provider/AthenaProvider.d.ts +11 -1
- package/dist/provider/AthenaProvider.js +53 -2
- package/dist/provider/AthenaProvider.js.map +1 -1
- package/dist/provider/chat-mode-context.cjs +6 -0
- package/dist/provider/chat-mode-context.cjs.map +1 -0
- package/dist/provider/chat-mode-context.d.ts +35 -0
- package/dist/provider/chat-mode-context.js +6 -0
- package/dist/provider/chat-mode-context.js.map +1 -0
- package/dist/react/package.json.cjs +1 -1
- package/dist/react/package.json.js +1 -1
- package/dist/runtime/statewire-run-config.cjs +10 -6
- package/dist/runtime/statewire-run-config.cjs.map +1 -1
- package/dist/runtime/statewire-run-config.d.ts +7 -0
- package/dist/runtime/statewire-run-config.js +10 -6
- package/dist/runtime/statewire-run-config.js.map +1 -1
- package/dist/runtime/useAthenaStatewireRuntime.cjs +2 -0
- package/dist/runtime/useAthenaStatewireRuntime.cjs.map +1 -1
- package/dist/runtime/useAthenaStatewireRuntime.d.ts +2 -0
- package/dist/runtime/useAthenaStatewireRuntime.js +2 -0
- package/dist/runtime/useAthenaStatewireRuntime.js.map +1 -1
- package/package.json +1 -1
|
@@ -285,6 +285,7 @@ exports.MODEL_PROVIDER_LABELS = MODEL_PROVIDER_LABELS;
|
|
|
285
285
|
exports.agentModelsRefetchInterval = agentModelsRefetchInterval;
|
|
286
286
|
exports.credentialFingerprint = credentialFingerprint;
|
|
287
287
|
exports.groupModelsByProvider = groupModelsByProvider;
|
|
288
|
+
exports.isAgentReadDenial = isAgentReadDenial;
|
|
288
289
|
exports.normalizeAgentCatalog = normalizeAgentCatalog;
|
|
289
290
|
exports.normalizeModelCatalog = normalizeModelCatalog;
|
|
290
291
|
exports.resolveCatalogModelId = resolveCatalogModelId;
|
|
@@ -292,4 +293,7 @@ exports.useAgentModelChoices = useAgentModelChoices;
|
|
|
292
293
|
exports.useAthenaAgentCatalog = useAthenaAgentCatalog;
|
|
293
294
|
exports.useAthenaAgentModelOptions = useAthenaAgentModelOptions;
|
|
294
295
|
exports.useAthenaModelCatalog = useAthenaModelCatalog;
|
|
296
|
+
exports.useCatalogAuth = useCatalogAuth;
|
|
297
|
+
exports.useCatalogPrincipal = useCatalogPrincipal;
|
|
298
|
+
exports.useCatalogQueryClient = useCatalogQueryClient;
|
|
295
299
|
//# sourceMappingURL=runtime-catalog.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-catalog.cjs","sources":["../../src/catalog/runtime-catalog.ts"],"sourcesContent":["import { QueryClient, QueryClientContext, useQuery } from '@tanstack/react-query';\nimport { useCallback, useContext, useMemo, useState } from 'react';\nimport { AthenaAuthContext } from '../auth/AthenaAuthContext';\nimport {\n AthenaGraphQLError,\n fetchGraphQL,\n type MentionAuthContext,\n} from '../chat-ui/mentions/suggestions/data/api-client';\nimport { useAthenaConfig } from '../provider/AthenaContext';\nimport { useOptionalAthenaThreadModel } from '../threads/use-athena-thread-model';\nimport {\n type AgentModelSource,\n type AthenaAgentModelOption,\n agentAssetIdOf,\n buildAgentModelOptions,\n publishedAgentModelSource,\n} from './agent-model-options';\n\n/**\n * The workspace's runtime catalog for a chat's model and agent pickers: the models the signed-in\n * person may run (the workspace's allow or deny list, admin-only models for admins), and the\n * published collab agents they can see. The rules are the Chrome extension's, which every host now\n * reads from here.\n */\n\nexport type AthenaModelStatus = 'ACTIVE' | 'LEGACY' | 'RECOMMENDED';\n\nexport interface AthenaModelOption {\n id: string;\n label: string;\n description: string | null;\n /** Upper-case provider key, e.g. `ANTHROPIC`, `OPENAI`. */\n provider: string;\n status: AthenaModelStatus;\n sortOrder: number;\n familyId: string;\n familyName: string;\n isFamilyDefault: boolean;\n isFeatured: boolean;\n variantOrder: number;\n}\n\nexport interface AthenaAgentOption {\n /** The collab agent's asset id; pass `collab_agent:<id>` as the provider's `agent`. */\n id: string;\n name: string;\n}\n\n/** The provider labels a model picker groups by. */\nexport const MODEL_PROVIDER_LABELS: Readonly<Record<string, string>> = {\n ANTHROPIC: 'Anthropic',\n OPENAI: 'OpenAI',\n GOOGLE: 'Google',\n XAI: 'xAI',\n META: 'Meta',\n MISTRAL: 'Mistral',\n DEEPSEEK: 'DeepSeek',\n QWEN: 'Qwen',\n MOONSHOT: 'Moonshot',\n MINIMAX: 'MiniMax',\n ZAI: 'Z.ai',\n};\n\nexport const ATHENA_MODEL_CATALOG_QUERY = `\n query AthenaModelCatalog {\n getCurrentUserWithWorkspace { user { isAdmin } }\n settings { workspace { approvedLargeLlms blacklistedLlms } }\n availableModels {\n id name provider status description adminOnly sortOrder\n familyId familyName isFamilyDefault isFeatured variantOrder\n }\n }\n`;\n\nexport const ATHENA_AGENT_CATALOG_QUERY = `\n query AthenaAgentCatalog {\n getAllAssets(filterByAthenaOriginalType: [\"collab_agent\"]) { id title }\n }\n`;\n\nexport interface AthenaModelCatalogResponse {\n getCurrentUserWithWorkspace: { user: { isAdmin: boolean } };\n settings: { workspace: { approvedLargeLlms: string[]; blacklistedLlms: string[] } };\n availableModels: Array<{\n id: string;\n name: string;\n provider: string;\n status: AthenaModelStatus;\n description: string | null;\n adminOnly: boolean;\n sortOrder: number;\n familyId: string;\n familyName: string;\n isFamilyDefault: boolean;\n isFeatured: boolean;\n variantOrder: number;\n }>;\n}\n\n/**\n * The models the person may pick, in catalog order. A workspace with an allow list offers only\n * those; otherwise everything except its deny list. Admin-only models are offered to admins only.\n */\nexport function normalizeModelCatalog({\n response,\n}: {\n response: AthenaModelCatalogResponse;\n}): AthenaModelOption[] {\n const isAdmin = response.getCurrentUserWithWorkspace.user.isAdmin;\n const approved = new Set(response.settings.workspace.approvedLargeLlms);\n const denied = new Set(response.settings.workspace.blacklistedLlms);\n const hasAllowlist = approved.size > 0;\n return response.availableModels\n .filter(\n (model) =>\n (!model.adminOnly || isAdmin) && (hasAllowlist ? approved.has(model.id) : !denied.has(model.id)),\n )\n .map(\n (model): AthenaModelOption => ({\n id: model.id,\n label: model.name,\n description: model.description,\n provider: model.provider,\n status: model.status,\n sortOrder: model.sortOrder,\n familyId: model.familyId,\n familyName: model.familyName,\n isFamilyDefault: model.isFamilyDefault,\n isFeatured: model.isFeatured,\n variantOrder: model.variantOrder,\n }),\n )\n .sort(\n (left, right) =>\n left.sortOrder - right.sortOrder ||\n left.variantOrder - right.variantOrder ||\n left.label.localeCompare(right.label),\n );\n}\n\n/** The published collab agents, named and sorted; assets with a blank id are dropped. */\nexport function normalizeAgentCatalog({\n assets,\n}: {\n assets: ReadonlyArray<{ id: string; title: string | null }>;\n}): AthenaAgentOption[] {\n return assets\n .filter((asset) => asset.id.trim().length > 0)\n .map((asset) => ({ id: asset.id, name: asset.title?.trim() || 'Untitled agent' }))\n .sort((left, right) => left.name.localeCompare(right.name));\n}\n\n/**\n * The model a picker shows as chosen: the stored choice while the catalog offers it, else the\n * default (unless retired), else a recommended family default, else any live model. While the\n * catalog is empty (loading, or unreachable) the stored choice or the default stands.\n */\nexport function resolveCatalogModelId({\n stored,\n defaultModelId,\n models,\n}: {\n stored: string | null;\n defaultModelId: string;\n models: readonly AthenaModelOption[];\n}): string {\n if (stored && models.some((model) => model.id === stored)) return stored;\n return (\n (\n models.find((model) => model.id === defaultModelId && model.status !== 'LEGACY') ??\n models.find((model) => model.status === 'RECOMMENDED' && model.isFamilyDefault) ??\n models.find((model) => model.status !== 'LEGACY' && model.isFamilyDefault) ??\n models.find((model) => model.status !== 'LEGACY') ??\n models[0]\n )?.id ?? (stored || defaultModelId)\n );\n}\n\n/** Models grouped under their provider's label, in catalog order. */\nexport function groupModelsByProvider({\n models,\n}: {\n models: readonly AthenaModelOption[];\n}): Array<{ provider: string; models: AthenaModelOption[] }> {\n const groups = new Map<string, AthenaModelOption[]>();\n for (const model of models) {\n const label = MODEL_PROVIDER_LABELS[model.provider] ?? model.provider;\n groups.set(label, [...(groups.get(label) ?? []), model]);\n }\n return Array.from(groups, ([provider, grouped]) => ({ provider, models: grouped }));\n}\n\n/** The provider's credentials as the SDK's GraphQL client takes them. */\nfunction useCatalogAuth(): MentionAuthContext {\n const config = useAthenaConfig();\n return useMemo<MentionAuthContext>(\n () => ({\n backendUrl: config.backendUrl,\n appUrl: config.appUrl,\n apiKey: config.apiKey,\n credentials: 'include',\n get accessToken() {\n return config.token ?? undefined;\n },\n getAccessToken: async () => (await config.getToken?.()) ?? undefined,\n renewAccessToken: async () => (await config.getToken?.({ force: true })) ?? undefined,\n }),\n [config],\n );\n}\n\n/**\n * A short fingerprint (two FNV-1a rounds, 64 bits) of a credential: it tells accounts apart in an\n * in-memory cache key without the credential itself, or anything it could be recovered from,\n * entering the key.\n */\nexport function credentialFingerprint({ credential }: { credential: string }): string {\n const round = (seed: number) => {\n let hash = seed;\n for (let index = 0; index < credential.length; index += 1) {\n hash ^= credential.charCodeAt(index);\n hash = Math.imul(hash, 0x01000193) >>> 0;\n }\n return hash.toString(16).padStart(8, '0');\n };\n return `${round(0x811c9dc5)}${round(0x01000193)}`;\n}\n\n/**\n * Who is asking, for the cache key: the catalog differs per account (admin-only models, the\n * workspace's lists, the agents shared with them), and a host can switch accounts while keeping its\n * query client. The signed-in user's id when the SDK knows it, else a fingerprint of the credential\n * in use. Never the credential itself.\n */\nfunction useCatalogPrincipal(): string {\n const config = useAthenaConfig();\n const userId = useContext(AthenaAuthContext)?.user?.userId;\n if (userId) return `user:${userId}`;\n if (config.apiKey) return `key:${credentialFingerprint({ credential: config.apiKey })}`;\n if (config.token) return `token:${credentialFingerprint({ credential: config.token })}`;\n return 'session';\n}\n\n/**\n * The host's QueryClient when there is one (the thread list provides one), else one of the\n * pickers' own, so the catalog hooks work with or without the thread list.\n */\nfunction useCatalogQueryClient(): QueryClient {\n const host = useContext(QueryClientContext);\n const [own] = useState(() => new QueryClient());\n return host ?? own;\n}\n\n/** The models the signed-in person may pick (`normalizeModelCatalog`). */\nexport function useAthenaModelCatalog() {\n return useModelCatalogQuery({\n auth: useCatalogAuth(),\n principal: useCatalogPrincipal(),\n queryClient: useCatalogQueryClient(),\n });\n}\n\n/** The model catalog's query on a given client (one client for every read a hook makes). */\nfunction useModelCatalogQuery({\n auth,\n principal,\n queryClient,\n}: {\n auth: MentionAuthContext;\n principal: string;\n queryClient: QueryClient;\n}) {\n return useQuery(\n {\n queryKey: ['athena-sdk', 'catalog', 'models', auth.backendUrl, principal],\n queryFn: async () =>\n normalizeModelCatalog({\n response: await fetchGraphQL<AthenaModelCatalogResponse>(auth, ATHENA_MODEL_CATALOG_QUERY),\n }),\n staleTime: 5 * 60 * 1000,\n },\n queryClient,\n );\n}\n\n/** The published collab agents the signed-in person can see (`normalizeAgentCatalog`). */\nexport function useAthenaAgentCatalog() {\n const auth = useCatalogAuth();\n const principal = useCatalogPrincipal();\n const queryClient = useCatalogQueryClient();\n return useQuery(\n {\n queryKey: ['athena-sdk', 'catalog', 'agents', auth.backendUrl, principal],\n queryFn: async () =>\n normalizeAgentCatalog({\n assets:\n (\n await fetchGraphQL<{ getAllAssets: Array<{ id: string; title: string | null }> }>(\n auth,\n ATHENA_AGENT_CATALOG_QUERY,\n )\n ).getAllAssets ?? [],\n }),\n staleTime: 60 * 1000,\n },\n queryClient,\n );\n}\n\nexport const ATHENA_AGENT_MODELS_QUERY = `\n query AthenaAgentModels($assetId: String!) {\n getAssetById(assetId: $assetId, withMetadata: true) { athenaMetadata }\n }\n`;\n\n/**\n * The `getAssetById` error codes that mean \"this person may not read the\n * agent\": no VIEW on it (`UNAUTHORIZED`, `FORBIDDEN`), or it lives in another\n * workspace (`WORKSPACE_MISMATCH`). Asking again answers the same.\n */\nconst AGENT_READ_DENIAL_CODES: ReadonlySet<string> = new Set([\n 'UNAUTHORIZED',\n 'FORBIDDEN',\n 'WORKSPACE_MISMATCH',\n]);\n\n/** What reading the agent's published models found: its models, or that this person cannot know them. */\nexport type AgentModelRead = { readable: true; source: AgentModelSource | null } | { readable: false };\n\n/**\n * Where `useAthenaAgentModelOptions` stands:\n *\n * - `idle`: no agent given;\n * - `loading`: the agent (or the model catalog that names its models) is being read;\n * - `ready`: the agent's published models are known;\n * - `unavailable`: this person cannot know them: no VIEW on the agent, no such\n * agent, or one never published (or whose published definition names no\n * model). Not an error: `allowAnyModel` stays `true`, so a picker keeps the\n * workspace's models;\n * - `error`: the read failed (network, server); `allowAnyModel` stays `true`.\n */\nexport type AthenaAgentModelOptionsStatus = 'idle' | 'loading' | 'ready' | 'unavailable' | 'error';\n\nexport interface AthenaAgentModelOptions {\n status: AthenaAgentModelOptionsStatus;\n /** The agent's model choices (see {@link AthenaAgentModelOption}); empty unless `ready`. */\n options: readonly AthenaAgentModelOption[];\n /**\n * The model the thread runs while it pins none: the channel's override, else\n * the agent's own model. `null` unless `ready`.\n */\n defaultModel: string | null;\n /**\n * Whether people may pick any workspace model with this agent: the agent's\n * \"Allow any workspace model\" switch (`allowAnyModel` in its published\n * definition). Only an explicit `false` restricts a picker to `options`;\n * absent, `null` or `true` (every agent saved before the switch) allow any,\n * and so does an agent this person cannot read. `true` unless `ready`.\n */\n allowAnyModel: boolean;\n}\n\nconst NO_AGENT_MODEL_OPTIONS: Readonly<\n Record<Exclude<AthenaAgentModelOptionsStatus, 'ready'>, AthenaAgentModelOptions>\n> = {\n idle: { status: 'idle', options: [], defaultModel: null, allowAnyModel: true },\n loading: { status: 'loading', options: [], defaultModel: null, allowAnyModel: true },\n unavailable: { status: 'unavailable', options: [], defaultModel: null, allowAnyModel: true },\n error: { status: 'error', options: [], defaultModel: null, allowAnyModel: true },\n};\n\n/** Whether a failed read is a denial: an answer, not a failure (nothing to retry or report). */\nfunction isAgentReadDenial(error: unknown): boolean {\n return (\n error instanceof AthenaGraphQLError &&\n error.code !== null &&\n AGENT_READ_DENIAL_CODES.has(error.code)\n );\n}\n\n/**\n * A selected collab agent's own model choices, and whether the agent restricts\n * a picker to them (`allowAnyModel: false`, the agent's \"Allow any workspace\n * model\" switch turned off). Restriction is opt-in per agent: with\n * `allowAnyModel` true (the default) a picker keeps the workspace's models and\n * can still show `defaultModel` as the agent's own; `ChatModelPicker` does\n * both.\n *\n * `agent` is the agent's asset id or its `collab_agent:<id>` ref (`null`:\n * none, `idle`). `channel` is the collab channel the chat runs on: its model\n * override is the default there. The rules are in `agent-model-options.ts`.\n *\n * The agent is read through `getAssetById(withMetadata: true)`, the\n * permission-checked asset read (VIEW on the agent), with the provider's\n * credentials: a person without VIEW gets `unavailable` with no options. The\n * model the thread pins comes from `useAthenaThreadModel()` (inside\n * `<AthenaProvider>`; none outside one): a pinned model outside the agent's\n * list is an option with `pinnedOutside`.\n *\n * Picking the `isDefault` option means \"no model\": hand the provider\n * `model={undefined}` so the agent's (or channel's) default runs, and a session\n * pinning another model is unpinned.\n *\n * While shown, the agent is read again every\n * {@link AGENT_MODELS_REFRESH_MS}: an author publishing new models elsewhere\n * invalidates nothing here.\n */\nexport function useAthenaAgentModelOptions({\n agent,\n channel = null,\n}: {\n agent: string | null | undefined;\n channel?: string | null;\n}): AthenaAgentModelOptions {\n return useAgentModelChoices({ agent, channel }).choices;\n}\n\n/** How often an agent's published models are read again while a hook shows them. */\nexport const AGENT_MODELS_REFRESH_MS = 60 * 1000;\n\n/**\n * The re-read cadence of an agent model read: every\n * {@link AGENT_MODELS_REFRESH_MS} while it answers, never for a denial (asking\n * again answers the same).\n */\nexport function agentModelsRefetchInterval({\n data,\n}: {\n data: AgentModelRead | undefined;\n}): number | false {\n return data !== undefined && !data.readable ? false : AGENT_MODELS_REFRESH_MS;\n}\n\n/**\n * {@link useAthenaAgentModelOptions} plus the catalog label of any model id,\n * from the same query client and catalog read (a picker needs both; reading\n * the catalog again through its own client would fetch it twice). Internal.\n */\nexport function useAgentModelChoices({\n agent,\n channel,\n}: {\n agent: string | null | undefined;\n channel: string | null;\n}): {\n choices: AthenaAgentModelOptions;\n modelLabel: (model: string) => string;\n /** The workspace model catalog, read through the same client as the agent. */\n catalog: ReturnType<typeof useModelCatalogQuery>;\n} {\n const assetId = agentAssetIdOf(agent);\n const auth = useCatalogAuth();\n const principal = useCatalogPrincipal();\n const queryClient = useCatalogQueryClient();\n const read = useQuery(\n {\n queryKey: ['athena-sdk', 'catalog', 'agent-models', auth.backendUrl, principal, assetId],\n queryFn: async (): Promise<AgentModelRead> => {\n try {\n const data = await fetchGraphQL<{ getAssetById: { athenaMetadata: unknown } | null }>(\n auth,\n ATHENA_AGENT_MODELS_QUERY,\n { assetId },\n );\n if (data.getAssetById === null) return { readable: false };\n return {\n readable: true,\n source: publishedAgentModelSource(data.getAssetById.athenaMetadata),\n };\n } catch (error) {\n if (isAgentReadDenial(error)) return { readable: false };\n throw error;\n }\n },\n enabled: assetId !== null,\n staleTime: AGENT_MODELS_REFRESH_MS,\n refetchInterval: (query) => agentModelsRefetchInterval({ data: query.state.data }),\n },\n queryClient,\n );\n const catalog = useModelCatalogQuery({ auth, principal, queryClient });\n const threadModel = useOptionalAthenaThreadModel();\n const pinnedModel =\n threadModel !== null && threadModel.ready && threadModel.configured ? threadModel.model : null;\n const trimmedChannel = channel?.trim() || null;\n const models = catalog.data;\n const catalogPending = catalog.isPending;\n const { isPending: readPending, data: readData } = read;\n const modelLabel = useCallback(\n (model: string) => models?.find((option) => option.id === model)?.label ?? model,\n [models],\n );\n const choices = useMemo((): AthenaAgentModelOptions => {\n if (assetId === null) return NO_AGENT_MODEL_OPTIONS.idle;\n // A failed re-read keeps the last answer; only a first read that failed is an error.\n if (readData === undefined) {\n return readPending ? NO_AGENT_MODEL_OPTIONS.loading : NO_AGENT_MODEL_OPTIONS.error;\n }\n if (!readData.readable || readData.source === null) return NO_AGENT_MODEL_OPTIONS.unavailable;\n // The catalog names the models: wait for it, unless it failed (ids then name them).\n if (catalogPending) return NO_AGENT_MODEL_OPTIONS.loading;\n return {\n status: 'ready',\n ...buildAgentModelOptions({\n source: readData.source,\n channel: trimmedChannel,\n pinnedModel,\n modelLabel,\n }),\n allowAnyModel: readData.source.allowAnyModel,\n };\n }, [assetId, readPending, readData, catalogPending, modelLabel, trimmedChannel, pinnedModel]);\n return useMemo(() => ({ choices, modelLabel, catalog }), [choices, modelLabel, catalog]);\n}\n"],"names":["useAthenaConfig","useMemo","useContext","AthenaAuthContext","QueryClientContext","useState","QueryClient","useQuery","fetchGraphQL","AthenaGraphQLError","agentAssetIdOf","publishedAgentModelSource","useOptionalAthenaThreadModel","useCallback","buildAgentModelOptions"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"runtime-catalog.cjs","sources":["../../src/catalog/runtime-catalog.ts"],"sourcesContent":["import { QueryClient, QueryClientContext, useQuery } from '@tanstack/react-query';\nimport { useCallback, useContext, useMemo, useState } from 'react';\nimport { AthenaAuthContext } from '../auth/AthenaAuthContext';\nimport {\n AthenaGraphQLError,\n fetchGraphQL,\n type MentionAuthContext,\n} from '../chat-ui/mentions/suggestions/data/api-client';\nimport { useAthenaConfig } from '../provider/AthenaContext';\nimport { useOptionalAthenaThreadModel } from '../threads/use-athena-thread-model';\nimport {\n type AgentModelSource,\n type AthenaAgentModelOption,\n agentAssetIdOf,\n buildAgentModelOptions,\n publishedAgentModelSource,\n} from './agent-model-options';\n\n/**\n * The workspace's runtime catalog for a chat's model and agent pickers: the models the signed-in\n * person may run (the workspace's allow or deny list, admin-only models for admins), and the\n * published collab agents they can see. The rules are the Chrome extension's, which every host now\n * reads from here.\n */\n\nexport type AthenaModelStatus = 'ACTIVE' | 'LEGACY' | 'RECOMMENDED';\n\nexport interface AthenaModelOption {\n id: string;\n label: string;\n description: string | null;\n /** Upper-case provider key, e.g. `ANTHROPIC`, `OPENAI`. */\n provider: string;\n status: AthenaModelStatus;\n sortOrder: number;\n familyId: string;\n familyName: string;\n isFamilyDefault: boolean;\n isFeatured: boolean;\n variantOrder: number;\n}\n\nexport interface AthenaAgentOption {\n /** The collab agent's asset id; pass `collab_agent:<id>` as the provider's `agent`. */\n id: string;\n name: string;\n}\n\n/** The provider labels a model picker groups by. */\nexport const MODEL_PROVIDER_LABELS: Readonly<Record<string, string>> = {\n ANTHROPIC: 'Anthropic',\n OPENAI: 'OpenAI',\n GOOGLE: 'Google',\n XAI: 'xAI',\n META: 'Meta',\n MISTRAL: 'Mistral',\n DEEPSEEK: 'DeepSeek',\n QWEN: 'Qwen',\n MOONSHOT: 'Moonshot',\n MINIMAX: 'MiniMax',\n ZAI: 'Z.ai',\n};\n\nexport const ATHENA_MODEL_CATALOG_QUERY = `\n query AthenaModelCatalog {\n getCurrentUserWithWorkspace { user { isAdmin } }\n settings { workspace { approvedLargeLlms blacklistedLlms } }\n availableModels {\n id name provider status description adminOnly sortOrder\n familyId familyName isFamilyDefault isFeatured variantOrder\n }\n }\n`;\n\nexport const ATHENA_AGENT_CATALOG_QUERY = `\n query AthenaAgentCatalog {\n getAllAssets(filterByAthenaOriginalType: [\"collab_agent\"]) { id title }\n }\n`;\n\nexport interface AthenaModelCatalogResponse {\n getCurrentUserWithWorkspace: { user: { isAdmin: boolean } };\n settings: { workspace: { approvedLargeLlms: string[]; blacklistedLlms: string[] } };\n availableModels: Array<{\n id: string;\n name: string;\n provider: string;\n status: AthenaModelStatus;\n description: string | null;\n adminOnly: boolean;\n sortOrder: number;\n familyId: string;\n familyName: string;\n isFamilyDefault: boolean;\n isFeatured: boolean;\n variantOrder: number;\n }>;\n}\n\n/**\n * The models the person may pick, in catalog order. A workspace with an allow list offers only\n * those; otherwise everything except its deny list. Admin-only models are offered to admins only.\n */\nexport function normalizeModelCatalog({\n response,\n}: {\n response: AthenaModelCatalogResponse;\n}): AthenaModelOption[] {\n const isAdmin = response.getCurrentUserWithWorkspace.user.isAdmin;\n const approved = new Set(response.settings.workspace.approvedLargeLlms);\n const denied = new Set(response.settings.workspace.blacklistedLlms);\n const hasAllowlist = approved.size > 0;\n return response.availableModels\n .filter(\n (model) =>\n (!model.adminOnly || isAdmin) && (hasAllowlist ? approved.has(model.id) : !denied.has(model.id)),\n )\n .map(\n (model): AthenaModelOption => ({\n id: model.id,\n label: model.name,\n description: model.description,\n provider: model.provider,\n status: model.status,\n sortOrder: model.sortOrder,\n familyId: model.familyId,\n familyName: model.familyName,\n isFamilyDefault: model.isFamilyDefault,\n isFeatured: model.isFeatured,\n variantOrder: model.variantOrder,\n }),\n )\n .sort(\n (left, right) =>\n left.sortOrder - right.sortOrder ||\n left.variantOrder - right.variantOrder ||\n left.label.localeCompare(right.label),\n );\n}\n\n/** The published collab agents, named and sorted; assets with a blank id are dropped. */\nexport function normalizeAgentCatalog({\n assets,\n}: {\n assets: ReadonlyArray<{ id: string; title: string | null }>;\n}): AthenaAgentOption[] {\n return assets\n .filter((asset) => asset.id.trim().length > 0)\n .map((asset) => ({ id: asset.id, name: asset.title?.trim() || 'Untitled agent' }))\n .sort((left, right) => left.name.localeCompare(right.name));\n}\n\n/**\n * The model a picker shows as chosen: the stored choice while the catalog offers it, else the\n * default (unless retired), else a recommended family default, else any live model. While the\n * catalog is empty (loading, or unreachable) the stored choice or the default stands.\n */\nexport function resolveCatalogModelId({\n stored,\n defaultModelId,\n models,\n}: {\n stored: string | null;\n defaultModelId: string;\n models: readonly AthenaModelOption[];\n}): string {\n if (stored && models.some((model) => model.id === stored)) return stored;\n return (\n (\n models.find((model) => model.id === defaultModelId && model.status !== 'LEGACY') ??\n models.find((model) => model.status === 'RECOMMENDED' && model.isFamilyDefault) ??\n models.find((model) => model.status !== 'LEGACY' && model.isFamilyDefault) ??\n models.find((model) => model.status !== 'LEGACY') ??\n models[0]\n )?.id ?? (stored || defaultModelId)\n );\n}\n\n/** Models grouped under their provider's label, in catalog order. */\nexport function groupModelsByProvider({\n models,\n}: {\n models: readonly AthenaModelOption[];\n}): Array<{ provider: string; models: AthenaModelOption[] }> {\n const groups = new Map<string, AthenaModelOption[]>();\n for (const model of models) {\n const label = MODEL_PROVIDER_LABELS[model.provider] ?? model.provider;\n groups.set(label, [...(groups.get(label) ?? []), model]);\n }\n return Array.from(groups, ([provider, grouped]) => ({ provider, models: grouped }));\n}\n\n/** The provider's credentials as the SDK's GraphQL client takes them. Internal. */\nexport function useCatalogAuth(): MentionAuthContext {\n const config = useAthenaConfig();\n return useMemo<MentionAuthContext>(\n () => ({\n backendUrl: config.backendUrl,\n appUrl: config.appUrl,\n apiKey: config.apiKey,\n credentials: 'include',\n get accessToken() {\n return config.token ?? undefined;\n },\n getAccessToken: async () => (await config.getToken?.()) ?? undefined,\n renewAccessToken: async () => (await config.getToken?.({ force: true })) ?? undefined,\n }),\n [config],\n );\n}\n\n/**\n * A short fingerprint (two FNV-1a rounds, 64 bits) of a credential: it tells accounts apart in an\n * in-memory cache key without the credential itself, or anything it could be recovered from,\n * entering the key.\n */\nexport function credentialFingerprint({ credential }: { credential: string }): string {\n const round = (seed: number) => {\n let hash = seed;\n for (let index = 0; index < credential.length; index += 1) {\n hash ^= credential.charCodeAt(index);\n hash = Math.imul(hash, 0x01000193) >>> 0;\n }\n return hash.toString(16).padStart(8, '0');\n };\n return `${round(0x811c9dc5)}${round(0x01000193)}`;\n}\n\n/**\n * Who is asking, for the cache key: the catalog differs per account (admin-only models, the\n * workspace's lists, the agents shared with them), and a host can switch accounts while keeping its\n * query client. The signed-in user's id when the SDK knows it, else a fingerprint of the credential\n * in use. Never the credential itself.\n */\nexport function useCatalogPrincipal(): string {\n const config = useAthenaConfig();\n const userId = useContext(AthenaAuthContext)?.user?.userId;\n if (userId) return `user:${userId}`;\n if (config.apiKey) return `key:${credentialFingerprint({ credential: config.apiKey })}`;\n if (config.token) return `token:${credentialFingerprint({ credential: config.token })}`;\n return 'session';\n}\n\n/**\n * The host's QueryClient when there is one (the thread list provides one), else one of the\n * pickers' own, so the catalog hooks work with or without the thread list.\n */\nexport function useCatalogQueryClient(): QueryClient {\n const host = useContext(QueryClientContext);\n const [own] = useState(() => new QueryClient());\n return host ?? own;\n}\n\n/** The models the signed-in person may pick (`normalizeModelCatalog`). */\nexport function useAthenaModelCatalog() {\n return useModelCatalogQuery({\n auth: useCatalogAuth(),\n principal: useCatalogPrincipal(),\n queryClient: useCatalogQueryClient(),\n });\n}\n\n/** The model catalog's query on a given client (one client for every read a hook makes). */\nfunction useModelCatalogQuery({\n auth,\n principal,\n queryClient,\n}: {\n auth: MentionAuthContext;\n principal: string;\n queryClient: QueryClient;\n}) {\n return useQuery(\n {\n queryKey: ['athena-sdk', 'catalog', 'models', auth.backendUrl, principal],\n queryFn: async () =>\n normalizeModelCatalog({\n response: await fetchGraphQL<AthenaModelCatalogResponse>(auth, ATHENA_MODEL_CATALOG_QUERY),\n }),\n staleTime: 5 * 60 * 1000,\n },\n queryClient,\n );\n}\n\n/** The published collab agents the signed-in person can see (`normalizeAgentCatalog`). */\nexport function useAthenaAgentCatalog() {\n const auth = useCatalogAuth();\n const principal = useCatalogPrincipal();\n const queryClient = useCatalogQueryClient();\n return useQuery(\n {\n queryKey: ['athena-sdk', 'catalog', 'agents', auth.backendUrl, principal],\n queryFn: async () =>\n normalizeAgentCatalog({\n assets:\n (\n await fetchGraphQL<{ getAllAssets: Array<{ id: string; title: string | null }> }>(\n auth,\n ATHENA_AGENT_CATALOG_QUERY,\n )\n ).getAllAssets ?? [],\n }),\n staleTime: 60 * 1000,\n },\n queryClient,\n );\n}\n\nexport const ATHENA_AGENT_MODELS_QUERY = `\n query AthenaAgentModels($assetId: String!) {\n getAssetById(assetId: $assetId, withMetadata: true) { athenaMetadata }\n }\n`;\n\n/**\n * The `getAssetById` error codes that mean \"this person may not read the\n * agent\": no VIEW on it (`UNAUTHORIZED`, `FORBIDDEN`), or it lives in another\n * workspace (`WORKSPACE_MISMATCH`). Asking again answers the same.\n */\nconst AGENT_READ_DENIAL_CODES: ReadonlySet<string> = new Set([\n 'UNAUTHORIZED',\n 'FORBIDDEN',\n 'WORKSPACE_MISMATCH',\n]);\n\n/** What reading the agent's published models found: its models, or that this person cannot know them. */\nexport type AgentModelRead = { readable: true; source: AgentModelSource | null } | { readable: false };\n\n/**\n * Where `useAthenaAgentModelOptions` stands:\n *\n * - `idle`: no agent given;\n * - `loading`: the agent (or the model catalog that names its models) is being read;\n * - `ready`: the agent's published models are known;\n * - `unavailable`: this person cannot know them: no VIEW on the agent, no such\n * agent, or one never published (or whose published definition names no\n * model). Not an error: `allowAnyModel` stays `true`, so a picker keeps the\n * workspace's models;\n * - `error`: the read failed (network, server); `allowAnyModel` stays `true`.\n */\nexport type AthenaAgentModelOptionsStatus = 'idle' | 'loading' | 'ready' | 'unavailable' | 'error';\n\nexport interface AthenaAgentModelOptions {\n status: AthenaAgentModelOptionsStatus;\n /** The agent's model choices (see {@link AthenaAgentModelOption}); empty unless `ready`. */\n options: readonly AthenaAgentModelOption[];\n /**\n * The model the thread runs while it pins none: the channel's override, else\n * the agent's own model. `null` unless `ready`.\n */\n defaultModel: string | null;\n /**\n * Whether people may pick any workspace model with this agent: the agent's\n * \"Allow any workspace model\" switch (`allowAnyModel` in its published\n * definition). Only an explicit `false` restricts a picker to `options`;\n * absent, `null` or `true` (every agent saved before the switch) allow any,\n * and so does an agent this person cannot read. `true` unless `ready`.\n */\n allowAnyModel: boolean;\n}\n\nconst NO_AGENT_MODEL_OPTIONS: Readonly<\n Record<Exclude<AthenaAgentModelOptionsStatus, 'ready'>, AthenaAgentModelOptions>\n> = {\n idle: { status: 'idle', options: [], defaultModel: null, allowAnyModel: true },\n loading: { status: 'loading', options: [], defaultModel: null, allowAnyModel: true },\n unavailable: { status: 'unavailable', options: [], defaultModel: null, allowAnyModel: true },\n error: { status: 'error', options: [], defaultModel: null, allowAnyModel: true },\n};\n\n/** Whether a failed read is a denial: an answer, not a failure (nothing to retry or report). */\nexport function isAgentReadDenial(error: unknown): boolean {\n return (\n error instanceof AthenaGraphQLError &&\n error.code !== null &&\n AGENT_READ_DENIAL_CODES.has(error.code)\n );\n}\n\n/**\n * A selected collab agent's own model choices, and whether the agent restricts\n * a picker to them (`allowAnyModel: false`, the agent's \"Allow any workspace\n * model\" switch turned off). Restriction is opt-in per agent: with\n * `allowAnyModel` true (the default) a picker keeps the workspace's models and\n * can still show `defaultModel` as the agent's own; `ChatModelPicker` does\n * both.\n *\n * `agent` is the agent's asset id or its `collab_agent:<id>` ref (`null`:\n * none, `idle`). `channel` is the collab channel the chat runs on: its model\n * override is the default there. The rules are in `agent-model-options.ts`.\n *\n * The agent is read through `getAssetById(withMetadata: true)`, the\n * permission-checked asset read (VIEW on the agent), with the provider's\n * credentials: a person without VIEW gets `unavailable` with no options. The\n * model the thread pins comes from `useAthenaThreadModel()` (inside\n * `<AthenaProvider>`; none outside one): a pinned model outside the agent's\n * list is an option with `pinnedOutside`.\n *\n * Picking the `isDefault` option means \"no model\": hand the provider\n * `model={undefined}` so the agent's (or channel's) default runs, and a session\n * pinning another model is unpinned.\n *\n * While shown, the agent is read again every\n * {@link AGENT_MODELS_REFRESH_MS}: an author publishing new models elsewhere\n * invalidates nothing here.\n */\nexport function useAthenaAgentModelOptions({\n agent,\n channel = null,\n}: {\n agent: string | null | undefined;\n channel?: string | null;\n}): AthenaAgentModelOptions {\n return useAgentModelChoices({ agent, channel }).choices;\n}\n\n/** How often an agent's published models are read again while a hook shows them. */\nexport const AGENT_MODELS_REFRESH_MS = 60 * 1000;\n\n/**\n * The re-read cadence of an agent model read: every\n * {@link AGENT_MODELS_REFRESH_MS} while it answers, never for a denial (asking\n * again answers the same).\n */\nexport function agentModelsRefetchInterval({\n data,\n}: {\n data: AgentModelRead | undefined;\n}): number | false {\n return data !== undefined && !data.readable ? false : AGENT_MODELS_REFRESH_MS;\n}\n\n/**\n * {@link useAthenaAgentModelOptions} plus the catalog label of any model id,\n * from the same query client and catalog read (a picker needs both; reading\n * the catalog again through its own client would fetch it twice). Internal.\n */\nexport function useAgentModelChoices({\n agent,\n channel,\n}: {\n agent: string | null | undefined;\n channel: string | null;\n}): {\n choices: AthenaAgentModelOptions;\n modelLabel: (model: string) => string;\n /** The workspace model catalog, read through the same client as the agent. */\n catalog: ReturnType<typeof useModelCatalogQuery>;\n} {\n const assetId = agentAssetIdOf(agent);\n const auth = useCatalogAuth();\n const principal = useCatalogPrincipal();\n const queryClient = useCatalogQueryClient();\n const read = useQuery(\n {\n queryKey: ['athena-sdk', 'catalog', 'agent-models', auth.backendUrl, principal, assetId],\n queryFn: async (): Promise<AgentModelRead> => {\n try {\n const data = await fetchGraphQL<{ getAssetById: { athenaMetadata: unknown } | null }>(\n auth,\n ATHENA_AGENT_MODELS_QUERY,\n { assetId },\n );\n if (data.getAssetById === null) return { readable: false };\n return {\n readable: true,\n source: publishedAgentModelSource(data.getAssetById.athenaMetadata),\n };\n } catch (error) {\n if (isAgentReadDenial(error)) return { readable: false };\n throw error;\n }\n },\n enabled: assetId !== null,\n staleTime: AGENT_MODELS_REFRESH_MS,\n refetchInterval: (query) => agentModelsRefetchInterval({ data: query.state.data }),\n },\n queryClient,\n );\n const catalog = useModelCatalogQuery({ auth, principal, queryClient });\n const threadModel = useOptionalAthenaThreadModel();\n const pinnedModel =\n threadModel !== null && threadModel.ready && threadModel.configured ? threadModel.model : null;\n const trimmedChannel = channel?.trim() || null;\n const models = catalog.data;\n const catalogPending = catalog.isPending;\n const { isPending: readPending, data: readData } = read;\n const modelLabel = useCallback(\n (model: string) => models?.find((option) => option.id === model)?.label ?? model,\n [models],\n );\n const choices = useMemo((): AthenaAgentModelOptions => {\n if (assetId === null) return NO_AGENT_MODEL_OPTIONS.idle;\n // A failed re-read keeps the last answer; only a first read that failed is an error.\n if (readData === undefined) {\n return readPending ? NO_AGENT_MODEL_OPTIONS.loading : NO_AGENT_MODEL_OPTIONS.error;\n }\n if (!readData.readable || readData.source === null) return NO_AGENT_MODEL_OPTIONS.unavailable;\n // The catalog names the models: wait for it, unless it failed (ids then name them).\n if (catalogPending) return NO_AGENT_MODEL_OPTIONS.loading;\n return {\n status: 'ready',\n ...buildAgentModelOptions({\n source: readData.source,\n channel: trimmedChannel,\n pinnedModel,\n modelLabel,\n }),\n allowAnyModel: readData.source.allowAnyModel,\n };\n }, [assetId, readPending, readData, catalogPending, modelLabel, trimmedChannel, pinnedModel]);\n return useMemo(() => ({ choices, modelLabel, catalog }), [choices, modelLabel, catalog]);\n}\n"],"names":["useAthenaConfig","useMemo","useContext","AthenaAuthContext","QueryClientContext","useState","QueryClient","useQuery","fetchGraphQL","AthenaGraphQLError","agentAssetIdOf","publishedAgentModelSource","useOptionalAthenaThreadModel","useCallback","buildAgentModelOptions"],"mappings":"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"}
|
|
@@ -89,6 +89,8 @@ export declare function groupModelsByProvider({ models, }: {
|
|
|
89
89
|
provider: string;
|
|
90
90
|
models: AthenaModelOption[];
|
|
91
91
|
}>;
|
|
92
|
+
/** The provider's credentials as the SDK's GraphQL client takes them. Internal. */
|
|
93
|
+
export declare function useCatalogAuth(): MentionAuthContext;
|
|
92
94
|
/**
|
|
93
95
|
* A short fingerprint (two FNV-1a rounds, 64 bits) of a credential: it tells accounts apart in an
|
|
94
96
|
* in-memory cache key without the credential itself, or anything it could be recovered from,
|
|
@@ -97,6 +99,18 @@ export declare function groupModelsByProvider({ models, }: {
|
|
|
97
99
|
export declare function credentialFingerprint({ credential }: {
|
|
98
100
|
credential: string;
|
|
99
101
|
}): string;
|
|
102
|
+
/**
|
|
103
|
+
* Who is asking, for the cache key: the catalog differs per account (admin-only models, the
|
|
104
|
+
* workspace's lists, the agents shared with them), and a host can switch accounts while keeping its
|
|
105
|
+
* query client. The signed-in user's id when the SDK knows it, else a fingerprint of the credential
|
|
106
|
+
* in use. Never the credential itself.
|
|
107
|
+
*/
|
|
108
|
+
export declare function useCatalogPrincipal(): string;
|
|
109
|
+
/**
|
|
110
|
+
* The host's QueryClient when there is one (the thread list provides one), else one of the
|
|
111
|
+
* pickers' own, so the catalog hooks work with or without the thread list.
|
|
112
|
+
*/
|
|
113
|
+
export declare function useCatalogQueryClient(): QueryClient;
|
|
100
114
|
/** The models the signed-in person may pick (`normalizeModelCatalog`). */
|
|
101
115
|
export declare function useAthenaModelCatalog(): import("@tanstack/react-query").UseQueryResult<AthenaModelOption[], Error>;
|
|
102
116
|
/** The model catalog's query on a given client (one client for every read a hook makes). */
|
|
@@ -146,6 +160,8 @@ export interface AthenaAgentModelOptions {
|
|
|
146
160
|
*/
|
|
147
161
|
allowAnyModel: boolean;
|
|
148
162
|
}
|
|
163
|
+
/** Whether a failed read is a denial: an answer, not a failure (nothing to retry or report). */
|
|
164
|
+
export declare function isAgentReadDenial(error: unknown): boolean;
|
|
149
165
|
/**
|
|
150
166
|
* A selected collab agent's own model choices, and whether the agent restricts
|
|
151
167
|
* a picker to them (`allowAnyModel: false`, the agent's "Allow any workspace
|
|
@@ -284,12 +284,16 @@ export {
|
|
|
284
284
|
agentModelsRefetchInterval,
|
|
285
285
|
credentialFingerprint,
|
|
286
286
|
groupModelsByProvider,
|
|
287
|
+
isAgentReadDenial,
|
|
287
288
|
normalizeAgentCatalog,
|
|
288
289
|
normalizeModelCatalog,
|
|
289
290
|
resolveCatalogModelId,
|
|
290
291
|
useAgentModelChoices,
|
|
291
292
|
useAthenaAgentCatalog,
|
|
292
293
|
useAthenaAgentModelOptions,
|
|
293
|
-
useAthenaModelCatalog
|
|
294
|
+
useAthenaModelCatalog,
|
|
295
|
+
useCatalogAuth,
|
|
296
|
+
useCatalogPrincipal,
|
|
297
|
+
useCatalogQueryClient
|
|
294
298
|
};
|
|
295
299
|
//# sourceMappingURL=runtime-catalog.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-catalog.js","sources":["../../src/catalog/runtime-catalog.ts"],"sourcesContent":["import { QueryClient, QueryClientContext, useQuery } from '@tanstack/react-query';\nimport { useCallback, useContext, useMemo, useState } from 'react';\nimport { AthenaAuthContext } from '../auth/AthenaAuthContext';\nimport {\n AthenaGraphQLError,\n fetchGraphQL,\n type MentionAuthContext,\n} from '../chat-ui/mentions/suggestions/data/api-client';\nimport { useAthenaConfig } from '../provider/AthenaContext';\nimport { useOptionalAthenaThreadModel } from '../threads/use-athena-thread-model';\nimport {\n type AgentModelSource,\n type AthenaAgentModelOption,\n agentAssetIdOf,\n buildAgentModelOptions,\n publishedAgentModelSource,\n} from './agent-model-options';\n\n/**\n * The workspace's runtime catalog for a chat's model and agent pickers: the models the signed-in\n * person may run (the workspace's allow or deny list, admin-only models for admins), and the\n * published collab agents they can see. The rules are the Chrome extension's, which every host now\n * reads from here.\n */\n\nexport type AthenaModelStatus = 'ACTIVE' | 'LEGACY' | 'RECOMMENDED';\n\nexport interface AthenaModelOption {\n id: string;\n label: string;\n description: string | null;\n /** Upper-case provider key, e.g. `ANTHROPIC`, `OPENAI`. */\n provider: string;\n status: AthenaModelStatus;\n sortOrder: number;\n familyId: string;\n familyName: string;\n isFamilyDefault: boolean;\n isFeatured: boolean;\n variantOrder: number;\n}\n\nexport interface AthenaAgentOption {\n /** The collab agent's asset id; pass `collab_agent:<id>` as the provider's `agent`. */\n id: string;\n name: string;\n}\n\n/** The provider labels a model picker groups by. */\nexport const MODEL_PROVIDER_LABELS: Readonly<Record<string, string>> = {\n ANTHROPIC: 'Anthropic',\n OPENAI: 'OpenAI',\n GOOGLE: 'Google',\n XAI: 'xAI',\n META: 'Meta',\n MISTRAL: 'Mistral',\n DEEPSEEK: 'DeepSeek',\n QWEN: 'Qwen',\n MOONSHOT: 'Moonshot',\n MINIMAX: 'MiniMax',\n ZAI: 'Z.ai',\n};\n\nexport const ATHENA_MODEL_CATALOG_QUERY = `\n query AthenaModelCatalog {\n getCurrentUserWithWorkspace { user { isAdmin } }\n settings { workspace { approvedLargeLlms blacklistedLlms } }\n availableModels {\n id name provider status description adminOnly sortOrder\n familyId familyName isFamilyDefault isFeatured variantOrder\n }\n }\n`;\n\nexport const ATHENA_AGENT_CATALOG_QUERY = `\n query AthenaAgentCatalog {\n getAllAssets(filterByAthenaOriginalType: [\"collab_agent\"]) { id title }\n }\n`;\n\nexport interface AthenaModelCatalogResponse {\n getCurrentUserWithWorkspace: { user: { isAdmin: boolean } };\n settings: { workspace: { approvedLargeLlms: string[]; blacklistedLlms: string[] } };\n availableModels: Array<{\n id: string;\n name: string;\n provider: string;\n status: AthenaModelStatus;\n description: string | null;\n adminOnly: boolean;\n sortOrder: number;\n familyId: string;\n familyName: string;\n isFamilyDefault: boolean;\n isFeatured: boolean;\n variantOrder: number;\n }>;\n}\n\n/**\n * The models the person may pick, in catalog order. A workspace with an allow list offers only\n * those; otherwise everything except its deny list. Admin-only models are offered to admins only.\n */\nexport function normalizeModelCatalog({\n response,\n}: {\n response: AthenaModelCatalogResponse;\n}): AthenaModelOption[] {\n const isAdmin = response.getCurrentUserWithWorkspace.user.isAdmin;\n const approved = new Set(response.settings.workspace.approvedLargeLlms);\n const denied = new Set(response.settings.workspace.blacklistedLlms);\n const hasAllowlist = approved.size > 0;\n return response.availableModels\n .filter(\n (model) =>\n (!model.adminOnly || isAdmin) && (hasAllowlist ? approved.has(model.id) : !denied.has(model.id)),\n )\n .map(\n (model): AthenaModelOption => ({\n id: model.id,\n label: model.name,\n description: model.description,\n provider: model.provider,\n status: model.status,\n sortOrder: model.sortOrder,\n familyId: model.familyId,\n familyName: model.familyName,\n isFamilyDefault: model.isFamilyDefault,\n isFeatured: model.isFeatured,\n variantOrder: model.variantOrder,\n }),\n )\n .sort(\n (left, right) =>\n left.sortOrder - right.sortOrder ||\n left.variantOrder - right.variantOrder ||\n left.label.localeCompare(right.label),\n );\n}\n\n/** The published collab agents, named and sorted; assets with a blank id are dropped. */\nexport function normalizeAgentCatalog({\n assets,\n}: {\n assets: ReadonlyArray<{ id: string; title: string | null }>;\n}): AthenaAgentOption[] {\n return assets\n .filter((asset) => asset.id.trim().length > 0)\n .map((asset) => ({ id: asset.id, name: asset.title?.trim() || 'Untitled agent' }))\n .sort((left, right) => left.name.localeCompare(right.name));\n}\n\n/**\n * The model a picker shows as chosen: the stored choice while the catalog offers it, else the\n * default (unless retired), else a recommended family default, else any live model. While the\n * catalog is empty (loading, or unreachable) the stored choice or the default stands.\n */\nexport function resolveCatalogModelId({\n stored,\n defaultModelId,\n models,\n}: {\n stored: string | null;\n defaultModelId: string;\n models: readonly AthenaModelOption[];\n}): string {\n if (stored && models.some((model) => model.id === stored)) return stored;\n return (\n (\n models.find((model) => model.id === defaultModelId && model.status !== 'LEGACY') ??\n models.find((model) => model.status === 'RECOMMENDED' && model.isFamilyDefault) ??\n models.find((model) => model.status !== 'LEGACY' && model.isFamilyDefault) ??\n models.find((model) => model.status !== 'LEGACY') ??\n models[0]\n )?.id ?? (stored || defaultModelId)\n );\n}\n\n/** Models grouped under their provider's label, in catalog order. */\nexport function groupModelsByProvider({\n models,\n}: {\n models: readonly AthenaModelOption[];\n}): Array<{ provider: string; models: AthenaModelOption[] }> {\n const groups = new Map<string, AthenaModelOption[]>();\n for (const model of models) {\n const label = MODEL_PROVIDER_LABELS[model.provider] ?? model.provider;\n groups.set(label, [...(groups.get(label) ?? []), model]);\n }\n return Array.from(groups, ([provider, grouped]) => ({ provider, models: grouped }));\n}\n\n/** The provider's credentials as the SDK's GraphQL client takes them. */\nfunction useCatalogAuth(): MentionAuthContext {\n const config = useAthenaConfig();\n return useMemo<MentionAuthContext>(\n () => ({\n backendUrl: config.backendUrl,\n appUrl: config.appUrl,\n apiKey: config.apiKey,\n credentials: 'include',\n get accessToken() {\n return config.token ?? undefined;\n },\n getAccessToken: async () => (await config.getToken?.()) ?? undefined,\n renewAccessToken: async () => (await config.getToken?.({ force: true })) ?? undefined,\n }),\n [config],\n );\n}\n\n/**\n * A short fingerprint (two FNV-1a rounds, 64 bits) of a credential: it tells accounts apart in an\n * in-memory cache key without the credential itself, or anything it could be recovered from,\n * entering the key.\n */\nexport function credentialFingerprint({ credential }: { credential: string }): string {\n const round = (seed: number) => {\n let hash = seed;\n for (let index = 0; index < credential.length; index += 1) {\n hash ^= credential.charCodeAt(index);\n hash = Math.imul(hash, 0x01000193) >>> 0;\n }\n return hash.toString(16).padStart(8, '0');\n };\n return `${round(0x811c9dc5)}${round(0x01000193)}`;\n}\n\n/**\n * Who is asking, for the cache key: the catalog differs per account (admin-only models, the\n * workspace's lists, the agents shared with them), and a host can switch accounts while keeping its\n * query client. The signed-in user's id when the SDK knows it, else a fingerprint of the credential\n * in use. Never the credential itself.\n */\nfunction useCatalogPrincipal(): string {\n const config = useAthenaConfig();\n const userId = useContext(AthenaAuthContext)?.user?.userId;\n if (userId) return `user:${userId}`;\n if (config.apiKey) return `key:${credentialFingerprint({ credential: config.apiKey })}`;\n if (config.token) return `token:${credentialFingerprint({ credential: config.token })}`;\n return 'session';\n}\n\n/**\n * The host's QueryClient when there is one (the thread list provides one), else one of the\n * pickers' own, so the catalog hooks work with or without the thread list.\n */\nfunction useCatalogQueryClient(): QueryClient {\n const host = useContext(QueryClientContext);\n const [own] = useState(() => new QueryClient());\n return host ?? own;\n}\n\n/** The models the signed-in person may pick (`normalizeModelCatalog`). */\nexport function useAthenaModelCatalog() {\n return useModelCatalogQuery({\n auth: useCatalogAuth(),\n principal: useCatalogPrincipal(),\n queryClient: useCatalogQueryClient(),\n });\n}\n\n/** The model catalog's query on a given client (one client for every read a hook makes). */\nfunction useModelCatalogQuery({\n auth,\n principal,\n queryClient,\n}: {\n auth: MentionAuthContext;\n principal: string;\n queryClient: QueryClient;\n}) {\n return useQuery(\n {\n queryKey: ['athena-sdk', 'catalog', 'models', auth.backendUrl, principal],\n queryFn: async () =>\n normalizeModelCatalog({\n response: await fetchGraphQL<AthenaModelCatalogResponse>(auth, ATHENA_MODEL_CATALOG_QUERY),\n }),\n staleTime: 5 * 60 * 1000,\n },\n queryClient,\n );\n}\n\n/** The published collab agents the signed-in person can see (`normalizeAgentCatalog`). */\nexport function useAthenaAgentCatalog() {\n const auth = useCatalogAuth();\n const principal = useCatalogPrincipal();\n const queryClient = useCatalogQueryClient();\n return useQuery(\n {\n queryKey: ['athena-sdk', 'catalog', 'agents', auth.backendUrl, principal],\n queryFn: async () =>\n normalizeAgentCatalog({\n assets:\n (\n await fetchGraphQL<{ getAllAssets: Array<{ id: string; title: string | null }> }>(\n auth,\n ATHENA_AGENT_CATALOG_QUERY,\n )\n ).getAllAssets ?? [],\n }),\n staleTime: 60 * 1000,\n },\n queryClient,\n );\n}\n\nexport const ATHENA_AGENT_MODELS_QUERY = `\n query AthenaAgentModels($assetId: String!) {\n getAssetById(assetId: $assetId, withMetadata: true) { athenaMetadata }\n }\n`;\n\n/**\n * The `getAssetById` error codes that mean \"this person may not read the\n * agent\": no VIEW on it (`UNAUTHORIZED`, `FORBIDDEN`), or it lives in another\n * workspace (`WORKSPACE_MISMATCH`). Asking again answers the same.\n */\nconst AGENT_READ_DENIAL_CODES: ReadonlySet<string> = new Set([\n 'UNAUTHORIZED',\n 'FORBIDDEN',\n 'WORKSPACE_MISMATCH',\n]);\n\n/** What reading the agent's published models found: its models, or that this person cannot know them. */\nexport type AgentModelRead = { readable: true; source: AgentModelSource | null } | { readable: false };\n\n/**\n * Where `useAthenaAgentModelOptions` stands:\n *\n * - `idle`: no agent given;\n * - `loading`: the agent (or the model catalog that names its models) is being read;\n * - `ready`: the agent's published models are known;\n * - `unavailable`: this person cannot know them: no VIEW on the agent, no such\n * agent, or one never published (or whose published definition names no\n * model). Not an error: `allowAnyModel` stays `true`, so a picker keeps the\n * workspace's models;\n * - `error`: the read failed (network, server); `allowAnyModel` stays `true`.\n */\nexport type AthenaAgentModelOptionsStatus = 'idle' | 'loading' | 'ready' | 'unavailable' | 'error';\n\nexport interface AthenaAgentModelOptions {\n status: AthenaAgentModelOptionsStatus;\n /** The agent's model choices (see {@link AthenaAgentModelOption}); empty unless `ready`. */\n options: readonly AthenaAgentModelOption[];\n /**\n * The model the thread runs while it pins none: the channel's override, else\n * the agent's own model. `null` unless `ready`.\n */\n defaultModel: string | null;\n /**\n * Whether people may pick any workspace model with this agent: the agent's\n * \"Allow any workspace model\" switch (`allowAnyModel` in its published\n * definition). Only an explicit `false` restricts a picker to `options`;\n * absent, `null` or `true` (every agent saved before the switch) allow any,\n * and so does an agent this person cannot read. `true` unless `ready`.\n */\n allowAnyModel: boolean;\n}\n\nconst NO_AGENT_MODEL_OPTIONS: Readonly<\n Record<Exclude<AthenaAgentModelOptionsStatus, 'ready'>, AthenaAgentModelOptions>\n> = {\n idle: { status: 'idle', options: [], defaultModel: null, allowAnyModel: true },\n loading: { status: 'loading', options: [], defaultModel: null, allowAnyModel: true },\n unavailable: { status: 'unavailable', options: [], defaultModel: null, allowAnyModel: true },\n error: { status: 'error', options: [], defaultModel: null, allowAnyModel: true },\n};\n\n/** Whether a failed read is a denial: an answer, not a failure (nothing to retry or report). */\nfunction isAgentReadDenial(error: unknown): boolean {\n return (\n error instanceof AthenaGraphQLError &&\n error.code !== null &&\n AGENT_READ_DENIAL_CODES.has(error.code)\n );\n}\n\n/**\n * A selected collab agent's own model choices, and whether the agent restricts\n * a picker to them (`allowAnyModel: false`, the agent's \"Allow any workspace\n * model\" switch turned off). Restriction is opt-in per agent: with\n * `allowAnyModel` true (the default) a picker keeps the workspace's models and\n * can still show `defaultModel` as the agent's own; `ChatModelPicker` does\n * both.\n *\n * `agent` is the agent's asset id or its `collab_agent:<id>` ref (`null`:\n * none, `idle`). `channel` is the collab channel the chat runs on: its model\n * override is the default there. The rules are in `agent-model-options.ts`.\n *\n * The agent is read through `getAssetById(withMetadata: true)`, the\n * permission-checked asset read (VIEW on the agent), with the provider's\n * credentials: a person without VIEW gets `unavailable` with no options. The\n * model the thread pins comes from `useAthenaThreadModel()` (inside\n * `<AthenaProvider>`; none outside one): a pinned model outside the agent's\n * list is an option with `pinnedOutside`.\n *\n * Picking the `isDefault` option means \"no model\": hand the provider\n * `model={undefined}` so the agent's (or channel's) default runs, and a session\n * pinning another model is unpinned.\n *\n * While shown, the agent is read again every\n * {@link AGENT_MODELS_REFRESH_MS}: an author publishing new models elsewhere\n * invalidates nothing here.\n */\nexport function useAthenaAgentModelOptions({\n agent,\n channel = null,\n}: {\n agent: string | null | undefined;\n channel?: string | null;\n}): AthenaAgentModelOptions {\n return useAgentModelChoices({ agent, channel }).choices;\n}\n\n/** How often an agent's published models are read again while a hook shows them. */\nexport const AGENT_MODELS_REFRESH_MS = 60 * 1000;\n\n/**\n * The re-read cadence of an agent model read: every\n * {@link AGENT_MODELS_REFRESH_MS} while it answers, never for a denial (asking\n * again answers the same).\n */\nexport function agentModelsRefetchInterval({\n data,\n}: {\n data: AgentModelRead | undefined;\n}): number | false {\n return data !== undefined && !data.readable ? false : AGENT_MODELS_REFRESH_MS;\n}\n\n/**\n * {@link useAthenaAgentModelOptions} plus the catalog label of any model id,\n * from the same query client and catalog read (a picker needs both; reading\n * the catalog again through its own client would fetch it twice). Internal.\n */\nexport function useAgentModelChoices({\n agent,\n channel,\n}: {\n agent: string | null | undefined;\n channel: string | null;\n}): {\n choices: AthenaAgentModelOptions;\n modelLabel: (model: string) => string;\n /** The workspace model catalog, read through the same client as the agent. */\n catalog: ReturnType<typeof useModelCatalogQuery>;\n} {\n const assetId = agentAssetIdOf(agent);\n const auth = useCatalogAuth();\n const principal = useCatalogPrincipal();\n const queryClient = useCatalogQueryClient();\n const read = useQuery(\n {\n queryKey: ['athena-sdk', 'catalog', 'agent-models', auth.backendUrl, principal, assetId],\n queryFn: async (): Promise<AgentModelRead> => {\n try {\n const data = await fetchGraphQL<{ getAssetById: { athenaMetadata: unknown } | null }>(\n auth,\n ATHENA_AGENT_MODELS_QUERY,\n { assetId },\n );\n if (data.getAssetById === null) return { readable: false };\n return {\n readable: true,\n source: publishedAgentModelSource(data.getAssetById.athenaMetadata),\n };\n } catch (error) {\n if (isAgentReadDenial(error)) return { readable: false };\n throw error;\n }\n },\n enabled: assetId !== null,\n staleTime: AGENT_MODELS_REFRESH_MS,\n refetchInterval: (query) => agentModelsRefetchInterval({ data: query.state.data }),\n },\n queryClient,\n );\n const catalog = useModelCatalogQuery({ auth, principal, queryClient });\n const threadModel = useOptionalAthenaThreadModel();\n const pinnedModel =\n threadModel !== null && threadModel.ready && threadModel.configured ? threadModel.model : null;\n const trimmedChannel = channel?.trim() || null;\n const models = catalog.data;\n const catalogPending = catalog.isPending;\n const { isPending: readPending, data: readData } = read;\n const modelLabel = useCallback(\n (model: string) => models?.find((option) => option.id === model)?.label ?? model,\n [models],\n );\n const choices = useMemo((): AthenaAgentModelOptions => {\n if (assetId === null) return NO_AGENT_MODEL_OPTIONS.idle;\n // A failed re-read keeps the last answer; only a first read that failed is an error.\n if (readData === undefined) {\n return readPending ? NO_AGENT_MODEL_OPTIONS.loading : NO_AGENT_MODEL_OPTIONS.error;\n }\n if (!readData.readable || readData.source === null) return NO_AGENT_MODEL_OPTIONS.unavailable;\n // The catalog names the models: wait for it, unless it failed (ids then name them).\n if (catalogPending) return NO_AGENT_MODEL_OPTIONS.loading;\n return {\n status: 'ready',\n ...buildAgentModelOptions({\n source: readData.source,\n channel: trimmedChannel,\n pinnedModel,\n modelLabel,\n }),\n allowAnyModel: readData.source.allowAnyModel,\n };\n }, [assetId, readPending, readData, catalogPending, modelLabel, trimmedChannel, pinnedModel]);\n return useMemo(() => ({ choices, modelLabel, catalog }), [choices, modelLabel, catalog]);\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"runtime-catalog.js","sources":["../../src/catalog/runtime-catalog.ts"],"sourcesContent":["import { QueryClient, QueryClientContext, useQuery } from '@tanstack/react-query';\nimport { useCallback, useContext, useMemo, useState } from 'react';\nimport { AthenaAuthContext } from '../auth/AthenaAuthContext';\nimport {\n AthenaGraphQLError,\n fetchGraphQL,\n type MentionAuthContext,\n} from '../chat-ui/mentions/suggestions/data/api-client';\nimport { useAthenaConfig } from '../provider/AthenaContext';\nimport { useOptionalAthenaThreadModel } from '../threads/use-athena-thread-model';\nimport {\n type AgentModelSource,\n type AthenaAgentModelOption,\n agentAssetIdOf,\n buildAgentModelOptions,\n publishedAgentModelSource,\n} from './agent-model-options';\n\n/**\n * The workspace's runtime catalog for a chat's model and agent pickers: the models the signed-in\n * person may run (the workspace's allow or deny list, admin-only models for admins), and the\n * published collab agents they can see. The rules are the Chrome extension's, which every host now\n * reads from here.\n */\n\nexport type AthenaModelStatus = 'ACTIVE' | 'LEGACY' | 'RECOMMENDED';\n\nexport interface AthenaModelOption {\n id: string;\n label: string;\n description: string | null;\n /** Upper-case provider key, e.g. `ANTHROPIC`, `OPENAI`. */\n provider: string;\n status: AthenaModelStatus;\n sortOrder: number;\n familyId: string;\n familyName: string;\n isFamilyDefault: boolean;\n isFeatured: boolean;\n variantOrder: number;\n}\n\nexport interface AthenaAgentOption {\n /** The collab agent's asset id; pass `collab_agent:<id>` as the provider's `agent`. */\n id: string;\n name: string;\n}\n\n/** The provider labels a model picker groups by. */\nexport const MODEL_PROVIDER_LABELS: Readonly<Record<string, string>> = {\n ANTHROPIC: 'Anthropic',\n OPENAI: 'OpenAI',\n GOOGLE: 'Google',\n XAI: 'xAI',\n META: 'Meta',\n MISTRAL: 'Mistral',\n DEEPSEEK: 'DeepSeek',\n QWEN: 'Qwen',\n MOONSHOT: 'Moonshot',\n MINIMAX: 'MiniMax',\n ZAI: 'Z.ai',\n};\n\nexport const ATHENA_MODEL_CATALOG_QUERY = `\n query AthenaModelCatalog {\n getCurrentUserWithWorkspace { user { isAdmin } }\n settings { workspace { approvedLargeLlms blacklistedLlms } }\n availableModels {\n id name provider status description adminOnly sortOrder\n familyId familyName isFamilyDefault isFeatured variantOrder\n }\n }\n`;\n\nexport const ATHENA_AGENT_CATALOG_QUERY = `\n query AthenaAgentCatalog {\n getAllAssets(filterByAthenaOriginalType: [\"collab_agent\"]) { id title }\n }\n`;\n\nexport interface AthenaModelCatalogResponse {\n getCurrentUserWithWorkspace: { user: { isAdmin: boolean } };\n settings: { workspace: { approvedLargeLlms: string[]; blacklistedLlms: string[] } };\n availableModels: Array<{\n id: string;\n name: string;\n provider: string;\n status: AthenaModelStatus;\n description: string | null;\n adminOnly: boolean;\n sortOrder: number;\n familyId: string;\n familyName: string;\n isFamilyDefault: boolean;\n isFeatured: boolean;\n variantOrder: number;\n }>;\n}\n\n/**\n * The models the person may pick, in catalog order. A workspace with an allow list offers only\n * those; otherwise everything except its deny list. Admin-only models are offered to admins only.\n */\nexport function normalizeModelCatalog({\n response,\n}: {\n response: AthenaModelCatalogResponse;\n}): AthenaModelOption[] {\n const isAdmin = response.getCurrentUserWithWorkspace.user.isAdmin;\n const approved = new Set(response.settings.workspace.approvedLargeLlms);\n const denied = new Set(response.settings.workspace.blacklistedLlms);\n const hasAllowlist = approved.size > 0;\n return response.availableModels\n .filter(\n (model) =>\n (!model.adminOnly || isAdmin) && (hasAllowlist ? approved.has(model.id) : !denied.has(model.id)),\n )\n .map(\n (model): AthenaModelOption => ({\n id: model.id,\n label: model.name,\n description: model.description,\n provider: model.provider,\n status: model.status,\n sortOrder: model.sortOrder,\n familyId: model.familyId,\n familyName: model.familyName,\n isFamilyDefault: model.isFamilyDefault,\n isFeatured: model.isFeatured,\n variantOrder: model.variantOrder,\n }),\n )\n .sort(\n (left, right) =>\n left.sortOrder - right.sortOrder ||\n left.variantOrder - right.variantOrder ||\n left.label.localeCompare(right.label),\n );\n}\n\n/** The published collab agents, named and sorted; assets with a blank id are dropped. */\nexport function normalizeAgentCatalog({\n assets,\n}: {\n assets: ReadonlyArray<{ id: string; title: string | null }>;\n}): AthenaAgentOption[] {\n return assets\n .filter((asset) => asset.id.trim().length > 0)\n .map((asset) => ({ id: asset.id, name: asset.title?.trim() || 'Untitled agent' }))\n .sort((left, right) => left.name.localeCompare(right.name));\n}\n\n/**\n * The model a picker shows as chosen: the stored choice while the catalog offers it, else the\n * default (unless retired), else a recommended family default, else any live model. While the\n * catalog is empty (loading, or unreachable) the stored choice or the default stands.\n */\nexport function resolveCatalogModelId({\n stored,\n defaultModelId,\n models,\n}: {\n stored: string | null;\n defaultModelId: string;\n models: readonly AthenaModelOption[];\n}): string {\n if (stored && models.some((model) => model.id === stored)) return stored;\n return (\n (\n models.find((model) => model.id === defaultModelId && model.status !== 'LEGACY') ??\n models.find((model) => model.status === 'RECOMMENDED' && model.isFamilyDefault) ??\n models.find((model) => model.status !== 'LEGACY' && model.isFamilyDefault) ??\n models.find((model) => model.status !== 'LEGACY') ??\n models[0]\n )?.id ?? (stored || defaultModelId)\n );\n}\n\n/** Models grouped under their provider's label, in catalog order. */\nexport function groupModelsByProvider({\n models,\n}: {\n models: readonly AthenaModelOption[];\n}): Array<{ provider: string; models: AthenaModelOption[] }> {\n const groups = new Map<string, AthenaModelOption[]>();\n for (const model of models) {\n const label = MODEL_PROVIDER_LABELS[model.provider] ?? model.provider;\n groups.set(label, [...(groups.get(label) ?? []), model]);\n }\n return Array.from(groups, ([provider, grouped]) => ({ provider, models: grouped }));\n}\n\n/** The provider's credentials as the SDK's GraphQL client takes them. Internal. */\nexport function useCatalogAuth(): MentionAuthContext {\n const config = useAthenaConfig();\n return useMemo<MentionAuthContext>(\n () => ({\n backendUrl: config.backendUrl,\n appUrl: config.appUrl,\n apiKey: config.apiKey,\n credentials: 'include',\n get accessToken() {\n return config.token ?? undefined;\n },\n getAccessToken: async () => (await config.getToken?.()) ?? undefined,\n renewAccessToken: async () => (await config.getToken?.({ force: true })) ?? undefined,\n }),\n [config],\n );\n}\n\n/**\n * A short fingerprint (two FNV-1a rounds, 64 bits) of a credential: it tells accounts apart in an\n * in-memory cache key without the credential itself, or anything it could be recovered from,\n * entering the key.\n */\nexport function credentialFingerprint({ credential }: { credential: string }): string {\n const round = (seed: number) => {\n let hash = seed;\n for (let index = 0; index < credential.length; index += 1) {\n hash ^= credential.charCodeAt(index);\n hash = Math.imul(hash, 0x01000193) >>> 0;\n }\n return hash.toString(16).padStart(8, '0');\n };\n return `${round(0x811c9dc5)}${round(0x01000193)}`;\n}\n\n/**\n * Who is asking, for the cache key: the catalog differs per account (admin-only models, the\n * workspace's lists, the agents shared with them), and a host can switch accounts while keeping its\n * query client. The signed-in user's id when the SDK knows it, else a fingerprint of the credential\n * in use. Never the credential itself.\n */\nexport function useCatalogPrincipal(): string {\n const config = useAthenaConfig();\n const userId = useContext(AthenaAuthContext)?.user?.userId;\n if (userId) return `user:${userId}`;\n if (config.apiKey) return `key:${credentialFingerprint({ credential: config.apiKey })}`;\n if (config.token) return `token:${credentialFingerprint({ credential: config.token })}`;\n return 'session';\n}\n\n/**\n * The host's QueryClient when there is one (the thread list provides one), else one of the\n * pickers' own, so the catalog hooks work with or without the thread list.\n */\nexport function useCatalogQueryClient(): QueryClient {\n const host = useContext(QueryClientContext);\n const [own] = useState(() => new QueryClient());\n return host ?? own;\n}\n\n/** The models the signed-in person may pick (`normalizeModelCatalog`). */\nexport function useAthenaModelCatalog() {\n return useModelCatalogQuery({\n auth: useCatalogAuth(),\n principal: useCatalogPrincipal(),\n queryClient: useCatalogQueryClient(),\n });\n}\n\n/** The model catalog's query on a given client (one client for every read a hook makes). */\nfunction useModelCatalogQuery({\n auth,\n principal,\n queryClient,\n}: {\n auth: MentionAuthContext;\n principal: string;\n queryClient: QueryClient;\n}) {\n return useQuery(\n {\n queryKey: ['athena-sdk', 'catalog', 'models', auth.backendUrl, principal],\n queryFn: async () =>\n normalizeModelCatalog({\n response: await fetchGraphQL<AthenaModelCatalogResponse>(auth, ATHENA_MODEL_CATALOG_QUERY),\n }),\n staleTime: 5 * 60 * 1000,\n },\n queryClient,\n );\n}\n\n/** The published collab agents the signed-in person can see (`normalizeAgentCatalog`). */\nexport function useAthenaAgentCatalog() {\n const auth = useCatalogAuth();\n const principal = useCatalogPrincipal();\n const queryClient = useCatalogQueryClient();\n return useQuery(\n {\n queryKey: ['athena-sdk', 'catalog', 'agents', auth.backendUrl, principal],\n queryFn: async () =>\n normalizeAgentCatalog({\n assets:\n (\n await fetchGraphQL<{ getAllAssets: Array<{ id: string; title: string | null }> }>(\n auth,\n ATHENA_AGENT_CATALOG_QUERY,\n )\n ).getAllAssets ?? [],\n }),\n staleTime: 60 * 1000,\n },\n queryClient,\n );\n}\n\nexport const ATHENA_AGENT_MODELS_QUERY = `\n query AthenaAgentModels($assetId: String!) {\n getAssetById(assetId: $assetId, withMetadata: true) { athenaMetadata }\n }\n`;\n\n/**\n * The `getAssetById` error codes that mean \"this person may not read the\n * agent\": no VIEW on it (`UNAUTHORIZED`, `FORBIDDEN`), or it lives in another\n * workspace (`WORKSPACE_MISMATCH`). Asking again answers the same.\n */\nconst AGENT_READ_DENIAL_CODES: ReadonlySet<string> = new Set([\n 'UNAUTHORIZED',\n 'FORBIDDEN',\n 'WORKSPACE_MISMATCH',\n]);\n\n/** What reading the agent's published models found: its models, or that this person cannot know them. */\nexport type AgentModelRead = { readable: true; source: AgentModelSource | null } | { readable: false };\n\n/**\n * Where `useAthenaAgentModelOptions` stands:\n *\n * - `idle`: no agent given;\n * - `loading`: the agent (or the model catalog that names its models) is being read;\n * - `ready`: the agent's published models are known;\n * - `unavailable`: this person cannot know them: no VIEW on the agent, no such\n * agent, or one never published (or whose published definition names no\n * model). Not an error: `allowAnyModel` stays `true`, so a picker keeps the\n * workspace's models;\n * - `error`: the read failed (network, server); `allowAnyModel` stays `true`.\n */\nexport type AthenaAgentModelOptionsStatus = 'idle' | 'loading' | 'ready' | 'unavailable' | 'error';\n\nexport interface AthenaAgentModelOptions {\n status: AthenaAgentModelOptionsStatus;\n /** The agent's model choices (see {@link AthenaAgentModelOption}); empty unless `ready`. */\n options: readonly AthenaAgentModelOption[];\n /**\n * The model the thread runs while it pins none: the channel's override, else\n * the agent's own model. `null` unless `ready`.\n */\n defaultModel: string | null;\n /**\n * Whether people may pick any workspace model with this agent: the agent's\n * \"Allow any workspace model\" switch (`allowAnyModel` in its published\n * definition). Only an explicit `false` restricts a picker to `options`;\n * absent, `null` or `true` (every agent saved before the switch) allow any,\n * and so does an agent this person cannot read. `true` unless `ready`.\n */\n allowAnyModel: boolean;\n}\n\nconst NO_AGENT_MODEL_OPTIONS: Readonly<\n Record<Exclude<AthenaAgentModelOptionsStatus, 'ready'>, AthenaAgentModelOptions>\n> = {\n idle: { status: 'idle', options: [], defaultModel: null, allowAnyModel: true },\n loading: { status: 'loading', options: [], defaultModel: null, allowAnyModel: true },\n unavailable: { status: 'unavailable', options: [], defaultModel: null, allowAnyModel: true },\n error: { status: 'error', options: [], defaultModel: null, allowAnyModel: true },\n};\n\n/** Whether a failed read is a denial: an answer, not a failure (nothing to retry or report). */\nexport function isAgentReadDenial(error: unknown): boolean {\n return (\n error instanceof AthenaGraphQLError &&\n error.code !== null &&\n AGENT_READ_DENIAL_CODES.has(error.code)\n );\n}\n\n/**\n * A selected collab agent's own model choices, and whether the agent restricts\n * a picker to them (`allowAnyModel: false`, the agent's \"Allow any workspace\n * model\" switch turned off). Restriction is opt-in per agent: with\n * `allowAnyModel` true (the default) a picker keeps the workspace's models and\n * can still show `defaultModel` as the agent's own; `ChatModelPicker` does\n * both.\n *\n * `agent` is the agent's asset id or its `collab_agent:<id>` ref (`null`:\n * none, `idle`). `channel` is the collab channel the chat runs on: its model\n * override is the default there. The rules are in `agent-model-options.ts`.\n *\n * The agent is read through `getAssetById(withMetadata: true)`, the\n * permission-checked asset read (VIEW on the agent), with the provider's\n * credentials: a person without VIEW gets `unavailable` with no options. The\n * model the thread pins comes from `useAthenaThreadModel()` (inside\n * `<AthenaProvider>`; none outside one): a pinned model outside the agent's\n * list is an option with `pinnedOutside`.\n *\n * Picking the `isDefault` option means \"no model\": hand the provider\n * `model={undefined}` so the agent's (or channel's) default runs, and a session\n * pinning another model is unpinned.\n *\n * While shown, the agent is read again every\n * {@link AGENT_MODELS_REFRESH_MS}: an author publishing new models elsewhere\n * invalidates nothing here.\n */\nexport function useAthenaAgentModelOptions({\n agent,\n channel = null,\n}: {\n agent: string | null | undefined;\n channel?: string | null;\n}): AthenaAgentModelOptions {\n return useAgentModelChoices({ agent, channel }).choices;\n}\n\n/** How often an agent's published models are read again while a hook shows them. */\nexport const AGENT_MODELS_REFRESH_MS = 60 * 1000;\n\n/**\n * The re-read cadence of an agent model read: every\n * {@link AGENT_MODELS_REFRESH_MS} while it answers, never for a denial (asking\n * again answers the same).\n */\nexport function agentModelsRefetchInterval({\n data,\n}: {\n data: AgentModelRead | undefined;\n}): number | false {\n return data !== undefined && !data.readable ? false : AGENT_MODELS_REFRESH_MS;\n}\n\n/**\n * {@link useAthenaAgentModelOptions} plus the catalog label of any model id,\n * from the same query client and catalog read (a picker needs both; reading\n * the catalog again through its own client would fetch it twice). Internal.\n */\nexport function useAgentModelChoices({\n agent,\n channel,\n}: {\n agent: string | null | undefined;\n channel: string | null;\n}): {\n choices: AthenaAgentModelOptions;\n modelLabel: (model: string) => string;\n /** The workspace model catalog, read through the same client as the agent. */\n catalog: ReturnType<typeof useModelCatalogQuery>;\n} {\n const assetId = agentAssetIdOf(agent);\n const auth = useCatalogAuth();\n const principal = useCatalogPrincipal();\n const queryClient = useCatalogQueryClient();\n const read = useQuery(\n {\n queryKey: ['athena-sdk', 'catalog', 'agent-models', auth.backendUrl, principal, assetId],\n queryFn: async (): Promise<AgentModelRead> => {\n try {\n const data = await fetchGraphQL<{ getAssetById: { athenaMetadata: unknown } | null }>(\n auth,\n ATHENA_AGENT_MODELS_QUERY,\n { assetId },\n );\n if (data.getAssetById === null) return { readable: false };\n return {\n readable: true,\n source: publishedAgentModelSource(data.getAssetById.athenaMetadata),\n };\n } catch (error) {\n if (isAgentReadDenial(error)) return { readable: false };\n throw error;\n }\n },\n enabled: assetId !== null,\n staleTime: AGENT_MODELS_REFRESH_MS,\n refetchInterval: (query) => agentModelsRefetchInterval({ data: query.state.data }),\n },\n queryClient,\n );\n const catalog = useModelCatalogQuery({ auth, principal, queryClient });\n const threadModel = useOptionalAthenaThreadModel();\n const pinnedModel =\n threadModel !== null && threadModel.ready && threadModel.configured ? threadModel.model : null;\n const trimmedChannel = channel?.trim() || null;\n const models = catalog.data;\n const catalogPending = catalog.isPending;\n const { isPending: readPending, data: readData } = read;\n const modelLabel = useCallback(\n (model: string) => models?.find((option) => option.id === model)?.label ?? model,\n [models],\n );\n const choices = useMemo((): AthenaAgentModelOptions => {\n if (assetId === null) return NO_AGENT_MODEL_OPTIONS.idle;\n // A failed re-read keeps the last answer; only a first read that failed is an error.\n if (readData === undefined) {\n return readPending ? NO_AGENT_MODEL_OPTIONS.loading : NO_AGENT_MODEL_OPTIONS.error;\n }\n if (!readData.readable || readData.source === null) return NO_AGENT_MODEL_OPTIONS.unavailable;\n // The catalog names the models: wait for it, unless it failed (ids then name them).\n if (catalogPending) return NO_AGENT_MODEL_OPTIONS.loading;\n return {\n status: 'ready',\n ...buildAgentModelOptions({\n source: readData.source,\n channel: trimmedChannel,\n pinnedModel,\n modelLabel,\n }),\n allowAnyModel: readData.source.allowAnyModel,\n };\n }, [assetId, readPending, readData, catalogPending, modelLabel, trimmedChannel, pinnedModel]);\n return useMemo(() => ({ choices, modelLabel, catalog }), [choices, modelLabel, catalog]);\n}\n"],"names":[],"mappings":";;;;;;;AAiDO,MAAM,wBAA0D;AAAA,EACrE,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,KAAK;AAAA,EACL,MAAM;AAAA,EACN,SAAS;AAAA,EACT,UAAU;AAAA,EACV,MAAM;AAAA,EACN,UAAU;AAAA,EACV,SAAS;AAAA,EACT,KAAK;AACP;AAEO,MAAM,6BAA6B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWnC,MAAM,6BAA6B;AAAA;AAAA;AAAA;AAAA;AA6BnC,SAAS,sBAAsB;AAAA,EACpC;AACF,GAEwB;AACtB,QAAM,UAAU,SAAS,4BAA4B,KAAK;AAC1D,QAAM,WAAW,IAAI,IAAI,SAAS,SAAS,UAAU,iBAAiB;AACtE,QAAM,SAAS,IAAI,IAAI,SAAS,SAAS,UAAU,eAAe;AAClE,QAAM,eAAe,SAAS,OAAO;AACrC,SAAO,SAAS,gBACb;AAAA,IACC,CAAC,WACE,CAAC,MAAM,aAAa,aAAa,eAAe,SAAS,IAAI,MAAM,EAAE,IAAI,CAAC,OAAO,IAAI,MAAM,EAAE;AAAA,EAAA,EAEjG;AAAA,IACC,CAAC,WAA8B;AAAA,MAC7B,IAAI,MAAM;AAAA,MACV,OAAO,MAAM;AAAA,MACb,aAAa,MAAM;AAAA,MACnB,UAAU,MAAM;AAAA,MAChB,QAAQ,MAAM;AAAA,MACd,WAAW,MAAM;AAAA,MACjB,UAAU,MAAM;AAAA,MAChB,YAAY,MAAM;AAAA,MAClB,iBAAiB,MAAM;AAAA,MACvB,YAAY,MAAM;AAAA,MAClB,cAAc,MAAM;AAAA,IAAA;AAAA,EACtB,EAED;AAAA,IACC,CAAC,MAAM,UACL,KAAK,YAAY,MAAM,aACvB,KAAK,eAAe,MAAM,gBAC1B,KAAK,MAAM,cAAc,MAAM,KAAK;AAAA,EAAA;AAE5C;AAGO,SAAS,sBAAsB;AAAA,EACpC;AACF,GAEwB;AACtB,SAAO,OACJ,OAAO,CAAC,UAAU,MAAM,GAAG,KAAA,EAAO,SAAS,CAAC,EAC5C,IAAI,CAAC,UAAA;;AAAW,aAAE,IAAI,MAAM,IAAI,QAAM,WAAM,UAAN,mBAAa,WAAU,iBAAA;AAAA,GAAmB,EAChF,KAAK,CAAC,MAAM,UAAU,KAAK,KAAK,cAAc,MAAM,IAAI,CAAC;AAC9D;AAOO,SAAS,sBAAsB;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AACF,GAIW;;AACT,MAAI,UAAU,OAAO,KAAK,CAAC,UAAU,MAAM,OAAO,MAAM,EAAG,QAAO;AAClE,WAEI,YAAO,KAAK,CAAC,UAAU,MAAM,OAAO,kBAAkB,MAAM,WAAW,QAAQ,KAC/E,OAAO,KAAK,CAAC,UAAU,MAAM,WAAW,iBAAiB,MAAM,eAAe,KAC9E,OAAO,KAAK,CAAC,UAAU,MAAM,WAAW,YAAY,MAAM,eAAe,KACzE,OAAO,KAAK,CAAC,UAAU,MAAM,WAAW,QAAQ,KAChD,OAAO,CAAC,MAJR,mBAKC,QAAO,UAAU;AAExB;AAGO,SAAS,sBAAsB;AAAA,EACpC;AACF,GAE6D;AAC3D,QAAM,6BAAa,IAAA;AACnB,aAAW,SAAS,QAAQ;AAC1B,UAAM,QAAQ,sBAAsB,MAAM,QAAQ,KAAK,MAAM;AAC7D,WAAO,IAAI,OAAO,CAAC,GAAI,OAAO,IAAI,KAAK,KAAK,IAAK,KAAK,CAAC;AAAA,EACzD;AACA,SAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,UAAU,OAAO,OAAO,EAAE,UAAU,QAAQ,QAAA,EAAU;AACpF;AAGO,SAAS,iBAAqC;AACnD,QAAM,SAAS,gBAAA;AACf,SAAO;AAAA,IACL,OAAO;AAAA,MACL,YAAY,OAAO;AAAA,MACnB,QAAQ,OAAO;AAAA,MACf,QAAQ,OAAO;AAAA,MACf,aAAa;AAAA,MACb,IAAI,cAAc;AAChB,eAAO,OAAO,SAAS;AAAA,MACzB;AAAA,MACA,gBAAgB,YAAA;;AAAa,uBAAM,YAAO,aAAP,oCAAwB;AAAA;AAAA,MAC3D,kBAAkB,YAAA;;AAAa,uBAAM,YAAO,aAAP,gCAAkB,EAAE,OAAO,KAAA,OAAY;AAAA;AAAA,IAAA;AAAA,IAE9E,CAAC,MAAM;AAAA,EAAA;AAEX;AAOO,SAAS,sBAAsB,EAAE,cAA8C;AACpF,QAAM,QAAQ,CAAC,SAAiB;AAC9B,QAAI,OAAO;AACX,aAAS,QAAQ,GAAG,QAAQ,WAAW,QAAQ,SAAS,GAAG;AACzD,cAAQ,WAAW,WAAW,KAAK;AACnC,aAAO,KAAK,KAAK,MAAM,QAAU,MAAM;AAAA,IACzC;AACA,WAAO,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAAA,EAC1C;AACA,SAAO,GAAG,MAAM,UAAU,CAAC,GAAG,MAAM,QAAU,CAAC;AACjD;AAQO,SAAS,sBAA8B;;AAC5C,QAAM,SAAS,gBAAA;AACf,QAAM,UAAS,sBAAW,iBAAiB,MAA5B,mBAA+B,SAA/B,mBAAqC;AACpD,MAAI,OAAQ,QAAO,QAAQ,MAAM;AACjC,MAAI,OAAO,OAAQ,QAAO,OAAO,sBAAsB,EAAE,YAAY,OAAO,OAAA,CAAQ,CAAC;AACrF,MAAI,OAAO,MAAO,QAAO,SAAS,sBAAsB,EAAE,YAAY,OAAO,MAAA,CAAO,CAAC;AACrF,SAAO;AACT;AAMO,SAAS,wBAAqC;AACnD,QAAM,OAAO,WAAW,kBAAkB;AAC1C,QAAM,CAAC,GAAG,IAAI,SAAS,MAAM,IAAI,aAAa;AAC9C,SAAO,QAAQ;AACjB;AAGO,SAAS,wBAAwB;AACtC,SAAO,qBAAqB;AAAA,IAC1B,MAAM,eAAA;AAAA,IACN,WAAW,oBAAA;AAAA,IACX,aAAa,sBAAA;AAAA,EAAsB,CACpC;AACH;AAGA,SAAS,qBAAqB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,SAAO;AAAA,IACL;AAAA,MACE,UAAU,CAAC,cAAc,WAAW,UAAU,KAAK,YAAY,SAAS;AAAA,MACxE,SAAS,YACP,sBAAsB;AAAA,QACpB,UAAU,MAAM,aAAyC,MAAM,0BAA0B;AAAA,MAAA,CAC1F;AAAA,MACH,WAAW,IAAI,KAAK;AAAA,IAAA;AAAA,IAEtB;AAAA,EAAA;AAEJ;AAGO,SAAS,wBAAwB;AACtC,QAAM,OAAO,eAAA;AACb,QAAM,YAAY,oBAAA;AAClB,QAAM,cAAc,sBAAA;AACpB,SAAO;AAAA,IACL;AAAA,MACE,UAAU,CAAC,cAAc,WAAW,UAAU,KAAK,YAAY,SAAS;AAAA,MACxE,SAAS,YACP,sBAAsB;AAAA,QACpB,SAEI,MAAM;AAAA,UACJ;AAAA,UACA;AAAA,QAAA,GAEF,gBAAgB,CAAA;AAAA,MAAC,CACtB;AAAA,MACH,WAAW,KAAK;AAAA,IAAA;AAAA,IAElB;AAAA,EAAA;AAEJ;AAEO,MAAM,4BAA4B;AAAA;AAAA;AAAA;AAAA;AAWzC,MAAM,8CAAmD,IAAI;AAAA,EAC3D;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAsCD,MAAM,yBAEF;AAAA,EACF,MAAM,EAAE,QAAQ,QAAQ,SAAS,CAAA,GAAI,cAAc,MAAM,eAAe,KAAA;AAAA,EACxE,SAAS,EAAE,QAAQ,WAAW,SAAS,CAAA,GAAI,cAAc,MAAM,eAAe,KAAA;AAAA,EAC9E,aAAa,EAAE,QAAQ,eAAe,SAAS,CAAA,GAAI,cAAc,MAAM,eAAe,KAAA;AAAA,EACtF,OAAO,EAAE,QAAQ,SAAS,SAAS,CAAA,GAAI,cAAc,MAAM,eAAe,KAAA;AAC5E;AAGO,SAAS,kBAAkB,OAAyB;AACzD,SACE,iBAAiB,sBACjB,MAAM,SAAS,QACf,wBAAwB,IAAI,MAAM,IAAI;AAE1C;AA6BO,SAAS,2BAA2B;AAAA,EACzC;AAAA,EACA,UAAU;AACZ,GAG4B;AAC1B,SAAO,qBAAqB,EAAE,OAAO,QAAA,CAAS,EAAE;AAClD;AAGO,MAAM,0BAA0B,KAAK;AAOrC,SAAS,2BAA2B;AAAA,EACzC;AACF,GAEmB;AACjB,SAAO,SAAS,UAAa,CAAC,KAAK,WAAW,QAAQ;AACxD;AAOO,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA;AACF,GAQE;AACA,QAAM,UAAU,eAAe,KAAK;AACpC,QAAM,OAAO,eAAA;AACb,QAAM,YAAY,oBAAA;AAClB,QAAM,cAAc,sBAAA;AACpB,QAAM,OAAO;AAAA,IACX;AAAA,MACE,UAAU,CAAC,cAAc,WAAW,gBAAgB,KAAK,YAAY,WAAW,OAAO;AAAA,MACvF,SAAS,YAAqC;AAC5C,YAAI;AACF,gBAAM,OAAO,MAAM;AAAA,YACjB;AAAA,YACA;AAAA,YACA,EAAE,QAAA;AAAA,UAAQ;AAEZ,cAAI,KAAK,iBAAiB,KAAM,QAAO,EAAE,UAAU,MAAA;AACnD,iBAAO;AAAA,YACL,UAAU;AAAA,YACV,QAAQ,0BAA0B,KAAK,aAAa,cAAc;AAAA,UAAA;AAAA,QAEtE,SAAS,OAAO;AACd,cAAI,kBAAkB,KAAK,EAAG,QAAO,EAAE,UAAU,MAAA;AACjD,gBAAM;AAAA,QACR;AAAA,MACF;AAAA,MACA,SAAS,YAAY;AAAA,MACrB,WAAW;AAAA,MACX,iBAAiB,CAAC,UAAU,2BAA2B,EAAE,MAAM,MAAM,MAAM,KAAA,CAAM;AAAA,IAAA;AAAA,IAEnF;AAAA,EAAA;AAEF,QAAM,UAAU,qBAAqB,EAAE,MAAM,WAAW,aAAa;AACrE,QAAM,cAAc,6BAAA;AACpB,QAAM,cACJ,gBAAgB,QAAQ,YAAY,SAAS,YAAY,aAAa,YAAY,QAAQ;AAC5F,QAAM,kBAAiB,mCAAS,WAAU;AAC1C,QAAM,SAAS,QAAQ;AACvB,QAAM,iBAAiB,QAAQ;AAC/B,QAAM,EAAE,WAAW,aAAa,MAAM,aAAa;AACnD,QAAM,aAAa;AAAA,IACjB,CAAC,UAAA;;AAAkB,qDAAQ,KAAK,CAAC,WAAW,OAAO,OAAO,WAAvC,mBAA+C,UAAS;AAAA;AAAA,IAC3E,CAAC,MAAM;AAAA,EAAA;AAET,QAAM,UAAU,QAAQ,MAA+B;AACrD,QAAI,YAAY,KAAM,QAAO,uBAAuB;AAEpD,QAAI,aAAa,QAAW;AAC1B,aAAO,cAAc,uBAAuB,UAAU,uBAAuB;AAAA,IAC/E;AACA,QAAI,CAAC,SAAS,YAAY,SAAS,WAAW,aAAa,uBAAuB;AAElF,QAAI,uBAAuB,uBAAuB;AAClD,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,GAAG,uBAAuB;AAAA,QACxB,QAAQ,SAAS;AAAA,QACjB,SAAS;AAAA,QACT;AAAA,QACA;AAAA,MAAA,CACD;AAAA,MACD,eAAe,SAAS,OAAO;AAAA,IAAA;AAAA,EAEnC,GAAG,CAAC,SAAS,aAAa,UAAU,gBAAgB,YAAY,gBAAgB,WAAW,CAAC;AAC5F,SAAO,QAAQ,OAAO,EAAE,SAAS,YAAY,YAAY,CAAC,SAAS,YAAY,OAAO,CAAC;AACzF;"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const react$1 = require("@assistant-ui/react");
|
|
4
|
+
const reactQuery = require("@tanstack/react-query");
|
|
5
|
+
const react = require("react");
|
|
6
|
+
const apiClient = require("../chat-ui/mentions/suggestions/data/api-client.cjs");
|
|
7
|
+
const agentSelectionContext = require("../provider/agent-selection-context.cjs");
|
|
8
|
+
const chatModeContext = require("../provider/chat-mode-context.cjs");
|
|
9
|
+
const agentModeMenu = require("./agent-mode-menu.cjs");
|
|
10
|
+
const runtimeCatalog = require("./runtime-catalog.cjs");
|
|
11
|
+
const OFF = { status: "off" };
|
|
12
|
+
const LOADING = { status: "loading" };
|
|
13
|
+
const NO_MODES = [];
|
|
14
|
+
const ATHENA_SESSION_MODE_QUERY = `
|
|
15
|
+
query AthenaSessionMode($assetId: String!) {
|
|
16
|
+
getAssetById(assetId: $assetId, withMetadata: true) { athenaMetadata }
|
|
17
|
+
}
|
|
18
|
+
`;
|
|
19
|
+
function useAthenaChatMode() {
|
|
20
|
+
var _a, _b, _c;
|
|
21
|
+
const selection = react.useContext(chatModeContext.AthenaChatModeSelectionContext);
|
|
22
|
+
const agentSelection = react.useContext(agentSelectionContext.AthenaAgentSelectionContext);
|
|
23
|
+
const agentAssetId = (agentSelection == null ? void 0 : agentSelection.agentAssetId) ?? null;
|
|
24
|
+
const channel = (agentSelection == null ? void 0 : agentSelection.channel) ?? null;
|
|
25
|
+
const hostModes = (selection == null ? void 0 : selection.hostModes) ?? NO_MODES;
|
|
26
|
+
const enabled = selection !== null && hostModes.length > 0 && agentAssetId !== null;
|
|
27
|
+
const auth = runtimeCatalog.useCatalogAuth();
|
|
28
|
+
const principal = runtimeCatalog.useCatalogPrincipal();
|
|
29
|
+
const queryClient = runtimeCatalog.useCatalogQueryClient();
|
|
30
|
+
const agentRead = reactQuery.useQuery(
|
|
31
|
+
{
|
|
32
|
+
queryKey: ["athena-sdk", "catalog", "agent-modes", auth.backendUrl, principal, agentAssetId],
|
|
33
|
+
queryFn: async () => {
|
|
34
|
+
try {
|
|
35
|
+
const data = await apiClient.fetchGraphQL(
|
|
36
|
+
auth,
|
|
37
|
+
runtimeCatalog.ATHENA_AGENT_MODELS_QUERY,
|
|
38
|
+
{ assetId: agentAssetId }
|
|
39
|
+
);
|
|
40
|
+
if (data.getAssetById === null) return { readable: false };
|
|
41
|
+
return {
|
|
42
|
+
readable: true,
|
|
43
|
+
source: agentModeMenu.publishedAgentModeSource(data.getAssetById.athenaMetadata)
|
|
44
|
+
};
|
|
45
|
+
} catch (error) {
|
|
46
|
+
if (runtimeCatalog.isAgentReadDenial(error)) return { readable: false };
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
enabled,
|
|
51
|
+
staleTime: runtimeCatalog.AGENT_MODELS_REFRESH_MS,
|
|
52
|
+
refetchInterval: (query) => query.state.data !== void 0 && !query.state.data.readable ? false : runtimeCatalog.AGENT_MODELS_REFRESH_MS
|
|
53
|
+
},
|
|
54
|
+
queryClient
|
|
55
|
+
);
|
|
56
|
+
const threadEmpty = react$1.useAuiState((state) => {
|
|
57
|
+
var _a2;
|
|
58
|
+
return ((_a2 = state.optional.thread) == null ? void 0 : _a2.isEmpty) ?? true;
|
|
59
|
+
});
|
|
60
|
+
const threadRunning = react$1.useAuiState((state) => {
|
|
61
|
+
var _a2;
|
|
62
|
+
return ((_a2 = state.optional.thread) == null ? void 0 : _a2.isRunning) ?? false;
|
|
63
|
+
});
|
|
64
|
+
const threadId = (selection == null ? void 0 : selection.threadId) ?? null;
|
|
65
|
+
const sessionStarted = (selection == null ? void 0 : selection.isNewChat) === false || !threadEmpty;
|
|
66
|
+
const readSession = enabled && threadId !== null && sessionStarted;
|
|
67
|
+
const sessionRead = reactQuery.useQuery(
|
|
68
|
+
{
|
|
69
|
+
queryKey: ["athena-sdk", "chat-mode", "session", auth.backendUrl, principal, threadId],
|
|
70
|
+
queryFn: async () => {
|
|
71
|
+
const data = await apiClient.fetchGraphQL(
|
|
72
|
+
auth,
|
|
73
|
+
ATHENA_SESSION_MODE_QUERY,
|
|
74
|
+
{ assetId: `asset_${threadId}` }
|
|
75
|
+
);
|
|
76
|
+
const asset = data.getAssetById;
|
|
77
|
+
return asset ? { exists: true, channel: agentModeMenu.sessionChannelFromMetadata(asset.athenaMetadata) } : { exists: false };
|
|
78
|
+
},
|
|
79
|
+
enabled: readSession,
|
|
80
|
+
staleTime: 0,
|
|
81
|
+
refetchOnWindowFocus: true,
|
|
82
|
+
retry: false
|
|
83
|
+
},
|
|
84
|
+
queryClient
|
|
85
|
+
);
|
|
86
|
+
const refetchSession = sessionRead.refetch;
|
|
87
|
+
const wasRunning = react.useRef(threadRunning);
|
|
88
|
+
react.useEffect(() => {
|
|
89
|
+
const ended = wasRunning.current && !threadRunning;
|
|
90
|
+
wasRunning.current = threadRunning;
|
|
91
|
+
if (ended && readSession) void refetchSession();
|
|
92
|
+
}, [threadRunning, readSession, refetchSession]);
|
|
93
|
+
const source = ((_a = agentRead.data) == null ? void 0 : _a.readable) ? agentRead.data.source : null;
|
|
94
|
+
const menu = react.useMemo(
|
|
95
|
+
() => source ? agentModeMenu.agentModeMenu({ source, channel, hostModes }) : null,
|
|
96
|
+
[source, channel, hostModes]
|
|
97
|
+
);
|
|
98
|
+
const pick = (selection == null ? void 0 : selection.pick) ?? null;
|
|
99
|
+
const session = readSession ? sessionRead.data : void 0;
|
|
100
|
+
const sessionChannel = (session == null ? void 0 : session.exists) ? session.channel : null;
|
|
101
|
+
const setPick = selection == null ? void 0 : selection.setPick;
|
|
102
|
+
const setSessionChannel = selection == null ? void 0 : selection.setSessionChannel;
|
|
103
|
+
react.useEffect(() => {
|
|
104
|
+
if (!threadId || !setSessionChannel || session === void 0) return;
|
|
105
|
+
setSessionChannel({ threadId, channel: sessionChannel });
|
|
106
|
+
}, [threadId, setSessionChannel, session, sessionChannel]);
|
|
107
|
+
react.useEffect(() => {
|
|
108
|
+
if (!threadId || !setPick || pick === null) return;
|
|
109
|
+
const applied = sessionChannel !== null && (sessionChannel === pick || source !== null && agentModeMenu.agentModeOfChannel({ source, channel: sessionChannel }) === pick);
|
|
110
|
+
const withdrawn = menu !== null && !menu.options.some((option) => option.id === pick);
|
|
111
|
+
if (applied || withdrawn) setPick({ threadId, modeId: null });
|
|
112
|
+
}, [threadId, setPick, pick, sessionChannel, source, menu]);
|
|
113
|
+
const canSwitch = !((menu == null ? void 0 : menu.locked) && sessionStarted);
|
|
114
|
+
const options = menu == null ? void 0 : menu.options;
|
|
115
|
+
const select = react.useCallback(
|
|
116
|
+
(modeId) => {
|
|
117
|
+
if (!threadId || !setPick || !canSwitch) return;
|
|
118
|
+
if (!(options == null ? void 0 : options.some((option) => option.id === modeId))) return;
|
|
119
|
+
setPick({ threadId, modeId });
|
|
120
|
+
},
|
|
121
|
+
[threadId, setPick, canSwitch, options]
|
|
122
|
+
);
|
|
123
|
+
if (!enabled) return OFF;
|
|
124
|
+
if (agentRead.isPending) return LOADING;
|
|
125
|
+
if (!menu || menu.options.length < 2) return OFF;
|
|
126
|
+
const current = pick ?? (source && sessionChannel !== null ? agentModeMenu.agentModeOfChannel({ source, channel: sessionChannel }) : menu.firstSendModeId);
|
|
127
|
+
const currentLabel = ((_b = menu.options.find((option) => option.id === current)) == null ? void 0 : _b.label) ?? (current === "base" ? "Default" : ((_c = source == null ? void 0 : source.channels[current]) == null ? void 0 : _c.label) ?? current);
|
|
128
|
+
return {
|
|
129
|
+
status: "ready",
|
|
130
|
+
options: menu.options,
|
|
131
|
+
current,
|
|
132
|
+
currentLabel,
|
|
133
|
+
canSwitch,
|
|
134
|
+
locked: menu.locked,
|
|
135
|
+
select
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
exports.useAthenaChatMode = useAthenaChatMode;
|
|
139
|
+
//# sourceMappingURL=use-athena-chat-mode.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-athena-chat-mode.cjs","sources":["../../src/catalog/use-athena-chat-mode.ts"],"sourcesContent":["import { useAuiState } from '@assistant-ui/react';\nimport { useQuery } from '@tanstack/react-query';\nimport { useCallback, useContext, useEffect, useMemo, useRef } from 'react';\nimport { fetchGraphQL } from '../chat-ui/mentions/suggestions/data/api-client';\nimport { AthenaAgentSelectionContext } from '../provider/agent-selection-context';\nimport { AthenaChatModeSelectionContext } from '../provider/chat-mode-context';\nimport {\n type AgentModeSource,\n type AthenaChatModeOption,\n agentModeMenu,\n agentModeOfChannel,\n publishedAgentModeSource,\n sessionChannelFromMetadata,\n} from './agent-mode-menu';\nimport {\n AGENT_MODELS_REFRESH_MS,\n ATHENA_AGENT_MODELS_QUERY,\n isAgentReadDenial,\n useCatalogAuth,\n useCatalogPrincipal,\n useCatalogQueryClient,\n} from './runtime-catalog';\n\n/**\n * The open chat's mode menu (`AthenaProvider` `modes`):\n *\n * - `off`: no menu: the host listed no modes, no collab agent is selected, the\n * agent cannot be read or was never published, or it offers fewer than two\n * of the listed modes on the provider's `channel`;\n * - `loading`: the agent is being read;\n * - `ready`: the modes to offer, the one the open session runs in (or a new\n * one starts in), and `select` to switch. `canSwitch` is false on a session\n * of a channel its author locks: there, people pick the mode before a\n * session's first message.\n */\nexport type AthenaChatMode =\n | { status: 'off' }\n | { status: 'loading' }\n | {\n status: 'ready';\n options: readonly AthenaChatModeOption[];\n /** The mode the open session runs in, or a new session starts in. */\n current: string;\n /** The current mode's name, also when it is none of `options`. */\n currentLabel: string;\n /** Whether a pick applies now. */\n canSwitch: boolean;\n /** The channel keeps each session in the mode it starts in. */\n locked: boolean;\n /** Run the open session's next sends in `modeId` (one of `options`). */\n select: (modeId: string) => void;\n };\n\ntype AgentModeRead = { readable: true; source: AgentModeSource | null } | { readable: false };\n\n/** The open thread's session asset: absent until its first send mints it. */\ntype SessionRead = { exists: false } | { exists: true; channel: string | null };\n\nconst OFF: AthenaChatMode = { status: 'off' };\nconst LOADING: AthenaChatMode = { status: 'loading' };\nconst NO_MODES: readonly string[] = [];\n\nconst ATHENA_SESSION_MODE_QUERY = `\n query AthenaSessionMode($assetId: String!) {\n getAssetById(assetId: $assetId, withMetadata: true) { athenaMetadata }\n }\n`;\n\n/**\n * The mode menu of the chat inside the nearest `AthenaProvider`, from the\n * agent's published definition, the open session's own mode, and the\n * provider's `modes`, `agent` and `channel`. `AthenaChat` renders it in its\n * composer; use it to build a menu of your own.\n *\n * The open session's mode is read again when the chat regains focus and after\n * every run, and its sends name it, so a session switched in another browser\n * continues where it is. A pick holds until the session runs in it (or the\n * menu stops offering it); only a pick not yet sent overrides the session.\n */\nexport function useAthenaChatMode(): AthenaChatMode {\n const selection = useContext(AthenaChatModeSelectionContext);\n const agentSelection = useContext(AthenaAgentSelectionContext);\n const agentAssetId = agentSelection?.agentAssetId ?? null;\n const channel = agentSelection?.channel ?? null;\n const hostModes = selection?.hostModes ?? NO_MODES;\n const enabled = selection !== null && hostModes.length > 0 && agentAssetId !== null;\n const auth = useCatalogAuth();\n const principal = useCatalogPrincipal();\n const queryClient = useCatalogQueryClient();\n\n const agentRead = useQuery(\n {\n queryKey: ['athena-sdk', 'catalog', 'agent-modes', auth.backendUrl, principal, agentAssetId],\n queryFn: async (): Promise<AgentModeRead> => {\n try {\n const data = await fetchGraphQL<{ getAssetById: { athenaMetadata: unknown } | null }>(\n auth,\n ATHENA_AGENT_MODELS_QUERY,\n { assetId: agentAssetId },\n );\n if (data.getAssetById === null) return { readable: false };\n return {\n readable: true,\n source: publishedAgentModeSource(data.getAssetById.athenaMetadata),\n };\n } catch (error) {\n if (isAgentReadDenial(error)) return { readable: false };\n throw error;\n }\n },\n enabled,\n staleTime: AGENT_MODELS_REFRESH_MS,\n refetchInterval: (query) =>\n query.state.data !== undefined && !query.state.data.readable\n ? false\n : AGENT_MODELS_REFRESH_MS,\n },\n queryClient,\n );\n\n // The open thread as this chat holds it: its first send ends a new chat's\n // pick-before-start window even where the provider cannot tell (a\n // standalone chat on a thread id it minted), and the end of each run, which\n // may have switched the session (here or in another browser), reads it again.\n const threadEmpty = useAuiState((state) => state.optional.thread?.isEmpty ?? true);\n const threadRunning = useAuiState((state) => state.optional.thread?.isRunning ?? false);\n\n const threadId = selection?.threadId ?? null;\n const sessionStarted = selection?.isNewChat === false || !threadEmpty;\n const readSession = enabled && threadId !== null && sessionStarted;\n const sessionRead = useQuery(\n {\n queryKey: ['athena-sdk', 'chat-mode', 'session', auth.backendUrl, principal, threadId],\n queryFn: async (): Promise<SessionRead> => {\n const data = await fetchGraphQL<{ getAssetById: { athenaMetadata: unknown } | null }>(\n auth,\n ATHENA_SESSION_MODE_QUERY,\n { assetId: `asset_${threadId}` },\n );\n const asset = data.getAssetById;\n return asset\n ? { exists: true, channel: sessionChannelFromMetadata(asset.athenaMetadata) }\n : { exists: false };\n },\n enabled: readSession,\n staleTime: 0,\n refetchOnWindowFocus: true,\n retry: false,\n },\n queryClient,\n );\n const refetchSession = sessionRead.refetch;\n const wasRunning = useRef(threadRunning);\n useEffect(() => {\n const ended = wasRunning.current && !threadRunning;\n wasRunning.current = threadRunning;\n if (ended && readSession) void refetchSession();\n }, [threadRunning, readSession, refetchSession]);\n\n const source = agentRead.data?.readable ? agentRead.data.source : null;\n const menu = useMemo(\n () => (source ? agentModeMenu({ source, channel, hostModes }) : null),\n [source, channel, hostModes],\n );\n const pick = selection?.pick ?? null;\n const session = readSession ? sessionRead.data : undefined;\n const sessionChannel = session?.exists ? session.channel : null;\n const setPick = selection?.setPick;\n const setSessionChannel = selection?.setSessionChannel;\n\n useEffect(() => {\n if (!threadId || !setSessionChannel || session === undefined) return;\n setSessionChannel({ threadId, channel: sessionChannel });\n }, [threadId, setSessionChannel, session, sessionChannel]);\n\n useEffect(() => {\n if (!threadId || !setPick || pick === null) return;\n const applied =\n sessionChannel !== null &&\n (sessionChannel === pick ||\n (source !== null && agentModeOfChannel({ source, channel: sessionChannel }) === pick));\n const withdrawn = menu !== null && !menu.options.some((option) => option.id === pick);\n if (applied || withdrawn) setPick({ threadId, modeId: null });\n }, [threadId, setPick, pick, sessionChannel, source, menu]);\n\n const canSwitch = !(menu?.locked && sessionStarted);\n const options = menu?.options;\n const select = useCallback(\n (modeId: string) => {\n if (!threadId || !setPick || !canSwitch) return;\n if (!options?.some((option) => option.id === modeId)) return;\n setPick({ threadId, modeId });\n },\n [threadId, setPick, canSwitch, options],\n );\n\n if (!enabled) return OFF;\n if (agentRead.isPending) return LOADING;\n if (!menu || menu.options.length < 2) return OFF;\n const current =\n pick ??\n (source && sessionChannel !== null\n ? agentModeOfChannel({ source, channel: sessionChannel })\n : menu.firstSendModeId);\n const currentLabel =\n menu.options.find((option) => option.id === current)?.label ??\n (current === 'base' ? 'Default' : (source?.channels[current]?.label ?? current));\n return {\n status: 'ready',\n options: menu.options,\n current,\n currentLabel,\n canSwitch,\n locked: menu.locked,\n select,\n };\n}\n"],"names":["useContext","AthenaChatModeSelectionContext","AthenaAgentSelectionContext","useCatalogAuth","useCatalogPrincipal","useCatalogQueryClient","useQuery","fetchGraphQL","ATHENA_AGENT_MODELS_QUERY","publishedAgentModeSource","isAgentReadDenial","AGENT_MODELS_REFRESH_MS","useAuiState","_a","sessionChannelFromMetadata","useRef","useEffect","useMemo","agentModeMenu","agentModeOfChannel","useCallback"],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type AthenaChatModeOption } from './agent-mode-menu';
|
|
2
|
+
/**
|
|
3
|
+
* The open chat's mode menu (`AthenaProvider` `modes`):
|
|
4
|
+
*
|
|
5
|
+
* - `off`: no menu: the host listed no modes, no collab agent is selected, the
|
|
6
|
+
* agent cannot be read or was never published, or it offers fewer than two
|
|
7
|
+
* of the listed modes on the provider's `channel`;
|
|
8
|
+
* - `loading`: the agent is being read;
|
|
9
|
+
* - `ready`: the modes to offer, the one the open session runs in (or a new
|
|
10
|
+
* one starts in), and `select` to switch. `canSwitch` is false on a session
|
|
11
|
+
* of a channel its author locks: there, people pick the mode before a
|
|
12
|
+
* session's first message.
|
|
13
|
+
*/
|
|
14
|
+
export type AthenaChatMode = {
|
|
15
|
+
status: 'off';
|
|
16
|
+
} | {
|
|
17
|
+
status: 'loading';
|
|
18
|
+
} | {
|
|
19
|
+
status: 'ready';
|
|
20
|
+
options: readonly AthenaChatModeOption[];
|
|
21
|
+
/** The mode the open session runs in, or a new session starts in. */
|
|
22
|
+
current: string;
|
|
23
|
+
/** The current mode's name, also when it is none of `options`. */
|
|
24
|
+
currentLabel: string;
|
|
25
|
+
/** Whether a pick applies now. */
|
|
26
|
+
canSwitch: boolean;
|
|
27
|
+
/** The channel keeps each session in the mode it starts in. */
|
|
28
|
+
locked: boolean;
|
|
29
|
+
/** Run the open session's next sends in `modeId` (one of `options`). */
|
|
30
|
+
select: (modeId: string) => void;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* The mode menu of the chat inside the nearest `AthenaProvider`, from the
|
|
34
|
+
* agent's published definition, the open session's own mode, and the
|
|
35
|
+
* provider's `modes`, `agent` and `channel`. `AthenaChat` renders it in its
|
|
36
|
+
* composer; use it to build a menu of your own.
|
|
37
|
+
*
|
|
38
|
+
* The open session's mode is read again when the chat regains focus and after
|
|
39
|
+
* every run, and its sends name it, so a session switched in another browser
|
|
40
|
+
* continues where it is. A pick holds until the session runs in it (or the
|
|
41
|
+
* menu stops offering it); only a pick not yet sent overrides the session.
|
|
42
|
+
*/
|
|
43
|
+
export declare function useAthenaChatMode(): AthenaChatMode;
|