@athenaintel/react 0.32.0 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +99 -9
- package/dist/agent-runtime/src/composer-markdown.cjs.map +1 -1
- package/dist/agent-runtime/src/composer-markdown.js.map +1 -1
- package/dist/agent-runtime/src/deep-agent-transport-recipe.cjs +2 -3
- package/dist/agent-runtime/src/deep-agent-transport-recipe.cjs.map +1 -1
- package/dist/agent-runtime/src/deep-agent-transport-recipe.js +3 -4
- package/dist/agent-runtime/src/deep-agent-transport-recipe.js.map +1 -1
- package/dist/agent-runtime/src/edit-command.cjs.map +1 -1
- package/dist/agent-runtime/src/edit-command.js.map +1 -1
- package/dist/agent-runtime/src/messages.cjs +57 -2
- package/dist/agent-runtime/src/messages.cjs.map +1 -1
- package/dist/agent-runtime/src/messages.js +57 -2
- package/dist/agent-runtime/src/messages.js.map +1 -1
- package/dist/agent-runtime/src/quota-safe-local-storage-session.cjs +24 -3
- package/dist/agent-runtime/src/quota-safe-local-storage-session.cjs.map +1 -1
- package/dist/agent-runtime/src/quota-safe-local-storage-session.js +24 -3
- package/dist/agent-runtime/src/quota-safe-local-storage-session.js.map +1 -1
- package/dist/agent-runtime/src/statewire-transport.cjs +4 -13
- package/dist/agent-runtime/src/statewire-transport.cjs.map +1 -1
- package/dist/agent-runtime/src/statewire-transport.js +5 -14
- package/dist/agent-runtime/src/statewire-transport.js.map +1 -1
- package/dist/agent-runtime/src/tool-metadata.cjs +1 -1
- package/dist/agent-runtime/src/tool-metadata.cjs.map +1 -1
- package/dist/agent-runtime/src/tool-metadata.js +1 -1
- package/dist/agent-runtime/src/tool-metadata.js.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-thread.cjs.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-thread.js.map +1 -1
- package/dist/assets/AthenaAssetEmbed.cjs +3 -1
- package/dist/assets/AthenaAssetEmbed.cjs.map +1 -1
- package/dist/assets/AthenaAssetEmbed.d.ts +7 -0
- package/dist/assets/AthenaAssetEmbed.js +4 -2
- package/dist/assets/AthenaAssetEmbed.js.map +1 -1
- package/dist/assets/use-asset-embed.cjs +71 -71
- package/dist/assets/use-asset-embed.cjs.map +1 -1
- package/dist/assets/use-asset-embed.d.ts +20 -7
- package/dist/assets/use-asset-embed.js +72 -72
- package/dist/assets/use-asset-embed.js.map +1 -1
- package/dist/catalog/agent-model-options.cjs +122 -0
- package/dist/catalog/agent-model-options.cjs.map +1 -0
- package/dist/catalog/agent-model-options.d.ts +115 -0
- package/dist/catalog/agent-model-options.js +122 -0
- package/dist/catalog/agent-model-options.js.map +1 -0
- package/dist/catalog/chat-pickers.cjs +184 -4
- package/dist/catalog/chat-pickers.cjs.map +1 -1
- package/dist/catalog/chat-pickers.d.ts +27 -3
- package/dist/catalog/chat-pickers.js +187 -7
- package/dist/catalog/chat-pickers.js.map +1 -1
- package/dist/catalog/runtime-catalog.cjs +117 -3
- package/dist/catalog/runtime-catalog.cjs.map +1 -1
- package/dist/catalog/runtime-catalog.d.ts +104 -0
- package/dist/catalog/runtime-catalog.js +119 -5
- package/dist/catalog/runtime-catalog.js.map +1 -1
- package/dist/chat/AthenaChat.cjs.map +1 -1
- package/dist/chat/AthenaChat.d.ts +8 -7
- package/dist/chat/AthenaChat.js.map +1 -1
- package/dist/chat/super-grouping/SuperGroupingCard.cjs +30 -3
- package/dist/chat/super-grouping/SuperGroupingCard.cjs.map +1 -1
- package/dist/chat/super-grouping/SuperGroupingCard.d.ts +12 -9
- package/dist/chat/super-grouping/SuperGroupingCard.js +31 -4
- package/dist/chat/super-grouping/SuperGroupingCard.js.map +1 -1
- package/dist/chat/super-grouping/SuperGroupingTranscript.cjs.map +1 -1
- package/dist/chat/super-grouping/SuperGroupingTranscript.d.ts +8 -8
- package/dist/chat/super-grouping/SuperGroupingTranscript.js.map +1 -1
- package/dist/chat/super-grouping/activity-timeline.cjs.map +1 -1
- package/dist/chat/super-grouping/activity-timeline.d.ts +4 -4
- package/dist/chat/super-grouping/activity-timeline.js.map +1 -1
- package/dist/chat/super-grouping/index.d.ts +6 -5
- package/dist/chat/super-grouping/toolkit-map.cjs +3 -1
- package/dist/chat/super-grouping/toolkit-map.cjs.map +1 -1
- package/dist/chat/super-grouping/toolkit-map.js +3 -1
- package/dist/chat/super-grouping/toolkit-map.js.map +1 -1
- package/dist/chat-ui/index.cjs +1 -0
- package/dist/chat-ui/index.cjs.map +1 -1
- package/dist/chat-ui/index.d.ts +1 -1
- package/dist/chat-ui/index.js +2 -1
- package/dist/chat-ui/mentions/suggestions/data/api-client.cjs +33 -2
- package/dist/chat-ui/mentions/suggestions/data/api-client.cjs.map +1 -1
- package/dist/chat-ui/mentions/suggestions/data/api-client.d.ts +14 -0
- package/dist/chat-ui/mentions/suggestions/data/api-client.js +33 -2
- package/dist/chat-ui/mentions/suggestions/data/api-client.js.map +1 -1
- package/dist/index.cjs +1 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/lib/use-athena-rest-query.cjs +4 -0
- package/dist/lib/use-athena-rest-query.cjs.map +1 -1
- package/dist/lib/use-athena-rest-query.d.ts +6 -0
- package/dist/lib/use-athena-rest-query.js +4 -0
- package/dist/lib/use-athena-rest-query.js.map +1 -1
- package/dist/provider/AthenaProvider.cjs +29 -8
- package/dist/provider/AthenaProvider.cjs.map +1 -1
- package/dist/provider/AthenaProvider.d.ts +14 -4
- package/dist/provider/AthenaProvider.js +31 -10
- package/dist/provider/AthenaProvider.js.map +1 -1
- package/dist/provider/agent-selection-context.cjs +6 -0
- package/dist/provider/agent-selection-context.cjs.map +1 -0
- package/dist/provider/agent-selection-context.d.ts +14 -0
- package/dist/provider/agent-selection-context.js +6 -0
- package/dist/provider/agent-selection-context.js.map +1 -0
- package/dist/react/package.json.cjs +1 -1
- package/dist/react/package.json.js +1 -1
- package/dist/runtime/StatewireThreadConfigLaneSync.cjs +125 -0
- package/dist/runtime/StatewireThreadConfigLaneSync.cjs.map +1 -0
- package/dist/runtime/StatewireThreadConfigLaneSync.d.ts +54 -0
- package/dist/runtime/StatewireThreadConfigLaneSync.js +125 -0
- package/dist/runtime/StatewireThreadConfigLaneSync.js.map +1 -0
- package/dist/runtime/statewire-model-lane.cjs +0 -12
- package/dist/runtime/statewire-model-lane.cjs.map +1 -1
- package/dist/runtime/statewire-model-lane.d.ts +3 -1
- package/dist/runtime/statewire-model-lane.js +0 -12
- package/dist/runtime/statewire-model-lane.js.map +1 -1
- package/dist/runtime/statewire-server-lane-context.cjs.map +1 -1
- package/dist/runtime/statewire-server-lane-context.d.ts +2 -2
- package/dist/runtime/statewire-server-lane-context.js.map +1 -1
- package/dist/runtime/statewire-thread-config-lane.cjs +84 -0
- package/dist/runtime/statewire-thread-config-lane.cjs.map +1 -0
- package/dist/runtime/statewire-thread-config-lane.d.ts +94 -0
- package/dist/runtime/statewire-thread-config-lane.js +84 -0
- package/dist/runtime/statewire-thread-config-lane.js.map +1 -0
- package/dist/runtime/useAthenaStatewireRuntime.cjs +2 -2
- package/dist/runtime/useAthenaStatewireRuntime.cjs.map +1 -1
- package/dist/runtime/useAthenaStatewireRuntime.js +2 -2
- package/dist/runtime/useAthenaStatewireRuntime.js.map +1 -1
- package/dist/threads/use-athena-thread-model.cjs +11 -3
- package/dist/threads/use-athena-thread-model.cjs.map +1 -1
- package/dist/threads/use-athena-thread-model.d.ts +5 -0
- package/dist/threads/use-athena-thread-model.js +12 -4
- package/dist/threads/use-athena-thread-model.js.map +1 -1
- package/package.json +3 -3
- package/dist/runtime/StatewireModelLaneSync.cjs +0 -96
- package/dist/runtime/StatewireModelLaneSync.cjs.map +0 -1
- package/dist/runtime/StatewireModelLaneSync.d.ts +0 -45
- package/dist/runtime/StatewireModelLaneSync.js +0 -96
- package/dist/runtime/StatewireModelLaneSync.js.map +0 -1
|
@@ -1,22 +1,28 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useQuery, QueryClient, QueryClientContext } from "@tanstack/react-query";
|
|
2
|
+
import { useRef, useContext } from "react";
|
|
2
3
|
import { deriveAthenaAppUrl } from "../lib/athena-urls.js";
|
|
4
|
+
import { keyPartFingerprint } from "../lib/use-athena-rest-query.js";
|
|
3
5
|
import { sendWithCredentialRenewal } from "../runtime/credential-renewal.js";
|
|
4
6
|
const DEFAULT_ASSET_EMBED_READ_ONLY = false;
|
|
7
|
+
const DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT = false;
|
|
5
8
|
const DEFAULT_ASSET_EMBED_DISPLAY_MODE = "full";
|
|
6
9
|
function buildAssetEmbedRequestBody({
|
|
7
10
|
assetId,
|
|
8
11
|
readOnly,
|
|
9
12
|
expiresInSeconds,
|
|
10
|
-
displayMode
|
|
13
|
+
displayMode,
|
|
14
|
+
allowQuickEdit = DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT
|
|
11
15
|
}) {
|
|
12
16
|
return {
|
|
13
17
|
asset_id: assetId,
|
|
14
18
|
read_only: readOnly,
|
|
15
19
|
expires_in_seconds: expiresInSeconds,
|
|
16
|
-
display_mode: displayMode
|
|
20
|
+
display_mode: displayMode,
|
|
21
|
+
// Sent only when asked: an agora that predates it is never handed an unknown field.
|
|
22
|
+
...allowQuickEdit && { allow_quick_edit: true }
|
|
17
23
|
};
|
|
18
24
|
}
|
|
19
|
-
const
|
|
25
|
+
const embedQueryClient = new QueryClient();
|
|
20
26
|
const ATHENA_APP_HOSTNAMES = /* @__PURE__ */ new Set(["app.athenaintel.com", "staging-app.athenaintel.com"]);
|
|
21
27
|
const ATHENA_PREVIEW_HOSTNAME_SUFFIX = ".previews.athenaintel.com";
|
|
22
28
|
const ATHENA_WORKSPACE_HOSTNAME_PATTERN = /^.+--(\d+)\.(?:app\.athenaintel\.com|staging-app\.athenaintel\.com)$/;
|
|
@@ -292,92 +298,86 @@ function getAssetEmbedAuthContext({ token, apiKey }) {
|
|
|
292
298
|
function useAssetEmbed(assetId, options = {
|
|
293
299
|
backendUrl: ""
|
|
294
300
|
}) {
|
|
301
|
+
var _a;
|
|
295
302
|
const {
|
|
296
303
|
readOnly = DEFAULT_ASSET_EMBED_READ_ONLY,
|
|
297
304
|
expiresInSeconds = 60 * 60 * 24 * 30,
|
|
298
305
|
displayMode = DEFAULT_ASSET_EMBED_DISPLAY_MODE,
|
|
306
|
+
allowQuickEdit = DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT,
|
|
299
307
|
backendUrl,
|
|
300
308
|
appUrl,
|
|
301
309
|
apiKey,
|
|
302
310
|
token,
|
|
303
311
|
getToken
|
|
304
312
|
} = options;
|
|
305
|
-
const [embedUrl, setEmbedUrl] = useState(null);
|
|
306
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
307
|
-
const [error, setError] = useState(null);
|
|
308
|
-
const abortRef = useRef(null);
|
|
309
313
|
const tokenRef = useRef(token);
|
|
310
314
|
tokenRef.current = token;
|
|
311
315
|
const getTokenRef = useRef(getToken);
|
|
312
316
|
getTokenRef.current = getToken;
|
|
313
317
|
const authContext = getAssetEmbedAuthContext({ token, apiKey });
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
expiresInSeconds,
|
|
348
|
-
displayMode
|
|
318
|
+
const enabled = Boolean(assetId && backendUrl);
|
|
319
|
+
const query = useQuery(
|
|
320
|
+
{
|
|
321
|
+
queryKey: [
|
|
322
|
+
"athena-sdk",
|
|
323
|
+
"asset-embed",
|
|
324
|
+
assetId,
|
|
325
|
+
readOnly,
|
|
326
|
+
displayMode,
|
|
327
|
+
allowQuickEdit,
|
|
328
|
+
// The identity, never the credential: an opaque token or a key is part of `authContext`.
|
|
329
|
+
keyPartFingerprint(authContext),
|
|
330
|
+
backendUrl,
|
|
331
|
+
appUrl ?? ""
|
|
332
|
+
],
|
|
333
|
+
queryFn: async ({ signal }) => {
|
|
334
|
+
const apiBaseUrl = backendUrl.replace(/\/api\/assistant-ui\/?$/, "");
|
|
335
|
+
const heldToken = tokenRef.current;
|
|
336
|
+
const res = await sendWithCredentialRenewal(
|
|
337
|
+
{ apiKey, token: heldToken, getToken: heldToken ? getTokenRef.current : void 0 },
|
|
338
|
+
(headers) => fetch(`${apiBaseUrl}/api/embed/generate-token`, {
|
|
339
|
+
method: "POST",
|
|
340
|
+
headers: { "Content-Type": "application/json", ...headers },
|
|
341
|
+
body: JSON.stringify(
|
|
342
|
+
buildAssetEmbedRequestBody({
|
|
343
|
+
assetId: assetId ?? "",
|
|
344
|
+
readOnly,
|
|
345
|
+
expiresInSeconds,
|
|
346
|
+
displayMode,
|
|
347
|
+
allowQuickEdit
|
|
348
|
+
})
|
|
349
|
+
),
|
|
350
|
+
signal
|
|
349
351
|
})
|
|
350
|
-
)
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
return () => controller.abort();
|
|
377
|
-
}, [assetId, readOnly, expiresInSeconds, displayMode, backendUrl, appUrl, authContext, apiKey]);
|
|
378
|
-
return { embedUrl, isLoading, error };
|
|
352
|
+
);
|
|
353
|
+
if (!res.ok) {
|
|
354
|
+
const text = await res.text().catch(() => "");
|
|
355
|
+
throw new Error(`Failed to generate embed token (${res.status}): ${text}`);
|
|
356
|
+
}
|
|
357
|
+
const data = await res.json();
|
|
358
|
+
return {
|
|
359
|
+
url: resolveAssetEmbedUrl({ embedUrl: data.embed_url, appUrl, backendUrl }),
|
|
360
|
+
expiresAt: data.expires_at
|
|
361
|
+
};
|
|
362
|
+
},
|
|
363
|
+
enabled,
|
|
364
|
+
// A minted link is good until it expires; a new one would reload the frame, so it is never
|
|
365
|
+
// minted again while the embed shows (no refetch on focus or reconnect) and never retried.
|
|
366
|
+
staleTime: (cached) => cached.state.data ? Math.max(0, cached.state.data.expiresAt * 1e3 - Date.now()) : 0,
|
|
367
|
+
refetchOnWindowFocus: false,
|
|
368
|
+
refetchOnReconnect: false,
|
|
369
|
+
retry: false
|
|
370
|
+
},
|
|
371
|
+
useContext(QueryClientContext) ?? embedQueryClient
|
|
372
|
+
);
|
|
373
|
+
return {
|
|
374
|
+
embedUrl: enabled ? ((_a = query.data) == null ? void 0 : _a.url) ?? null : null,
|
|
375
|
+
isLoading: enabled && query.isLoading,
|
|
376
|
+
error: enabled && query.error ? query.error.message : null
|
|
377
|
+
};
|
|
379
378
|
}
|
|
380
379
|
export {
|
|
380
|
+
DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT,
|
|
381
381
|
DEFAULT_ASSET_EMBED_DISPLAY_MODE,
|
|
382
382
|
DEFAULT_ASSET_EMBED_READ_ONLY,
|
|
383
383
|
appendCitationReferenceParams,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-asset-embed.js","sources":["../../src/assets/use-asset-embed.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\nimport { deriveAthenaAppUrl } from '../lib/athena-urls';\nimport { sendWithCredentialRenewal } from '../runtime/credential-renewal';\nimport type { AthenaStatewireTokenGetter } from '../runtime/statewire-auth-headers';\nimport type { AthenaAssetCitationReference } from './asset-embed-messages';\n\nexport interface UseAssetEmbedOptions {\n readOnly?: boolean;\n expiresInSeconds?: number;\n /** Native Olympus surface rendered by the iframe. Defaults to `full`. */\n displayMode?: 'minimal' | 'full';\n}\n\nexport const DEFAULT_ASSET_EMBED_READ_ONLY = false;\nexport const DEFAULT_ASSET_EMBED_DISPLAY_MODE = 'full' as const;\n\nexport interface UseAssetEmbedResult {\n embedUrl: string | null;\n isLoading: boolean;\n error: string | null;\n}\n\nexport function buildAssetEmbedRequestBody({\n assetId,\n readOnly,\n expiresInSeconds,\n displayMode,\n}: {\n assetId: string;\n readOnly: boolean;\n expiresInSeconds: number;\n displayMode: 'minimal' | 'full';\n}) {\n return {\n asset_id: assetId,\n read_only: readOnly,\n expires_in_seconds: expiresInSeconds,\n display_mode: displayMode,\n };\n}\n\n// Cache embed URLs to avoid re-generating tokens for the same asset\nconst embedCache = new Map<string, { url: string; expiresAt: number }>();\n\nconst ATHENA_APP_HOSTNAMES = new Set(['app.athenaintel.com', 'staging-app.athenaintel.com']);\nconst ATHENA_PREVIEW_HOSTNAME_SUFFIX = '.previews.athenaintel.com';\nconst ATHENA_WORKSPACE_HOSTNAME_PATTERN = /^.+--(\\d+)\\.(?:app\\.athenaintel\\.com|staging-app\\.athenaintel\\.com)$/;\nconst OLYMPUS_WORKSPACE_PORT = '8082';\n\nconst getUrl = (value?: string | null): URL | null => {\n if (!value) {\n return null;\n }\n\n try {\n return new URL(value);\n } catch {\n return null;\n }\n};\n\nconst getCurrentOrigin = (): string | null => {\n if (typeof window === 'undefined') {\n return null;\n }\n\n return window.location.origin;\n};\n\nconst getOrigin = (value?: string | null): string | null => getUrl(value)?.origin ?? null;\n\nconst getAthenaWorkspacePort = (url: URL): string | null => {\n const match = url.hostname.toLowerCase().match(ATHENA_WORKSPACE_HOSTNAME_PATTERN);\n return match?.[1] ?? null;\n};\n\nconst isEmbedCapableAthenaHost = (url: URL): boolean => {\n const hostname = url.hostname.toLowerCase();\n\n if (ATHENA_APP_HOSTNAMES.has(hostname) || hostname.endsWith(ATHENA_PREVIEW_HOSTNAME_SUFFIX)) {\n return true;\n }\n\n return getAthenaWorkspacePort(url) === OLYMPUS_WORKSPACE_PORT;\n};\n\nconst shouldPreferDerivedEmbedAppUrl = ({\n appUrl,\n derivedAppUrl,\n currentOrigin,\n}: {\n appUrl: string;\n derivedAppUrl: string | null;\n currentOrigin?: string | null;\n}): boolean => {\n if (!derivedAppUrl) {\n return false;\n }\n\n const resolvedAppUrl = getUrl(appUrl);\n if (!resolvedAppUrl) {\n return true;\n }\n\n const workspacePort = getAthenaWorkspacePort(resolvedAppUrl);\n if (workspacePort && workspacePort !== OLYMPUS_WORKSPACE_PORT) {\n return true;\n }\n\n if (isEmbedCapableAthenaHost(resolvedAppUrl)) {\n return false;\n }\n\n const resolvedCurrentOrigin = getOrigin(currentOrigin ?? getCurrentOrigin());\n return !!resolvedCurrentOrigin && resolvedAppUrl.origin === resolvedCurrentOrigin;\n};\n\nconst resolveEmbedAppUrl = ({\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string | null => {\n const derivedAppUrl = deriveAthenaAppUrl({ backendUrl });\n\n if (!appUrl) {\n return derivedAppUrl;\n }\n\n if (shouldPreferDerivedEmbedAppUrl({ appUrl, derivedAppUrl, currentOrigin })) {\n return derivedAppUrl;\n }\n\n return appUrl;\n};\n\nexport function resolveAssetEmbedUrl({\n embedUrl,\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n embedUrl: string;\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string {\n try {\n const resolvedEmbedUrl = new URL(embedUrl);\n if (!resolvedEmbedUrl.pathname.startsWith('/embed/')) {\n return embedUrl;\n }\n\n const embedAppUrl = resolveEmbedAppUrl({\n appUrl,\n backendUrl,\n currentOrigin,\n });\n if (!embedAppUrl) {\n return embedUrl;\n }\n\n const resolvedAppUrl = new URL(embedAppUrl);\n resolvedEmbedUrl.protocol = resolvedAppUrl.protocol;\n resolvedEmbedUrl.host = resolvedAppUrl.host;\n return resolvedEmbedUrl.toString();\n } catch {\n return embedUrl;\n }\n}\n\nfunction setFiniteNumberParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'number' && Number.isFinite(value)) {\n params.set(key, String(value));\n }\n}\n\nfunction setNonEmptyStringParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'string' && value.length > 0) {\n params.set(key, value);\n }\n}\n\n/** The SDK's own embed parameter: it marks the bridge, never a place, so no anchor writes it. */\nconst SDK_BRIDGE_PARAM = 'athena_sdk_bridge';\n\n/**\n * The pairs of an anchor given as its raw query — the string an Athena link, citation or search\n * result carries (`anchor=slide&slide_index=3&slide_id=…`, `anchor=time&time=0:42`), alone or\n * after the `?` of an asset ref (`asset_x?anchor=…`) — read as URL parameters, or `null` when the\n * string holds none, or a parameter with no name. The SDK never interprets the anchor: the embed\n * page parses it with the references grammar, so every kind of place travels as written.\n */\nfunction rawAnchorQueryPairs(query: string): Array<[string, string]> | null {\n const queryIndex = query.indexOf('?');\n const search = queryIndex === -1 ? query : query.slice(queryIndex + 1);\n const pairs = [...new URLSearchParams(search)].filter(([key]) => key !== SDK_BRIDGE_PARAM);\n if (pairs.length === 0 || pairs.some(([key]) => key.length === 0)) return null;\n return pairs;\n}\n\nfunction appendRawAnchorQuery({ params, query }: { params: URLSearchParams; query: string }): void {\n const pairs = rawAnchorQueryPairs(query);\n if (!pairs) return;\n // Verbatim: a key the URL already has is replaced, and a repeated key keeps every value.\n for (const [key] of pairs) params.delete(key);\n for (const [key, value] of pairs) params.append(key, value);\n}\n\nfunction appendTypedAnchorParams({\n params,\n anchor,\n}: {\n params: URLSearchParams;\n anchor: object & { type: string };\n}): void {\n switch (anchor.type) {\n case 'page':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n break;\n case 'page_range':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'start_page',\n value: 'startPage' in anchor ? anchor.startPage : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'end_page',\n value: 'endPage' in anchor ? anchor.endPage : undefined,\n });\n break;\n case 'page_rect':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'x',\n value: 'x' in anchor ? anchor.x : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'y',\n value: 'y' in anchor ? anchor.y : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'width',\n value: 'width' in anchor ? anchor.width : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'height',\n value: 'height' in anchor ? anchor.height : undefined,\n });\n if (\n 'unit' in anchor &&\n (anchor.unit === 'percent' || anchor.unit === 'point')\n ) {\n params.set('unit', anchor.unit);\n }\n break;\n case 'page_text':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n if ('text' in anchor && typeof anchor.text === 'string') {\n params.set('text', anchor.text);\n }\n // Optional context that disambiguates a repeated phrase, and the hash of\n // the cited unit's normalized text; the embed parses both with the shared\n // `@athenaintel/references` parser.\n setNonEmptyStringParam({ params, key: 'prefix', value: 'prefix' in anchor ? anchor.prefix : undefined });\n setNonEmptyStringParam({ params, key: 'suffix', value: 'suffix' in anchor ? anchor.suffix : undefined });\n setNonEmptyStringParam({ params, key: 'hash', value: 'hash' in anchor ? anchor.hash : undefined });\n break;\n // Sheet anchors ride the same query contract the embed page parses with\n // the shared `@athenaintel/references` parser.\n case 'sheet_cell':\n case 'sheet_range':\n case 'sheet_tab':\n case 'sheet_table':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'sheet_id',\n value: 'sheetId' in anchor ? anchor.sheetId : undefined,\n });\n if ('cell' in anchor && typeof anchor.cell === 'string') {\n params.set('cell', anchor.cell);\n }\n if ('range' in anchor && typeof anchor.range === 'string') {\n params.set('range', anchor.range);\n }\n if ('tableName' in anchor && typeof anchor.tableName === 'string') {\n params.set('table_name', anchor.tableName);\n }\n if ('fallbackRange' in anchor && typeof anchor.fallbackRange === 'string') {\n params.set('fallback_range', anchor.fallbackRange);\n }\n break;\n }\n}\n\n/**\n * Writes a citation's place into an embed URL: its anchor and its excerpt (the passage the embed\n * finds by its words — alone, when the citation names no anchor). The anchor is either its raw\n * query string, forwarded as written whatever kind of place it names, or one of the typed page and\n * sheet anchors. Athena on the web's Spaces links read the same grammar, so a host that opens a\n * cited place there writes it the same way.\n */\nexport function appendCitationReferenceParams({\n params,\n reference,\n}: {\n params: URLSearchParams;\n reference?: AthenaAssetCitationReference;\n}): void {\n const anchor = reference?.anchor;\n if (typeof anchor === 'string') {\n appendRawAnchorQuery({ params, query: anchor });\n } else if (anchor && typeof anchor === 'object' && 'type' in anchor && typeof anchor.type === 'string') {\n appendTypedAnchorParams({ params, anchor: { ...anchor, type: anchor.type } });\n }\n\n const excerpt = reference?.meta?.excerpt;\n if (typeof excerpt === 'string' && excerpt) {\n params.set('excerpt', excerpt);\n }\n}\n\nexport function buildSdkAssetEmbedUrl(embedUrl: string, reference?: AthenaAssetCitationReference): string {\n try {\n const url = new URL(embedUrl);\n url.searchParams.set('athena_sdk_bridge', '1');\n appendCitationReferenceParams({ params: url.searchParams, reference });\n return url.toString();\n } catch {\n return embedUrl;\n }\n}\n\nconst getJwtPayload = (token: string): Record<string, unknown> | null => {\n try {\n const payloadSegment = token.split('.')[1];\n if (!payloadSegment || typeof atob !== 'function') return null;\n const normalized = payloadSegment.replace(/-/g, '+').replace(/_/g, '/');\n const payload: unknown = JSON.parse(\n atob(normalized.padEnd(normalized.length + ((4 - (normalized.length % 4)) % 4), '=')),\n );\n return payload && typeof payload === 'object' ? (payload as Record<string, unknown>) : null;\n } catch {\n return null;\n }\n};\n\nexport function getAssetEmbedAuthContext({ token, apiKey }: { token?: string | null; apiKey?: string }): string {\n if (!token) return apiKey ? `api-key:${apiKey}` : 'anonymous';\n\n const payload = getJwtPayload(token);\n const userId = payload?.user_id ?? payload?.sub;\n if (typeof userId !== 'string' || !userId) return `opaque-token:${token}`;\n\n return `jwt:${JSON.stringify({\n issuer: payload?.iss ?? null,\n userId,\n activeOrgId: payload?.active_org_id ?? payload?.org_id ?? null,\n workspaceId: payload?.workspace_id ?? null,\n orgMemberships: payload?.org_id_to_org_member_info ?? null,\n })}`;\n}\n\n/**\n * Hook to generate an embed URL for rendering an asset in an iframe.\n * Calls the Athena embed token endpoint.\n *\n * Note: The auth token is read via a ref and is intentionally NOT part\n * of the effect dependency array. PropelAuth and similar providers refresh\n * the token value periodically (e.g. on window focus), but the underlying\n * session is the same — re-running the effect on every token rotation\n * would force the iframe to reload unnecessarily. The effect only re-runs\n * when the auth _mode_ changes (auth vs anonymous) or the asset changes.\n */\nexport function useAssetEmbed(\n assetId: string | null,\n options: UseAssetEmbedOptions & {\n backendUrl: string;\n appUrl?: string;\n apiKey?: string;\n token?: string | null;\n /**\n * Live token resolver (the provider's `config.getToken`) that renews the\n * held `token`: read before the embed-token request, and asked for a\n * renewed token (`force: true`) when the backend refuses it. Ignored when\n * no token is held, so the request always authenticates as the identity\n * the embed cache is keyed on.\n */\n getToken?: AthenaStatewireTokenGetter;\n } = {\n backendUrl: '',\n },\n): UseAssetEmbedResult {\n const {\n readOnly = DEFAULT_ASSET_EMBED_READ_ONLY,\n expiresInSeconds = 60 * 60 * 24 * 30,\n displayMode = DEFAULT_ASSET_EMBED_DISPLAY_MODE,\n backendUrl,\n appUrl,\n apiKey,\n token,\n getToken,\n } = options;\n const [embedUrl, setEmbedUrl] = useState<string | null>(null);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const abortRef = useRef<AbortController | null>(null);\n\n // Keep the latest token in a ref so the effect can read it without\n // being re-triggered every time PropelAuth rotates the token value.\n const tokenRef = useRef(token);\n tokenRef.current = token;\n const getTokenRef = useRef(getToken);\n getTokenRef.current = getToken;\n\n // Stable across normal JWT rotations, but changes when the user or tenant\n // changes so a signed URL can never be reused across auth boundaries.\n const authContext = getAssetEmbedAuthContext({ token, apiKey });\n\n useEffect(() => {\n if (!assetId || !backendUrl) {\n setEmbedUrl(null);\n setIsLoading(false);\n setError(null);\n return;\n }\n\n const cacheKey = `${assetId}:${readOnly}:${displayMode}:${authContext}:${backendUrl}:${appUrl ?? ''}`;\n const cached = embedCache.get(cacheKey);\n if (cached && cached.expiresAt > Date.now() / 1000) {\n setEmbedUrl(cached.url);\n setIsLoading(false);\n setError(null);\n return;\n }\n\n // Derive the Athena API base from backendUrl before calling the embed endpoint.\n const apiBaseUrl = backendUrl.replace(/\\/api\\/assistant-ui\\/?$/, '');\n const endpoint = `${apiBaseUrl}/api/embed/generate-token`;\n\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n\n setIsLoading(true);\n setError(null);\n\n const heldToken = tokenRef.current;\n sendWithCredentialRenewal(\n { apiKey, token: heldToken, getToken: heldToken ? getTokenRef.current : undefined },\n (headers) =>\n fetch(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...headers },\n body: JSON.stringify(\n buildAssetEmbedRequestBody({\n assetId,\n readOnly,\n expiresInSeconds,\n displayMode,\n }),\n ),\n signal: controller.signal,\n }),\n )\n .then(async (res) => {\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`Failed to generate embed token (${res.status}): ${text}`);\n }\n return res.json();\n })\n .then((data: { embed_url: string; expires_at: number }) => {\n const resolvedEmbedUrl = resolveAssetEmbedUrl({\n embedUrl: data.embed_url,\n appUrl,\n backendUrl,\n });\n embedCache.set(cacheKey, {\n url: resolvedEmbedUrl,\n expiresAt: data.expires_at,\n });\n setEmbedUrl(resolvedEmbedUrl);\n setIsLoading(false);\n })\n .catch((err) => {\n if (err.name === 'AbortError') return;\n setError(err.message);\n setIsLoading(false);\n });\n\n return () => controller.abort();\n // Raw token intentionally excluded; authContext tracks user/tenant identity\n // while remaining stable across ordinary JWT refreshes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [assetId, readOnly, expiresInSeconds, displayMode, backendUrl, appUrl, authContext, apiKey]);\n\n return { embedUrl, isLoading, error };\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-asset-embed.js","sources":["../../src/assets/use-asset-embed.ts"],"sourcesContent":["import { QueryClient, QueryClientContext, useQuery } from '@tanstack/react-query';\nimport { useContext, useRef } from 'react';\nimport { deriveAthenaAppUrl } from '../lib/athena-urls';\nimport { keyPartFingerprint } from '../lib/use-athena-rest-query';\nimport { sendWithCredentialRenewal } from '../runtime/credential-renewal';\nimport type { AthenaStatewireTokenGetter } from '../runtime/statewire-auth-headers';\nimport type { AthenaAssetCitationReference } from './asset-embed-messages';\n\nexport interface UseAssetEmbedOptions {\n readOnly?: boolean;\n expiresInSeconds?: number;\n /** Native Olympus surface rendered by the iframe. Defaults to `full`. */\n displayMode?: 'minimal' | 'full';\n /**\n * Offer Quick Edit (⌘J, an AI rewrite of the selection) in a minimal embed, where the viewer\n * does not sign in: spreadsheets and Word documents, on an editable link (`readOnly: false`)\n * only. Its runs are billed to the asset's workspace under the link's creator, and each link has\n * its own hourly cap. The full mode's editors offer it to the signed-in person already. Defaults\n * to `false`.\n */\n allowQuickEdit?: boolean;\n}\n\nexport const DEFAULT_ASSET_EMBED_READ_ONLY = false;\nexport const DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT = false;\nexport const DEFAULT_ASSET_EMBED_DISPLAY_MODE = 'full' as const;\n\nexport interface UseAssetEmbedResult {\n embedUrl: string | null;\n isLoading: boolean;\n error: string | null;\n}\n\nexport function buildAssetEmbedRequestBody({\n assetId,\n readOnly,\n expiresInSeconds,\n displayMode,\n allowQuickEdit = DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT,\n}: {\n assetId: string;\n readOnly: boolean;\n expiresInSeconds: number;\n displayMode: 'minimal' | 'full';\n allowQuickEdit?: boolean;\n}) {\n return {\n asset_id: assetId,\n read_only: readOnly,\n expires_in_seconds: expiresInSeconds,\n display_mode: displayMode,\n // Sent only when asked: an agora that predates it is never handed an unknown field.\n ...(allowQuickEdit && { allow_quick_edit: true }),\n };\n}\n\n/**\n * The embed links' query client when the host provides none: one for the page, so every embed of\n * one asset, mode and identity shares one minted link until it expires.\n */\nconst embedQueryClient = new QueryClient();\n\nconst ATHENA_APP_HOSTNAMES = new Set(['app.athenaintel.com', 'staging-app.athenaintel.com']);\nconst ATHENA_PREVIEW_HOSTNAME_SUFFIX = '.previews.athenaintel.com';\nconst ATHENA_WORKSPACE_HOSTNAME_PATTERN = /^.+--(\\d+)\\.(?:app\\.athenaintel\\.com|staging-app\\.athenaintel\\.com)$/;\nconst OLYMPUS_WORKSPACE_PORT = '8082';\n\nconst getUrl = (value?: string | null): URL | null => {\n if (!value) {\n return null;\n }\n\n try {\n return new URL(value);\n } catch {\n return null;\n }\n};\n\nconst getCurrentOrigin = (): string | null => {\n if (typeof window === 'undefined') {\n return null;\n }\n\n return window.location.origin;\n};\n\nconst getOrigin = (value?: string | null): string | null => getUrl(value)?.origin ?? null;\n\nconst getAthenaWorkspacePort = (url: URL): string | null => {\n const match = url.hostname.toLowerCase().match(ATHENA_WORKSPACE_HOSTNAME_PATTERN);\n return match?.[1] ?? null;\n};\n\nconst isEmbedCapableAthenaHost = (url: URL): boolean => {\n const hostname = url.hostname.toLowerCase();\n\n if (ATHENA_APP_HOSTNAMES.has(hostname) || hostname.endsWith(ATHENA_PREVIEW_HOSTNAME_SUFFIX)) {\n return true;\n }\n\n return getAthenaWorkspacePort(url) === OLYMPUS_WORKSPACE_PORT;\n};\n\nconst shouldPreferDerivedEmbedAppUrl = ({\n appUrl,\n derivedAppUrl,\n currentOrigin,\n}: {\n appUrl: string;\n derivedAppUrl: string | null;\n currentOrigin?: string | null;\n}): boolean => {\n if (!derivedAppUrl) {\n return false;\n }\n\n const resolvedAppUrl = getUrl(appUrl);\n if (!resolvedAppUrl) {\n return true;\n }\n\n const workspacePort = getAthenaWorkspacePort(resolvedAppUrl);\n if (workspacePort && workspacePort !== OLYMPUS_WORKSPACE_PORT) {\n return true;\n }\n\n if (isEmbedCapableAthenaHost(resolvedAppUrl)) {\n return false;\n }\n\n const resolvedCurrentOrigin = getOrigin(currentOrigin ?? getCurrentOrigin());\n return !!resolvedCurrentOrigin && resolvedAppUrl.origin === resolvedCurrentOrigin;\n};\n\nconst resolveEmbedAppUrl = ({\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string | null => {\n const derivedAppUrl = deriveAthenaAppUrl({ backendUrl });\n\n if (!appUrl) {\n return derivedAppUrl;\n }\n\n if (shouldPreferDerivedEmbedAppUrl({ appUrl, derivedAppUrl, currentOrigin })) {\n return derivedAppUrl;\n }\n\n return appUrl;\n};\n\nexport function resolveAssetEmbedUrl({\n embedUrl,\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n embedUrl: string;\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string {\n try {\n const resolvedEmbedUrl = new URL(embedUrl);\n if (!resolvedEmbedUrl.pathname.startsWith('/embed/')) {\n return embedUrl;\n }\n\n const embedAppUrl = resolveEmbedAppUrl({\n appUrl,\n backendUrl,\n currentOrigin,\n });\n if (!embedAppUrl) {\n return embedUrl;\n }\n\n const resolvedAppUrl = new URL(embedAppUrl);\n resolvedEmbedUrl.protocol = resolvedAppUrl.protocol;\n resolvedEmbedUrl.host = resolvedAppUrl.host;\n return resolvedEmbedUrl.toString();\n } catch {\n return embedUrl;\n }\n}\n\nfunction setFiniteNumberParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'number' && Number.isFinite(value)) {\n params.set(key, String(value));\n }\n}\n\nfunction setNonEmptyStringParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'string' && value.length > 0) {\n params.set(key, value);\n }\n}\n\n/** The SDK's own embed parameter: it marks the bridge, never a place, so no anchor writes it. */\nconst SDK_BRIDGE_PARAM = 'athena_sdk_bridge';\n\n/**\n * The pairs of an anchor given as its raw query — the string an Athena link, citation or search\n * result carries (`anchor=slide&slide_index=3&slide_id=…`, `anchor=time&time=0:42`), alone or\n * after the `?` of an asset ref (`asset_x?anchor=…`) — read as URL parameters, or `null` when the\n * string holds none, or a parameter with no name. The SDK never interprets the anchor: the embed\n * page parses it with the references grammar, so every kind of place travels as written.\n */\nfunction rawAnchorQueryPairs(query: string): Array<[string, string]> | null {\n const queryIndex = query.indexOf('?');\n const search = queryIndex === -1 ? query : query.slice(queryIndex + 1);\n const pairs = [...new URLSearchParams(search)].filter(([key]) => key !== SDK_BRIDGE_PARAM);\n if (pairs.length === 0 || pairs.some(([key]) => key.length === 0)) return null;\n return pairs;\n}\n\nfunction appendRawAnchorQuery({ params, query }: { params: URLSearchParams; query: string }): void {\n const pairs = rawAnchorQueryPairs(query);\n if (!pairs) return;\n // Verbatim: a key the URL already has is replaced, and a repeated key keeps every value.\n for (const [key] of pairs) params.delete(key);\n for (const [key, value] of pairs) params.append(key, value);\n}\n\nfunction appendTypedAnchorParams({\n params,\n anchor,\n}: {\n params: URLSearchParams;\n anchor: object & { type: string };\n}): void {\n switch (anchor.type) {\n case 'page':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n break;\n case 'page_range':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'start_page',\n value: 'startPage' in anchor ? anchor.startPage : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'end_page',\n value: 'endPage' in anchor ? anchor.endPage : undefined,\n });\n break;\n case 'page_rect':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'x',\n value: 'x' in anchor ? anchor.x : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'y',\n value: 'y' in anchor ? anchor.y : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'width',\n value: 'width' in anchor ? anchor.width : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'height',\n value: 'height' in anchor ? anchor.height : undefined,\n });\n if (\n 'unit' in anchor &&\n (anchor.unit === 'percent' || anchor.unit === 'point')\n ) {\n params.set('unit', anchor.unit);\n }\n break;\n case 'page_text':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n if ('text' in anchor && typeof anchor.text === 'string') {\n params.set('text', anchor.text);\n }\n // Optional context that disambiguates a repeated phrase, and the hash of\n // the cited unit's normalized text; the embed parses both with the shared\n // `@athenaintel/references` parser.\n setNonEmptyStringParam({ params, key: 'prefix', value: 'prefix' in anchor ? anchor.prefix : undefined });\n setNonEmptyStringParam({ params, key: 'suffix', value: 'suffix' in anchor ? anchor.suffix : undefined });\n setNonEmptyStringParam({ params, key: 'hash', value: 'hash' in anchor ? anchor.hash : undefined });\n break;\n // Sheet anchors ride the same query contract the embed page parses with\n // the shared `@athenaintel/references` parser.\n case 'sheet_cell':\n case 'sheet_range':\n case 'sheet_tab':\n case 'sheet_table':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'sheet_id',\n value: 'sheetId' in anchor ? anchor.sheetId : undefined,\n });\n if ('cell' in anchor && typeof anchor.cell === 'string') {\n params.set('cell', anchor.cell);\n }\n if ('range' in anchor && typeof anchor.range === 'string') {\n params.set('range', anchor.range);\n }\n if ('tableName' in anchor && typeof anchor.tableName === 'string') {\n params.set('table_name', anchor.tableName);\n }\n if ('fallbackRange' in anchor && typeof anchor.fallbackRange === 'string') {\n params.set('fallback_range', anchor.fallbackRange);\n }\n break;\n }\n}\n\n/**\n * Writes a citation's place into an embed URL: its anchor and its excerpt (the passage the embed\n * finds by its words — alone, when the citation names no anchor). The anchor is either its raw\n * query string, forwarded as written whatever kind of place it names, or one of the typed page and\n * sheet anchors. Athena on the web's Spaces links read the same grammar, so a host that opens a\n * cited place there writes it the same way.\n */\nexport function appendCitationReferenceParams({\n params,\n reference,\n}: {\n params: URLSearchParams;\n reference?: AthenaAssetCitationReference;\n}): void {\n const anchor = reference?.anchor;\n if (typeof anchor === 'string') {\n appendRawAnchorQuery({ params, query: anchor });\n } else if (anchor && typeof anchor === 'object' && 'type' in anchor && typeof anchor.type === 'string') {\n appendTypedAnchorParams({ params, anchor: { ...anchor, type: anchor.type } });\n }\n\n const excerpt = reference?.meta?.excerpt;\n if (typeof excerpt === 'string' && excerpt) {\n params.set('excerpt', excerpt);\n }\n}\n\nexport function buildSdkAssetEmbedUrl(embedUrl: string, reference?: AthenaAssetCitationReference): string {\n try {\n const url = new URL(embedUrl);\n url.searchParams.set('athena_sdk_bridge', '1');\n appendCitationReferenceParams({ params: url.searchParams, reference });\n return url.toString();\n } catch {\n return embedUrl;\n }\n}\n\nconst getJwtPayload = (token: string): Record<string, unknown> | null => {\n try {\n const payloadSegment = token.split('.')[1];\n if (!payloadSegment || typeof atob !== 'function') return null;\n const normalized = payloadSegment.replace(/-/g, '+').replace(/_/g, '/');\n const payload: unknown = JSON.parse(\n atob(normalized.padEnd(normalized.length + ((4 - (normalized.length % 4)) % 4), '=')),\n );\n return payload && typeof payload === 'object' ? (payload as Record<string, unknown>) : null;\n } catch {\n return null;\n }\n};\n\nexport function getAssetEmbedAuthContext({ token, apiKey }: { token?: string | null; apiKey?: string }): string {\n if (!token) return apiKey ? `api-key:${apiKey}` : 'anonymous';\n\n const payload = getJwtPayload(token);\n const userId = payload?.user_id ?? payload?.sub;\n if (typeof userId !== 'string' || !userId) return `opaque-token:${token}`;\n\n return `jwt:${JSON.stringify({\n issuer: payload?.iss ?? null,\n userId,\n activeOrgId: payload?.active_org_id ?? payload?.org_id ?? null,\n workspaceId: payload?.workspace_id ?? null,\n orgMemberships: payload?.org_id_to_org_member_info ?? null,\n })}`;\n}\n\n/**\n * Hook to generate an embed URL for rendering an asset in an iframe.\n * Calls the Athena embed token endpoint through TanStack Query (the host's\n * QueryClient when there is one, else the page's embed client), keyed on the\n * asset, the mode and the identity.\n *\n * Note: The auth token is read via a ref and is intentionally NOT part\n * of the query key. PropelAuth and similar providers refresh the token value\n * periodically (e.g. on window focus), but the underlying session is the\n * same — minting again on every token rotation would force the iframe to\n * reload unnecessarily. The link is minted again only when the identity\n * (user or tenant), the asset or the mode changes.\n */\nexport function useAssetEmbed(\n assetId: string | null,\n options: UseAssetEmbedOptions & {\n backendUrl: string;\n appUrl?: string;\n apiKey?: string;\n token?: string | null;\n /**\n * Live token resolver (the provider's `config.getToken`) that renews the\n * held `token`: read before the embed-token request, and asked for a\n * renewed token (`force: true`) when the backend refuses it. Ignored when\n * no token is held, so the request always authenticates as the identity\n * the embed cache is keyed on.\n */\n getToken?: AthenaStatewireTokenGetter;\n } = {\n backendUrl: '',\n },\n): UseAssetEmbedResult {\n const {\n readOnly = DEFAULT_ASSET_EMBED_READ_ONLY,\n expiresInSeconds = 60 * 60 * 24 * 30,\n displayMode = DEFAULT_ASSET_EMBED_DISPLAY_MODE,\n allowQuickEdit = DEFAULT_ASSET_EMBED_ALLOW_QUICK_EDIT,\n backendUrl,\n appUrl,\n apiKey,\n token,\n getToken,\n } = options;\n // Keep the latest token in a ref so the query can read it without being\n // keyed on it: PropelAuth and similar providers rotate the token value.\n const tokenRef = useRef(token);\n tokenRef.current = token;\n const getTokenRef = useRef(getToken);\n getTokenRef.current = getToken;\n\n // Stable across normal JWT rotations, but changes when the user or tenant\n // changes so a signed URL can never be reused across auth boundaries.\n const authContext = getAssetEmbedAuthContext({ token, apiKey });\n const enabled = Boolean(assetId && backendUrl);\n\n const query = useQuery(\n {\n queryKey: [\n 'athena-sdk',\n 'asset-embed',\n assetId,\n readOnly,\n displayMode,\n allowQuickEdit,\n // The identity, never the credential: an opaque token or a key is part of `authContext`.\n keyPartFingerprint(authContext),\n backendUrl,\n appUrl ?? '',\n ],\n queryFn: async ({ signal }): Promise<{ url: string; expiresAt: number }> => {\n // Derive the Athena API base from backendUrl before calling the embed endpoint.\n const apiBaseUrl = backendUrl.replace(/\\/api\\/assistant-ui\\/?$/, '');\n const heldToken = tokenRef.current;\n const res = await sendWithCredentialRenewal(\n { apiKey, token: heldToken, getToken: heldToken ? getTokenRef.current : undefined },\n (headers) =>\n fetch(`${apiBaseUrl}/api/embed/generate-token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...headers },\n body: JSON.stringify(\n buildAssetEmbedRequestBody({\n assetId: assetId ?? '',\n readOnly,\n expiresInSeconds,\n displayMode,\n allowQuickEdit,\n }),\n ),\n signal,\n }),\n );\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`Failed to generate embed token (${res.status}): ${text}`);\n }\n const data: { embed_url: string; expires_at: number } = await res.json();\n return {\n url: resolveAssetEmbedUrl({ embedUrl: data.embed_url, appUrl, backendUrl }),\n expiresAt: data.expires_at,\n };\n },\n enabled,\n // A minted link is good until it expires; a new one would reload the frame, so it is never\n // minted again while the embed shows (no refetch on focus or reconnect) and never retried.\n staleTime: (cached) =>\n cached.state.data ? Math.max(0, cached.state.data.expiresAt * 1000 - Date.now()) : 0,\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n retry: false,\n },\n useContext(QueryClientContext) ?? embedQueryClient,\n );\n\n return {\n embedUrl: enabled ? (query.data?.url ?? null) : null,\n isLoading: enabled && query.isLoading,\n error: enabled && query.error ? query.error.message : null,\n };\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const statewireRunConfig = require("../runtime/statewire-run-config.cjs");
|
|
4
|
+
function allowsAnyModel(value) {
|
|
5
|
+
return value !== false;
|
|
6
|
+
}
|
|
7
|
+
const LEGACY_DEFAULT_ENTRY_ID = "default";
|
|
8
|
+
const LEGACY_DEFAULT_ENTRY_LABEL = "Default";
|
|
9
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10
|
+
const nonBlank = (value) => typeof value === "string" && value.trim() !== "" ? value.trim() : null;
|
|
11
|
+
function agentAssetIdOf(agent) {
|
|
12
|
+
const trimmed = nonBlank(agent);
|
|
13
|
+
if (trimmed === null) return null;
|
|
14
|
+
return statewireRunConfig.parseCollabAgentRef(trimmed) ?? (trimmed.includes(":") ? null : trimmed);
|
|
15
|
+
}
|
|
16
|
+
function labeledModelsOf(value) {
|
|
17
|
+
let rows = value;
|
|
18
|
+
if (typeof rows === "string") {
|
|
19
|
+
try {
|
|
20
|
+
rows = JSON.parse(rows);
|
|
21
|
+
} catch {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (!Array.isArray(rows)) return [];
|
|
26
|
+
const entries = [];
|
|
27
|
+
for (const row of rows) {
|
|
28
|
+
if (!isRecord(row)) continue;
|
|
29
|
+
const model = nonBlank(row.model);
|
|
30
|
+
if (model === null) continue;
|
|
31
|
+
entries.push({
|
|
32
|
+
id: typeof row.id === "string" && row.id !== "" ? row.id : model,
|
|
33
|
+
model,
|
|
34
|
+
label: nonBlank(row.label) ?? model,
|
|
35
|
+
isDefault: row.isDefault === true
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return entries;
|
|
39
|
+
}
|
|
40
|
+
function channelModelsOf(value) {
|
|
41
|
+
if (!isRecord(value)) return {};
|
|
42
|
+
const out = {};
|
|
43
|
+
for (const [channel, override] of Object.entries(value)) {
|
|
44
|
+
const model = isRecord(override) ? nonBlank(override.model) : null;
|
|
45
|
+
if (model !== null) out[channel] = model;
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
function isPublishedAgent(athenaMetadata) {
|
|
50
|
+
if (!isRecord(athenaMetadata)) return false;
|
|
51
|
+
const versionId = athenaMetadata.published_version_id;
|
|
52
|
+
return typeof versionId === "string" && versionId !== "" || typeof versionId === "number" && Number.isFinite(versionId);
|
|
53
|
+
}
|
|
54
|
+
function publishedAgentModelSource(athenaMetadata) {
|
|
55
|
+
if (!isRecord(athenaMetadata) || !isPublishedAgent(athenaMetadata)) return null;
|
|
56
|
+
const config = athenaMetadata.published_config;
|
|
57
|
+
if (!isRecord(config)) return null;
|
|
58
|
+
const source = {
|
|
59
|
+
model: nonBlank(config.model),
|
|
60
|
+
models: labeledModelsOf(config.models),
|
|
61
|
+
channelModels: channelModelsOf(config.channelOverrides),
|
|
62
|
+
// The editor stores the field name; agora also accepts its snake-case alias.
|
|
63
|
+
allowAnyModel: allowsAnyModel(
|
|
64
|
+
Object.hasOwn(config, "allowAnyModel") ? config.allowAnyModel : config.allow_any_model
|
|
65
|
+
)
|
|
66
|
+
};
|
|
67
|
+
const namesAModel = source.model !== null || source.models.length > 0 || Object.keys(source.channelModels).length > 0;
|
|
68
|
+
return namesAModel ? source : null;
|
|
69
|
+
}
|
|
70
|
+
function isAutomaticModelLabel({
|
|
71
|
+
entry,
|
|
72
|
+
modelLabel
|
|
73
|
+
}) {
|
|
74
|
+
const label = entry.label.trim();
|
|
75
|
+
if (label === "") return true;
|
|
76
|
+
if (entry.id === LEGACY_DEFAULT_ENTRY_ID && label === LEGACY_DEFAULT_ENTRY_LABEL) return true;
|
|
77
|
+
return label === entry.model || label === modelLabel(entry.model);
|
|
78
|
+
}
|
|
79
|
+
function agentDefaultModel({
|
|
80
|
+
source,
|
|
81
|
+
channel
|
|
82
|
+
}) {
|
|
83
|
+
const marked = source.models.find((entry) => entry.isDefault) ?? source.models[0];
|
|
84
|
+
return (channel ? source.channelModels[channel] : void 0) ?? source.model ?? (marked == null ? void 0 : marked.model) ?? null;
|
|
85
|
+
}
|
|
86
|
+
function buildAgentModelOptions({
|
|
87
|
+
source,
|
|
88
|
+
channel,
|
|
89
|
+
pinnedModel,
|
|
90
|
+
modelLabel
|
|
91
|
+
}) {
|
|
92
|
+
const options = [];
|
|
93
|
+
const add = (model, label) => {
|
|
94
|
+
if (options.some((option) => option.model === model)) return;
|
|
95
|
+
options.push({ model, label, isDefault: false, pinnedOutside: false });
|
|
96
|
+
};
|
|
97
|
+
for (const entry of source.models) {
|
|
98
|
+
add(
|
|
99
|
+
entry.model,
|
|
100
|
+
isAutomaticModelLabel({ entry, modelLabel }) ? modelLabel(entry.model) : entry.label.trim()
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
if (options.length === 0 && source.model !== null) add(source.model, modelLabel(source.model));
|
|
104
|
+
const defaultModel = agentDefaultModel({ source, channel });
|
|
105
|
+
if (defaultModel !== null) {
|
|
106
|
+
add(defaultModel, modelLabel(defaultModel));
|
|
107
|
+
for (const option of options) option.isDefault = option.model === defaultModel;
|
|
108
|
+
}
|
|
109
|
+
const pinned = nonBlank(pinnedModel);
|
|
110
|
+
if (pinned !== null && !options.some((option) => option.model === pinned)) {
|
|
111
|
+
options.push({ model: pinned, label: modelLabel(pinned), isDefault: false, pinnedOutside: true });
|
|
112
|
+
}
|
|
113
|
+
return { options, defaultModel };
|
|
114
|
+
}
|
|
115
|
+
exports.agentAssetIdOf = agentAssetIdOf;
|
|
116
|
+
exports.agentDefaultModel = agentDefaultModel;
|
|
117
|
+
exports.allowsAnyModel = allowsAnyModel;
|
|
118
|
+
exports.buildAgentModelOptions = buildAgentModelOptions;
|
|
119
|
+
exports.isAutomaticModelLabel = isAutomaticModelLabel;
|
|
120
|
+
exports.isPublishedAgent = isPublishedAgent;
|
|
121
|
+
exports.publishedAgentModelSource = publishedAgentModelSource;
|
|
122
|
+
//# sourceMappingURL=agent-model-options.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-model-options.cjs","sources":["../../src/catalog/agent-model-options.ts"],"sourcesContent":["import { parseCollabAgentRef } from '../runtime/statewire-run-config';\n\n/**\n * A collab agent's own model choices, from its published definition: exactly\n * the models its author listed (the agent editor's Models section), the\n * default marked. The rules are Athena's own composer's (Olympus) and the\n * Jevin app's, so every host offers the same choices for one agent:\n *\n * - the options are the agent's labeled models, by the author's labels; an\n * automatic name (empty, the editor's old `{ id: 'default', label: 'Default' }`,\n * or one that only restates the model) shows the model's catalog label;\n * - a legacy agent with no labeled list offers its single `model`;\n * - the session channel's model override (`channelOverrides[channel].model`)\n * is that channel's default, added when the list lacks it;\n * - a model the thread pins that is none of the agent's models is shown as it\n * is (`pinnedOutside`), never replaced.\n *\n * Restricting a picker to these choices is opt-in per agent: only an agent\n * whose \"Allow any workspace model\" switch is off (`allowAnyModel: false`)\n * does; every other agent keeps the workspace's models.\n *\n * Pure, for `useAthenaAgentModelOptions` and the tests.\n */\n\n/** One model choice of a collab agent. */\nexport interface AthenaAgentModelOption {\n /** The model id a send carries (`model`). */\n model: string;\n /** The author's label, or the model's catalog label for an automatic name. */\n label: string;\n /** The model the thread runs while it pins none (the agent's own, or its channel's). */\n isDefault: boolean;\n /**\n * A model the thread's server config pins that is none of the agent's models\n * (pinned by another client, or by an older picker): shown, never replaced.\n */\n pinnedOutside: boolean;\n}\n\n/** One entry of the agent's labeled model list. */\nexport interface AgentLabeledModel {\n id: string;\n model: string;\n label: string;\n isDefault: boolean;\n}\n\n/** What an agent's published definition says about its models. */\nexport interface AgentModelSource {\n /** The model the definition runs (`config.model`); `null` when it names none. */\n model: string | null;\n /** The labeled list (`config.models`), complete rows only, in the author's order. */\n models: readonly AgentLabeledModel[];\n /** Per-channel model overrides (`channelOverrides[<channel>].model`), by channel id. */\n channelModels: Readonly<Record<string, string>>;\n /**\n * Whether people may pick any workspace model (`config.allowAnyModel`, the\n * agent editor's \"Allow any workspace model\" switch; see\n * {@link allowsAnyModel}). Only `false` restricts a picker to the agent's\n * own models.\n */\n allowAnyModel: boolean;\n}\n\n/**\n * Whether a config's `allowAnyModel` value lets people pick any model: only an\n * explicit `false` restricts; absent, `null`, `true` and any non-boolean allow\n * (agora's `collab_agent_allows_any_model`), so every agent saved before the\n * switch existed behaves as it always has.\n */\nexport function allowsAnyModel(value: unknown): boolean {\n return value !== false;\n}\n\n/** The display label of a model id; the id itself when the catalog does not know it. */\nexport type ModelLabelOf = (model: string) => string;\n\n/** The row the agent editor used to create for an agent with no stored entries. */\nconst LEGACY_DEFAULT_ENTRY_ID = 'default';\nconst LEGACY_DEFAULT_ENTRY_LABEL = 'Default';\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\nconst nonBlank = (value: unknown): string | null =>\n typeof value === 'string' && value.trim() !== '' ? value.trim() : null;\n\n/**\n * The collab agent's asset id for an `agent` value: a `collab_agent:<id>` ref\n * (the provider's `agent` shape) or the bare asset id (the agent picker's\n * value). `null` for a blank value.\n */\nexport function agentAssetIdOf(agent: string | null | undefined): string | null {\n const trimmed = nonBlank(agent);\n if (trimmed === null) return null;\n return parseCollabAgentRef(trimmed) ?? (trimmed.includes(':') ? null : trimmed);\n}\n\n/** A stored `models` value: an array of rows, JSON-encoded or not; malformed rows dropped. */\nfunction labeledModelsOf(value: unknown): AgentLabeledModel[] {\n let rows: unknown = value;\n if (typeof rows === 'string') {\n try {\n rows = JSON.parse(rows);\n } catch {\n return [];\n }\n }\n if (!Array.isArray(rows)) return [];\n const entries: AgentLabeledModel[] = [];\n for (const row of rows) {\n if (!isRecord(row)) continue;\n const model = nonBlank(row.model);\n if (model === null) continue;\n entries.push({\n id: typeof row.id === 'string' && row.id !== '' ? row.id : model,\n model,\n label: nonBlank(row.label) ?? model,\n isDefault: row.isDefault === true,\n });\n }\n return entries;\n}\n\n/** A stored `channelOverrides` value's per-channel models. */\nfunction channelModelsOf(value: unknown): Record<string, string> {\n if (!isRecord(value)) return {};\n const out: Record<string, string> = {};\n for (const [channel, override] of Object.entries(value)) {\n const model = isRecord(override) ? nonBlank(override.model) : null;\n if (model !== null) out[channel] = model;\n }\n return out;\n}\n\n/** Whether the agent asset's `athena_metadata` points at a published version. */\nexport function isPublishedAgent(athenaMetadata: unknown): boolean {\n if (!isRecord(athenaMetadata)) return false;\n const versionId = athenaMetadata.published_version_id;\n return (\n (typeof versionId === 'string' && versionId !== '') ||\n (typeof versionId === 'number' && Number.isFinite(versionId))\n );\n}\n\n/**\n * The model source of the agent's PUBLISHED definition, which every SDK run\n * compiles, from the agent asset's `athena_metadata` (publish writes\n * `published_config` beside `published_version_id`). `null` for an agent\n * that was never published, or whose published definition names no model\n * (not even a channel's).\n */\nexport function publishedAgentModelSource(athenaMetadata: unknown): AgentModelSource | null {\n if (!isRecord(athenaMetadata) || !isPublishedAgent(athenaMetadata)) return null;\n const config = athenaMetadata.published_config;\n if (!isRecord(config)) return null;\n const source: AgentModelSource = {\n model: nonBlank(config.model),\n models: labeledModelsOf(config.models),\n channelModels: channelModelsOf(config.channelOverrides),\n // The editor stores the field name; agora also accepts its snake-case alias.\n allowAnyModel: allowsAnyModel(\n Object.hasOwn(config, 'allowAnyModel') ? config.allowAnyModel : config.allow_any_model,\n ),\n };\n const namesAModel =\n source.model !== null ||\n source.models.length > 0 ||\n Object.keys(source.channelModels).length > 0;\n return namesAModel ? source : null;\n}\n\n/**\n * Whether an entry's name is automatic: empty, the editor's old \"Default\", or\n * a name that only restates the entry's model (its id or catalog label).\n */\nexport function isAutomaticModelLabel({\n entry,\n modelLabel,\n}: {\n entry: Pick<AgentLabeledModel, 'id' | 'model' | 'label'>;\n modelLabel: ModelLabelOf;\n}): boolean {\n const label = entry.label.trim();\n if (label === '') return true;\n if (entry.id === LEGACY_DEFAULT_ENTRY_ID && label === LEGACY_DEFAULT_ENTRY_LABEL) return true;\n return label === entry.model || label === modelLabel(entry.model);\n}\n\n/**\n * The model a thread runs while it pins none: the channel's override, else the\n * definition's `model`, else its marked (or first) labeled entry.\n */\nexport function agentDefaultModel({\n source,\n channel,\n}: {\n source: AgentModelSource;\n channel: string | null;\n}): string | null {\n const marked = source.models.find((entry) => entry.isDefault) ?? source.models[0];\n return (channel ? source.channelModels[channel] : undefined) ?? source.model ?? marked?.model ?? null;\n}\n\n/**\n * The agent's model choices and its default, by the rules above. Options keep\n * the author's order, one per model (the first entry of a model names it); the\n * default is marked, and appended when the list lacks it; a pinned model\n * outside the list comes last.\n */\nexport function buildAgentModelOptions({\n source,\n channel,\n pinnedModel,\n modelLabel,\n}: {\n source: AgentModelSource;\n channel: string | null;\n /** The model the thread's server config pins; `null` while it pins none (or is unknown). */\n pinnedModel: string | null;\n modelLabel: ModelLabelOf;\n}): { options: AthenaAgentModelOption[]; defaultModel: string | null } {\n const options: AthenaAgentModelOption[] = [];\n const add = (model: string, label: string) => {\n if (options.some((option) => option.model === model)) return;\n options.push({ model, label, isDefault: false, pinnedOutside: false });\n };\n for (const entry of source.models) {\n add(\n entry.model,\n isAutomaticModelLabel({ entry, modelLabel }) ? modelLabel(entry.model) : entry.label.trim(),\n );\n }\n if (options.length === 0 && source.model !== null) add(source.model, modelLabel(source.model));\n\n const defaultModel = agentDefaultModel({ source, channel });\n if (defaultModel !== null) {\n add(defaultModel, modelLabel(defaultModel));\n for (const option of options) option.isDefault = option.model === defaultModel;\n }\n\n const pinned = nonBlank(pinnedModel);\n if (pinned !== null && !options.some((option) => option.model === pinned)) {\n options.push({ model: pinned, label: modelLabel(pinned), isDefault: false, pinnedOutside: true });\n }\n return { options, defaultModel };\n}\n"],"names":["parseCollabAgentRef"],"mappings":"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"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A collab agent's own model choices, from its published definition: exactly
|
|
3
|
+
* the models its author listed (the agent editor's Models section), the
|
|
4
|
+
* default marked. The rules are Athena's own composer's (Olympus) and the
|
|
5
|
+
* Jevin app's, so every host offers the same choices for one agent:
|
|
6
|
+
*
|
|
7
|
+
* - the options are the agent's labeled models, by the author's labels; an
|
|
8
|
+
* automatic name (empty, the editor's old `{ id: 'default', label: 'Default' }`,
|
|
9
|
+
* or one that only restates the model) shows the model's catalog label;
|
|
10
|
+
* - a legacy agent with no labeled list offers its single `model`;
|
|
11
|
+
* - the session channel's model override (`channelOverrides[channel].model`)
|
|
12
|
+
* is that channel's default, added when the list lacks it;
|
|
13
|
+
* - a model the thread pins that is none of the agent's models is shown as it
|
|
14
|
+
* is (`pinnedOutside`), never replaced.
|
|
15
|
+
*
|
|
16
|
+
* Restricting a picker to these choices is opt-in per agent: only an agent
|
|
17
|
+
* whose "Allow any workspace model" switch is off (`allowAnyModel: false`)
|
|
18
|
+
* does; every other agent keeps the workspace's models.
|
|
19
|
+
*
|
|
20
|
+
* Pure, for `useAthenaAgentModelOptions` and the tests.
|
|
21
|
+
*/
|
|
22
|
+
/** One model choice of a collab agent. */
|
|
23
|
+
export interface AthenaAgentModelOption {
|
|
24
|
+
/** The model id a send carries (`model`). */
|
|
25
|
+
model: string;
|
|
26
|
+
/** The author's label, or the model's catalog label for an automatic name. */
|
|
27
|
+
label: string;
|
|
28
|
+
/** The model the thread runs while it pins none (the agent's own, or its channel's). */
|
|
29
|
+
isDefault: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* A model the thread's server config pins that is none of the agent's models
|
|
32
|
+
* (pinned by another client, or by an older picker): shown, never replaced.
|
|
33
|
+
*/
|
|
34
|
+
pinnedOutside: boolean;
|
|
35
|
+
}
|
|
36
|
+
/** One entry of the agent's labeled model list. */
|
|
37
|
+
export interface AgentLabeledModel {
|
|
38
|
+
id: string;
|
|
39
|
+
model: string;
|
|
40
|
+
label: string;
|
|
41
|
+
isDefault: boolean;
|
|
42
|
+
}
|
|
43
|
+
/** What an agent's published definition says about its models. */
|
|
44
|
+
export interface AgentModelSource {
|
|
45
|
+
/** The model the definition runs (`config.model`); `null` when it names none. */
|
|
46
|
+
model: string | null;
|
|
47
|
+
/** The labeled list (`config.models`), complete rows only, in the author's order. */
|
|
48
|
+
models: readonly AgentLabeledModel[];
|
|
49
|
+
/** Per-channel model overrides (`channelOverrides[<channel>].model`), by channel id. */
|
|
50
|
+
channelModels: Readonly<Record<string, string>>;
|
|
51
|
+
/**
|
|
52
|
+
* Whether people may pick any workspace model (`config.allowAnyModel`, the
|
|
53
|
+
* agent editor's "Allow any workspace model" switch; see
|
|
54
|
+
* {@link allowsAnyModel}). Only `false` restricts a picker to the agent's
|
|
55
|
+
* own models.
|
|
56
|
+
*/
|
|
57
|
+
allowAnyModel: boolean;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Whether a config's `allowAnyModel` value lets people pick any model: only an
|
|
61
|
+
* explicit `false` restricts; absent, `null`, `true` and any non-boolean allow
|
|
62
|
+
* (agora's `collab_agent_allows_any_model`), so every agent saved before the
|
|
63
|
+
* switch existed behaves as it always has.
|
|
64
|
+
*/
|
|
65
|
+
export declare function allowsAnyModel(value: unknown): boolean;
|
|
66
|
+
/** The display label of a model id; the id itself when the catalog does not know it. */
|
|
67
|
+
export type ModelLabelOf = (model: string) => string;
|
|
68
|
+
/**
|
|
69
|
+
* The collab agent's asset id for an `agent` value: a `collab_agent:<id>` ref
|
|
70
|
+
* (the provider's `agent` shape) or the bare asset id (the agent picker's
|
|
71
|
+
* value). `null` for a blank value.
|
|
72
|
+
*/
|
|
73
|
+
export declare function agentAssetIdOf(agent: string | null | undefined): string | null;
|
|
74
|
+
/** Whether the agent asset's `athena_metadata` points at a published version. */
|
|
75
|
+
export declare function isPublishedAgent(athenaMetadata: unknown): boolean;
|
|
76
|
+
/**
|
|
77
|
+
* The model source of the agent's PUBLISHED definition, which every SDK run
|
|
78
|
+
* compiles, from the agent asset's `athena_metadata` (publish writes
|
|
79
|
+
* `published_config` beside `published_version_id`). `null` for an agent
|
|
80
|
+
* that was never published, or whose published definition names no model
|
|
81
|
+
* (not even a channel's).
|
|
82
|
+
*/
|
|
83
|
+
export declare function publishedAgentModelSource(athenaMetadata: unknown): AgentModelSource | null;
|
|
84
|
+
/**
|
|
85
|
+
* Whether an entry's name is automatic: empty, the editor's old "Default", or
|
|
86
|
+
* a name that only restates the entry's model (its id or catalog label).
|
|
87
|
+
*/
|
|
88
|
+
export declare function isAutomaticModelLabel({ entry, modelLabel, }: {
|
|
89
|
+
entry: Pick<AgentLabeledModel, 'id' | 'model' | 'label'>;
|
|
90
|
+
modelLabel: ModelLabelOf;
|
|
91
|
+
}): boolean;
|
|
92
|
+
/**
|
|
93
|
+
* The model a thread runs while it pins none: the channel's override, else the
|
|
94
|
+
* definition's `model`, else its marked (or first) labeled entry.
|
|
95
|
+
*/
|
|
96
|
+
export declare function agentDefaultModel({ source, channel, }: {
|
|
97
|
+
source: AgentModelSource;
|
|
98
|
+
channel: string | null;
|
|
99
|
+
}): string | null;
|
|
100
|
+
/**
|
|
101
|
+
* The agent's model choices and its default, by the rules above. Options keep
|
|
102
|
+
* the author's order, one per model (the first entry of a model names it); the
|
|
103
|
+
* default is marked, and appended when the list lacks it; a pinned model
|
|
104
|
+
* outside the list comes last.
|
|
105
|
+
*/
|
|
106
|
+
export declare function buildAgentModelOptions({ source, channel, pinnedModel, modelLabel, }: {
|
|
107
|
+
source: AgentModelSource;
|
|
108
|
+
channel: string | null;
|
|
109
|
+
/** The model the thread's server config pins; `null` while it pins none (or is unknown). */
|
|
110
|
+
pinnedModel: string | null;
|
|
111
|
+
modelLabel: ModelLabelOf;
|
|
112
|
+
}): {
|
|
113
|
+
options: AthenaAgentModelOption[];
|
|
114
|
+
defaultModel: string | null;
|
|
115
|
+
};
|