@athenaintel/react 0.59.0 → 0.60.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 +124 -20
- package/dist/agent-runtime/src/client-tool-bridge.cjs +20 -2
- package/dist/agent-runtime/src/client-tool-bridge.cjs.map +1 -1
- package/dist/agent-runtime/src/client-tool-bridge.d.ts +9 -4
- package/dist/agent-runtime/src/client-tool-bridge.js +20 -2
- package/dist/agent-runtime/src/client-tool-bridge.js.map +1 -1
- package/dist/agent-runtime/src/client-tools.cjs.map +1 -1
- package/dist/agent-runtime/src/client-tools.d.ts +9 -0
- package/dist/agent-runtime/src/client-tools.js.map +1 -1
- package/dist/agent-runtime/src/composer-markdown.cjs +0 -16
- package/dist/agent-runtime/src/composer-markdown.cjs.map +1 -1
- package/dist/agent-runtime/src/composer-markdown.js +1 -17
- package/dist/agent-runtime/src/composer-markdown.js.map +1 -1
- package/dist/agent-runtime/src/named-agents.js +1 -1
- package/dist/agent-runtime/src/offline-status.cjs.map +1 -1
- package/dist/agent-runtime/src/offline-status.d.ts +27 -0
- package/dist/agent-runtime/src/offline-status.js.map +1 -1
- package/dist/agent-runtime/src/tool-metadata.cjs +7 -0
- package/dist/agent-runtime/src/tool-metadata.cjs.map +1 -1
- package/dist/agent-runtime/src/tool-metadata.js +7 -0
- package/dist/agent-runtime/src/tool-metadata.js.map +1 -1
- package/dist/chat/AthenaChat.cjs +17 -5
- package/dist/chat/AthenaChat.cjs.map +1 -1
- package/dist/chat/AthenaChat.js +18 -6
- package/dist/chat/AthenaChat.js.map +1 -1
- package/dist/chat/StatewireClientToolBridge.cjs +69 -1
- package/dist/chat/StatewireClientToolBridge.cjs.map +1 -1
- package/dist/chat/StatewireClientToolBridge.d.ts +14 -1
- package/dist/chat/StatewireClientToolBridge.js +70 -2
- package/dist/chat/StatewireClientToolBridge.js.map +1 -1
- package/dist/chat/StatewireClientToolConsentCard.cjs +65 -0
- package/dist/chat/StatewireClientToolConsentCard.cjs.map +1 -0
- package/dist/chat/StatewireClientToolConsentCard.d.ts +24 -0
- package/dist/chat/StatewireClientToolConsentCard.js +65 -0
- package/dist/chat/StatewireClientToolConsentCard.js.map +1 -0
- package/dist/chat/client-tool-write-consent.cjs +111 -0
- package/dist/chat/client-tool-write-consent.cjs.map +1 -0
- package/dist/chat/client-tool-write-consent.d.ts +71 -0
- package/dist/chat/client-tool-write-consent.js +111 -0
- package/dist/chat/client-tool-write-consent.js.map +1 -0
- package/dist/chat/statewire-client-tools.cjs +30 -5
- package/dist/chat/statewire-client-tools.cjs.map +1 -1
- package/dist/chat/statewire-client-tools.d.ts +34 -2
- package/dist/chat/statewire-client-tools.js +30 -5
- package/dist/chat/statewire-client-tools.js.map +1 -1
- package/dist/chat-ui/index.cjs +2 -0
- package/dist/chat-ui/index.cjs.map +1 -1
- package/dist/chat-ui/index.d.ts +1 -0
- package/dist/chat-ui/index.js +2 -0
- package/dist/chat-ui/index.js.map +1 -1
- package/dist/chat-ui/mentions/mention-chip.cjs +9 -3
- package/dist/chat-ui/mentions/mention-chip.cjs.map +1 -1
- package/dist/chat-ui/mentions/mention-chip.js +9 -3
- package/dist/chat-ui/mentions/mention-chip.js.map +1 -1
- package/dist/chat-ui/mentions/suggestions/components/mention-icon.cjs +18 -2
- package/dist/chat-ui/mentions/suggestions/components/mention-icon.cjs.map +1 -1
- package/dist/chat-ui/mentions/suggestions/components/mention-icon.d.ts +10 -1
- package/dist/chat-ui/mentions/suggestions/components/mention-icon.js +19 -3
- package/dist/chat-ui/mentions/suggestions/components/mention-icon.js.map +1 -1
- package/dist/chat-ui/mentions/suggestions/data/api-client.cjs +9 -0
- package/dist/chat-ui/mentions/suggestions/data/api-client.cjs.map +1 -1
- package/dist/chat-ui/mentions/suggestions/data/api-client.d.ts +6 -0
- package/dist/chat-ui/mentions/suggestions/data/api-client.js +9 -0
- package/dist/chat-ui/mentions/suggestions/data/api-client.js.map +1 -1
- package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icon-names.generated.cjs +89 -0
- package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icon-names.generated.cjs.map +1 -0
- package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icon-names.generated.d.ts +8 -0
- package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icon-names.generated.js +89 -0
- package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icon-names.generated.js.map +1 -0
- package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.cjs +153 -116
- package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.cjs.map +1 -1
- package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.d.ts +58 -12
- package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.js +154 -117
- package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.js.map +1 -1
- package/dist/chat-ui/mentions/suggestions/data/sources/index.cjs +38 -6
- package/dist/chat-ui/mentions/suggestions/data/sources/index.cjs.map +1 -1
- package/dist/chat-ui/mentions/suggestions/data/sources/index.js +40 -8
- package/dist/chat-ui/mentions/suggestions/data/sources/index.js.map +1 -1
- package/dist/chat-ui/mentions/use-asset-icon-app-url.cjs +13 -0
- package/dist/chat-ui/mentions/use-asset-icon-app-url.cjs.map +1 -0
- package/dist/chat-ui/mentions/use-asset-icon-app-url.d.ts +9 -0
- package/dist/chat-ui/mentions/use-asset-icon-app-url.js +13 -0
- package/dist/chat-ui/mentions/use-asset-icon-app-url.js.map +1 -0
- package/dist/chat-ui/statewire.cjs +4 -0
- package/dist/chat-ui/statewire.cjs.map +1 -1
- package/dist/chat-ui/statewire.d.ts +2 -0
- package/dist/chat-ui/statewire.js +4 -0
- package/dist/chat-ui/statewire.js.map +1 -1
- package/dist/collab/client.cjs +24 -1
- package/dist/collab/client.cjs.map +1 -1
- package/dist/collab/client.d.ts +16 -0
- package/dist/collab/client.js +24 -1
- package/dist/collab/client.js.map +1 -1
- package/dist/composer/attached-references.js +1 -1
- package/dist/composer/staged-message-metadata.cjs +12 -0
- package/dist/composer/staged-message-metadata.cjs.map +1 -0
- package/dist/composer/staged-message-metadata.d.ts +15 -0
- package/dist/composer/staged-message-metadata.js +12 -0
- package/dist/composer/staged-message-metadata.js.map +1 -0
- package/dist/data/athena-request.cjs +2 -0
- package/dist/data/athena-request.cjs.map +1 -1
- package/dist/data/athena-request.js +2 -0
- package/dist/data/athena-request.js.map +1 -1
- package/dist/index.cjs +5 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/provider/AthenaProvider.cjs +26 -6
- package/dist/provider/AthenaProvider.cjs.map +1 -1
- package/dist/provider/AthenaProvider.d.ts +8 -1
- package/dist/provider/AthenaProvider.js +27 -7
- package/dist/provider/AthenaProvider.js.map +1 -1
- package/dist/react/package.json.cjs +1 -1
- package/dist/react/package.json.js +1 -1
- package/dist/references/src/anchors/parse.cjs +11 -1
- package/dist/references/src/anchors/parse.cjs.map +1 -1
- package/dist/references/src/anchors/parse.js +11 -1
- package/dist/references/src/anchors/parse.js.map +1 -1
- package/dist/references/src/anchors/schemas.d.ts +1 -0
- package/dist/references/src/anchors/serialize.cjs +1 -0
- package/dist/references/src/anchors/serialize.cjs.map +1 -1
- package/dist/references/src/anchors/serialize.js +1 -0
- package/dist/references/src/anchors/serialize.js.map +1 -1
- package/dist/references/src/anchors/types.cjs.map +1 -1
- package/dist/references/src/anchors/types.d.ts +9 -0
- package/dist/references/src/anchors/types.js.map +1 -1
- package/dist/references/src/schemas.d.ts +2 -0
- package/dist/runtime/useAthenaStatewireRuntime.cjs +6 -8
- package/dist/runtime/useAthenaStatewireRuntime.cjs.map +1 -1
- package/dist/runtime/useAthenaStatewireRuntime.js +6 -8
- package/dist/runtime/useAthenaStatewireRuntime.js.map +1 -1
- package/dist/threads/statewire-thread-list.cjs +14 -2
- package/dist/threads/statewire-thread-list.cjs.map +1 -1
- package/dist/threads/statewire-thread-list.d.ts +11 -2
- package/dist/threads/statewire-thread-list.js +14 -2
- package/dist/threads/statewire-thread-list.js.map +1 -1
- package/dist/tools/drive-tool-uis.cjs +8 -4
- package/dist/tools/drive-tool-uis.cjs.map +1 -1
- package/dist/tools/drive-tool-uis.d.ts +5 -4
- package/dist/tools/drive-tool-uis.js +9 -5
- package/dist/tools/drive-tool-uis.js.map +1 -1
- package/dist/version.cjs +4 -0
- package/dist/version.cjs.map +1 -1
- package/dist/version.d.ts +9 -0
- package/dist/version.js +4 -0
- package/dist/version.js.map +1 -1
- package/dist/workbench/live-desktop-api.cjs +2 -1
- package/dist/workbench/live-desktop-api.cjs.map +1 -1
- package/dist/workbench/live-desktop-api.js +2 -1
- package/dist/workbench/live-desktop-api.js.map +1 -1
- package/dist/workbench/session-workspace-api.cjs +7 -2
- package/dist/workbench/session-workspace-api.cjs.map +1 -1
- package/dist/workbench/session-workspace-api.d.ts +10 -0
- package/dist/workbench/session-workspace-api.js +7 -2
- package/dist/workbench/session-workspace-api.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { ClientToolWriteConsentDecision } from '../agent-runtime/src/index';
|
|
2
|
+
/** One write tool of a batch waiting on the person. */
|
|
3
|
+
export interface ClientToolWriteConsentTool {
|
|
4
|
+
/** The tool's name, as the model calls it. */
|
|
5
|
+
name: string;
|
|
6
|
+
/** What it does, when the tool said (`requiresWriteConsent(…, { summary })`). */
|
|
7
|
+
summary?: string;
|
|
8
|
+
}
|
|
9
|
+
/** The question a parked batch of write calls puts to the person. */
|
|
10
|
+
export interface ClientToolWriteConsentRequest {
|
|
11
|
+
/** The parked request the answer resumes. */
|
|
12
|
+
requestId: string;
|
|
13
|
+
/** The conversation the answer covers. */
|
|
14
|
+
threadId: string;
|
|
15
|
+
/** The batch's write tools, each once. */
|
|
16
|
+
tools: readonly ClientToolWriteConsentTool[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A host's own consent UI (`AthenaProviderProps.confirmClientToolWrites`).
|
|
20
|
+
* Resolve `true` to allow the conversation's writes, `false` to decline this
|
|
21
|
+
* batch. `signal` aborts when the question no longer has a call to answer
|
|
22
|
+
* (the run was stopped): close the dialog then; the value is ignored.
|
|
23
|
+
*/
|
|
24
|
+
export type ClientToolWriteConfirmer = (request: ClientToolWriteConsentRequest & {
|
|
25
|
+
signal: AbortSignal;
|
|
26
|
+
}) => Promise<boolean>;
|
|
27
|
+
export interface ClientToolWriteConsentStore {
|
|
28
|
+
/** The question on screen, or `null`. */
|
|
29
|
+
getPending(): ClientToolWriteConsentRequest | null;
|
|
30
|
+
subscribe(listener: () => void): () => void;
|
|
31
|
+
/** Whether the person already allowed writes in this conversation. */
|
|
32
|
+
isGranted(threadId: string): boolean;
|
|
33
|
+
/** Put the question to the person; resolves with their answer, or `abandoned`. */
|
|
34
|
+
ask(request: ClientToolWriteConsentRequest): Promise<ClientToolWriteConsentDecision>;
|
|
35
|
+
/** The person's answer to the pending question. */
|
|
36
|
+
answer(args: {
|
|
37
|
+
requestId: string;
|
|
38
|
+
allow: boolean;
|
|
39
|
+
}): void;
|
|
40
|
+
/** The pending question lost its call (stop, another conversation): close it. */
|
|
41
|
+
abandon(requestId: string): void;
|
|
42
|
+
/** Record a consent the host's own UI collected. */
|
|
43
|
+
grant(threadId: string): void;
|
|
44
|
+
/** A mounted card registers itself, so a question nobody can see is reported. */
|
|
45
|
+
registerCard(): () => void;
|
|
46
|
+
/** The store's person is gone: close the pending question and forget every yes. */
|
|
47
|
+
dispose(): void;
|
|
48
|
+
}
|
|
49
|
+
export declare function createClientToolWriteConsentStore(): ClientToolWriteConsentStore;
|
|
50
|
+
/**
|
|
51
|
+
* The consent store for one signed-in person on one Athena. `scope` names
|
|
52
|
+
* both (the provider passes the session principal and the sync host): when it
|
|
53
|
+
* changes, the previous person's store is disposed (its question closed, its
|
|
54
|
+
* yeses forgotten) and the next person starts with none.
|
|
55
|
+
*/
|
|
56
|
+
export declare function useClientToolWriteConsentStore(scope: string): ClientToolWriteConsentStore;
|
|
57
|
+
/** Provided by `AthenaProvider`; `null` outside one. */
|
|
58
|
+
export declare const ClientToolWriteConsentContext: import("react").Context<ClientToolWriteConsentStore | null>;
|
|
59
|
+
export interface ClientToolWriteConsent {
|
|
60
|
+
/** The question waiting on the person, or `null`. */
|
|
61
|
+
request: ClientToolWriteConsentRequest | null;
|
|
62
|
+
/** Allow the conversation's writes and run the waiting calls. */
|
|
63
|
+
allow: () => void;
|
|
64
|
+
/** Decline the waiting calls; the agent is told, and a later call asks again. */
|
|
65
|
+
decline: () => void;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The write-consent question for the chat the nearest `AthenaProvider` shows,
|
|
69
|
+
* for a host that draws its own card. Calling it marks the question as shown.
|
|
70
|
+
*/
|
|
71
|
+
export declare function useClientToolWriteConsent(): ClientToolWriteConsent;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { useContext, useSyncExternalStore, useEffect, useCallback, createContext, useState } from "react";
|
|
2
|
+
import { athenaDiagnostics } from "../diagnostics/athena-diagnostics.js";
|
|
3
|
+
import { AthenaSdkError } from "../diagnostics/errors.js";
|
|
4
|
+
function createClientToolWriteConsentStore() {
|
|
5
|
+
let pending = null;
|
|
6
|
+
let cards = 0;
|
|
7
|
+
const granted = /* @__PURE__ */ new Set();
|
|
8
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
9
|
+
const notify = () => {
|
|
10
|
+
for (const listener of Array.from(listeners)) listener();
|
|
11
|
+
};
|
|
12
|
+
const settle = (decision) => {
|
|
13
|
+
const settled = pending;
|
|
14
|
+
pending = null;
|
|
15
|
+
settled == null ? void 0 : settled.resolve(decision);
|
|
16
|
+
notify();
|
|
17
|
+
};
|
|
18
|
+
return {
|
|
19
|
+
getPending: () => (pending == null ? void 0 : pending.request) ?? null,
|
|
20
|
+
subscribe(listener) {
|
|
21
|
+
listeners.add(listener);
|
|
22
|
+
return () => {
|
|
23
|
+
listeners.delete(listener);
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
isGranted: (threadId) => granted.has(threadId),
|
|
27
|
+
ask(request) {
|
|
28
|
+
if (granted.has(request.threadId)) return Promise.resolve("proceed");
|
|
29
|
+
if (pending) settle("abandoned");
|
|
30
|
+
if (cards === 0) {
|
|
31
|
+
athenaDiagnostics.emit("sdk.config.warning", {
|
|
32
|
+
level: "warn",
|
|
33
|
+
error: new AthenaSdkError({
|
|
34
|
+
code: "config_invalid",
|
|
35
|
+
message: "A frontend tool is waiting for the person to allow it, and nothing on screen asks them.",
|
|
36
|
+
hint: "Render <AthenaChat> (it draws the question), mount <StatewireClientToolConsentCard>, or pass confirmClientToolWrites on <AthenaProvider>. The run stays parked until the question is answered.",
|
|
37
|
+
context: { tools: request.tools.map((tool) => tool.name) }
|
|
38
|
+
}),
|
|
39
|
+
data: { tools: request.tools.map((tool) => tool.name) }
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return new Promise((resolve) => {
|
|
43
|
+
pending = { request, resolve };
|
|
44
|
+
notify();
|
|
45
|
+
});
|
|
46
|
+
},
|
|
47
|
+
answer({ requestId, allow }) {
|
|
48
|
+
if ((pending == null ? void 0 : pending.request.requestId) !== requestId) return;
|
|
49
|
+
if (allow) granted.add(pending.request.threadId);
|
|
50
|
+
settle(allow ? "proceed" : "declined");
|
|
51
|
+
},
|
|
52
|
+
abandon(requestId) {
|
|
53
|
+
if ((pending == null ? void 0 : pending.request.requestId) !== requestId) return;
|
|
54
|
+
settle("abandoned");
|
|
55
|
+
},
|
|
56
|
+
grant(threadId) {
|
|
57
|
+
granted.add(threadId);
|
|
58
|
+
},
|
|
59
|
+
registerCard() {
|
|
60
|
+
cards += 1;
|
|
61
|
+
return () => {
|
|
62
|
+
cards -= 1;
|
|
63
|
+
};
|
|
64
|
+
},
|
|
65
|
+
dispose() {
|
|
66
|
+
granted.clear();
|
|
67
|
+
if (pending) settle("abandoned");
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function useClientToolWriteConsentStore(scope) {
|
|
72
|
+
const [held, setHeld] = useState(() => ({ scope, store: createClientToolWriteConsentStore() }));
|
|
73
|
+
let current = held;
|
|
74
|
+
if (held.scope !== scope) {
|
|
75
|
+
current = { scope, store: createClientToolWriteConsentStore() };
|
|
76
|
+
setHeld(current);
|
|
77
|
+
}
|
|
78
|
+
const { store } = current;
|
|
79
|
+
useEffect(() => () => store.dispose(), [store]);
|
|
80
|
+
return store;
|
|
81
|
+
}
|
|
82
|
+
const ClientToolWriteConsentContext = createContext(
|
|
83
|
+
null
|
|
84
|
+
);
|
|
85
|
+
const NO_SUBSCRIPTION = () => () => {
|
|
86
|
+
};
|
|
87
|
+
const NO_PENDING = () => null;
|
|
88
|
+
function useClientToolWriteConsent() {
|
|
89
|
+
const store = useContext(ClientToolWriteConsentContext);
|
|
90
|
+
const request = useSyncExternalStore(
|
|
91
|
+
(store == null ? void 0 : store.subscribe) ?? NO_SUBSCRIPTION,
|
|
92
|
+
(store == null ? void 0 : store.getPending) ?? NO_PENDING,
|
|
93
|
+
NO_PENDING
|
|
94
|
+
);
|
|
95
|
+
useEffect(() => store == null ? void 0 : store.registerCard(), [store]);
|
|
96
|
+
const requestId = request == null ? void 0 : request.requestId;
|
|
97
|
+
const allow = useCallback(() => {
|
|
98
|
+
if (requestId !== void 0) store == null ? void 0 : store.answer({ requestId, allow: true });
|
|
99
|
+
}, [requestId, store]);
|
|
100
|
+
const decline = useCallback(() => {
|
|
101
|
+
if (requestId !== void 0) store == null ? void 0 : store.answer({ requestId, allow: false });
|
|
102
|
+
}, [requestId, store]);
|
|
103
|
+
return { request, allow, decline };
|
|
104
|
+
}
|
|
105
|
+
export {
|
|
106
|
+
ClientToolWriteConsentContext,
|
|
107
|
+
createClientToolWriteConsentStore,
|
|
108
|
+
useClientToolWriteConsent,
|
|
109
|
+
useClientToolWriteConsentStore
|
|
110
|
+
};
|
|
111
|
+
//# sourceMappingURL=client-tool-write-consent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-tool-write-consent.js","sources":["../../src/chat/client-tool-write-consent.ts"],"sourcesContent":["'use client';\n\nimport type { ClientToolWriteConsentDecision } from '@athenaintel/agent-runtime';\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useState,\n useSyncExternalStore,\n} from 'react';\nimport { athenaDiagnostics } from '../diagnostics/athena-diagnostics';\nimport { AthenaSdkError } from '../diagnostics/errors';\n\n/*\n * Write consent for the app's own frontend tools. A tool marked with\n * `requiresWriteConsent` parks its first call in a conversation until the\n * person answers; this store holds that question between the client-tool\n * bridge (which asks) and whatever draws it (`StatewireClientToolConsentCard`\n * in `AthenaChat`, or a host's own card on `useClientToolWriteConsent`).\n *\n * Allowing covers the rest of the conversation, as Athena's own apps do, for\n * the person who allowed it: a store belongs to one signed-in person on one\n * Athena (`useClientToolWriteConsentStore`), so someone else signing in to the\n * same mounted chat is asked for themselves. Nothing is remembered across a\n * reload: the next call asks again.\n */\n\n/** One write tool of a batch waiting on the person. */\nexport interface ClientToolWriteConsentTool {\n /** The tool's name, as the model calls it. */\n name: string;\n /** What it does, when the tool said (`requiresWriteConsent(…, { summary })`). */\n summary?: string;\n}\n\n/** The question a parked batch of write calls puts to the person. */\nexport interface ClientToolWriteConsentRequest {\n /** The parked request the answer resumes. */\n requestId: string;\n /** The conversation the answer covers. */\n threadId: string;\n /** The batch's write tools, each once. */\n tools: readonly ClientToolWriteConsentTool[];\n}\n\n/**\n * A host's own consent UI (`AthenaProviderProps.confirmClientToolWrites`).\n * Resolve `true` to allow the conversation's writes, `false` to decline this\n * batch. `signal` aborts when the question no longer has a call to answer\n * (the run was stopped): close the dialog then; the value is ignored.\n */\nexport type ClientToolWriteConfirmer = (\n request: ClientToolWriteConsentRequest & { signal: AbortSignal },\n) => Promise<boolean>;\n\nexport interface ClientToolWriteConsentStore {\n /** The question on screen, or `null`. */\n getPending(): ClientToolWriteConsentRequest | null;\n subscribe(listener: () => void): () => void;\n /** Whether the person already allowed writes in this conversation. */\n isGranted(threadId: string): boolean;\n /** Put the question to the person; resolves with their answer, or `abandoned`. */\n ask(request: ClientToolWriteConsentRequest): Promise<ClientToolWriteConsentDecision>;\n /** The person's answer to the pending question. */\n answer(args: { requestId: string; allow: boolean }): void;\n /** The pending question lost its call (stop, another conversation): close it. */\n abandon(requestId: string): void;\n /** Record a consent the host's own UI collected. */\n grant(threadId: string): void;\n /** A mounted card registers itself, so a question nobody can see is reported. */\n registerCard(): () => void;\n /** The store's person is gone: close the pending question and forget every yes. */\n dispose(): void;\n}\n\ninterface PendingConsent {\n request: ClientToolWriteConsentRequest;\n resolve: (decision: ClientToolWriteConsentDecision) => void;\n}\n\nexport function createClientToolWriteConsentStore(): ClientToolWriteConsentStore {\n let pending: PendingConsent | null = null;\n let cards = 0;\n const granted = new Set<string>();\n const listeners = new Set<() => void>();\n const notify = () => {\n for (const listener of Array.from(listeners)) listener();\n };\n const settle = (decision: ClientToolWriteConsentDecision) => {\n const settled = pending;\n pending = null;\n settled?.resolve(decision);\n notify();\n };\n\n return {\n getPending: () => pending?.request ?? null,\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n isGranted: (threadId) => granted.has(threadId),\n ask(request) {\n if (granted.has(request.threadId)) return Promise.resolve('proceed');\n // One question at a time: a newer batch replaces a question whose call\n // the bridge has already given up on.\n if (pending) settle('abandoned');\n if (cards === 0) {\n athenaDiagnostics.emit('sdk.config.warning', {\n level: 'warn',\n error: new AthenaSdkError({\n code: 'config_invalid',\n message:\n 'A frontend tool is waiting for the person to allow it, and nothing on screen asks them.',\n hint: 'Render <AthenaChat> (it draws the question), mount <StatewireClientToolConsentCard>, or pass confirmClientToolWrites on <AthenaProvider>. The run stays parked until the question is answered.',\n context: { tools: request.tools.map((tool) => tool.name) },\n }),\n data: { tools: request.tools.map((tool) => tool.name) },\n });\n }\n return new Promise<ClientToolWriteConsentDecision>((resolve) => {\n pending = { request, resolve };\n notify();\n });\n },\n answer({ requestId, allow }) {\n if (pending?.request.requestId !== requestId) return;\n if (allow) granted.add(pending.request.threadId);\n settle(allow ? 'proceed' : 'declined');\n },\n abandon(requestId) {\n if (pending?.request.requestId !== requestId) return;\n settle('abandoned');\n },\n grant(threadId) {\n granted.add(threadId);\n },\n registerCard() {\n cards += 1;\n return () => {\n cards -= 1;\n };\n },\n dispose() {\n granted.clear();\n if (pending) settle('abandoned');\n },\n };\n}\n\n/**\n * The consent store for one signed-in person on one Athena. `scope` names\n * both (the provider passes the session principal and the sync host): when it\n * changes, the previous person's store is disposed (its question closed, its\n * yeses forgotten) and the next person starts with none.\n */\nexport function useClientToolWriteConsentStore(scope: string): ClientToolWriteConsentStore {\n const [held, setHeld] = useState(() => ({ scope, store: createClientToolWriteConsentStore() }));\n let current = held;\n if (held.scope !== scope) {\n // Replaced during render, so no commit ever pairs the new person with the\n // previous person's store.\n current = { scope, store: createClientToolWriteConsentStore() };\n setHeld(current);\n }\n const { store } = current;\n useEffect(() => () => store.dispose(), [store]);\n return store;\n}\n\n/** Provided by `AthenaProvider`; `null` outside one. */\nexport const ClientToolWriteConsentContext = createContext<ClientToolWriteConsentStore | null>(\n null,\n);\n\nconst NO_SUBSCRIPTION = () => () => {};\nconst NO_PENDING = () => null;\n\nexport interface ClientToolWriteConsent {\n /** The question waiting on the person, or `null`. */\n request: ClientToolWriteConsentRequest | null;\n /** Allow the conversation's writes and run the waiting calls. */\n allow: () => void;\n /** Decline the waiting calls; the agent is told, and a later call asks again. */\n decline: () => void;\n}\n\n/**\n * The write-consent question for the chat the nearest `AthenaProvider` shows,\n * for a host that draws its own card. Calling it marks the question as shown.\n */\nexport function useClientToolWriteConsent(): ClientToolWriteConsent {\n const store = useContext(ClientToolWriteConsentContext);\n const request = useSyncExternalStore(\n store?.subscribe ?? NO_SUBSCRIPTION,\n store?.getPending ?? NO_PENDING,\n NO_PENDING,\n );\n useEffect(() => store?.registerCard(), [store]);\n const requestId = request?.requestId;\n const allow = useCallback(() => {\n if (requestId !== undefined) store?.answer({ requestId, allow: true });\n }, [requestId, store]);\n const decline = useCallback(() => {\n if (requestId !== undefined) store?.answer({ requestId, allow: false });\n }, [requestId, store]);\n return { request, allow, decline };\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -3,6 +3,28 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
3
3
|
const athenaDiagnostics = require("../diagnostics/athena-diagnostics.cjs");
|
|
4
4
|
const assistantStream = require("assistant-stream");
|
|
5
5
|
const clientTools = require("../agent-runtime/src/client-tools.cjs");
|
|
6
|
+
const CLIENT_TOOL_WRITE_CONSENT = Symbol.for("athena.clientTool.writeConsent");
|
|
7
|
+
function requiresWriteConsent(tool, options = {}) {
|
|
8
|
+
var _a;
|
|
9
|
+
const summary = (_a = options.summary) == null ? void 0 : _a.trim();
|
|
10
|
+
return { ...tool, [CLIENT_TOOL_WRITE_CONSENT]: summary ? { summary } : {} };
|
|
11
|
+
}
|
|
12
|
+
function clientToolWriteConsent(entry) {
|
|
13
|
+
if (!entry || typeof entry !== "object") return null;
|
|
14
|
+
const mark = entry[CLIENT_TOOL_WRITE_CONSENT];
|
|
15
|
+
if (!mark || typeof mark !== "object") return null;
|
|
16
|
+
const summary = mark.summary;
|
|
17
|
+
return typeof summary === "string" && summary ? { summary } : {};
|
|
18
|
+
}
|
|
19
|
+
function clientToolWriteConsentSummaries(toolkit) {
|
|
20
|
+
var _a;
|
|
21
|
+
const summaries = {};
|
|
22
|
+
for (const [name, entry] of Object.entries(toolkit ?? {})) {
|
|
23
|
+
const summary = (_a = clientToolWriteConsent(entry)) == null ? void 0 : _a.summary;
|
|
24
|
+
if (summary) summaries[name] = summary;
|
|
25
|
+
}
|
|
26
|
+
return summaries;
|
|
27
|
+
}
|
|
6
28
|
function hasCallableExecute(entry) {
|
|
7
29
|
return !!entry && typeof entry === "object" && typeof entry.execute === "function";
|
|
8
30
|
}
|
|
@@ -32,15 +54,15 @@ function collectStatewireClientTools(toolkit) {
|
|
|
32
54
|
name,
|
|
33
55
|
description: entry.description ?? "",
|
|
34
56
|
parameters,
|
|
35
|
-
requiresWrite:
|
|
57
|
+
requiresWrite: clientToolWriteConsent(entry) !== null
|
|
36
58
|
},
|
|
37
59
|
handler: async (args, context) => {
|
|
38
|
-
const controller = new AbortController();
|
|
39
60
|
const output = await execute(args, {
|
|
40
|
-
// The bridge always supplies the parked call's id
|
|
41
|
-
// the
|
|
61
|
+
// The bridge always supplies the parked call's id and its signal,
|
|
62
|
+
// which aborts when the call's request goes away (the run was
|
|
63
|
+
// stopped); a host invoking the handler directly has neither.
|
|
42
64
|
toolCallId: (context == null ? void 0 : context.toolCallId) ?? "",
|
|
43
|
-
abortSignal:
|
|
65
|
+
abortSignal: (context == null ? void 0 : context.signal) ?? new AbortController().signal,
|
|
44
66
|
human: () => Promise.reject(
|
|
45
67
|
new Error("Human input is not supported for statewire client tools.")
|
|
46
68
|
)
|
|
@@ -73,7 +95,10 @@ function resolveClientToolSurface(appId) {
|
|
|
73
95
|
}
|
|
74
96
|
exports.CLIENT_TOOL_SURFACE_APP_IDS = CLIENT_TOOL_SURFACE_APP_IDS;
|
|
75
97
|
exports.backendDeclaredFrontendToolIds = backendDeclaredFrontendToolIds;
|
|
98
|
+
exports.clientToolWriteConsent = clientToolWriteConsent;
|
|
99
|
+
exports.clientToolWriteConsentSummaries = clientToolWriteConsentSummaries;
|
|
76
100
|
exports.collectStatewireClientTools = collectStatewireClientTools;
|
|
101
|
+
exports.requiresWriteConsent = requiresWriteConsent;
|
|
77
102
|
exports.resolveClientToolSurface = resolveClientToolSurface;
|
|
78
103
|
exports.statewireClientToolWireEntries = statewireClientToolWireEntries;
|
|
79
104
|
//# sourceMappingURL=statewire-client-tools.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"statewire-client-tools.cjs","sources":["../../src/chat/statewire-client-tools.ts"],"sourcesContent":["import { athenaDiagnostics } from '../diagnostics/athena-diagnostics';\nimport type { Toolkit } from '@assistant-ui/react';\nimport {\n type ClientToolHandlerContext,\n clientToolRunConfigEntries,\n type RegisteredClientTool,\n} from '@athenaintel/agent-runtime';\nimport { ToolResponse, toJSONSchema } from 'assistant-stream';\n\n/**\n * @deprecated The SDK's client tools ARE the shared `RegisteredClientTool`\n * shape from `@athenaintel/agent-runtime` (`definition` + `handler`); import\n * that type instead. Retained for one release so existing imports type-check.\n */\nexport type StatewireClientTool = RegisteredClientTool;\n\nfunction hasCallableExecute(\n entry: unknown,\n): entry is { execute: (args: Record<string, unknown>, context: unknown) => unknown } {\n return (\n !!entry &&\n typeof entry === 'object' &&\n typeof (entry as { execute?: unknown }).execute === 'function'\n );\n}\n\n/**\n * Collect the client-executable tools from a frontend toolkit, in the shared\n * `RegisteredClientTool` shape every statewire surface declares and executes.\n *\n * The deep-agent runtime drops frontend (`ui_*`) tool definitions, so tools\n * with a local `execute` must instead be declared through\n * `runConfig.custom.client_tools` and answered by the statewire client-tool\n * bridge. Schema conversion reuses `toJSONSchema` — the same conversion\n * primitive as the legacy assistant-transport — and a tool whose schema cannot\n * be converted is skipped with a warning rather than failing the whole set.\n * SDK tools never gate on write consent (`requiresWrite: false`): a toolkit\n * `execute` is the host app's own code.\n */\nexport function collectStatewireClientTools(toolkit: Toolkit): RegisteredClientTool[] {\n const executable = Object.entries(toolkit).filter(([, entry]) => hasCallableExecute(entry));\n if (executable.length === 0) return [];\n\n return executable.flatMap(([name, entry]) => {\n if (entry.parameters === undefined) return [];\n\n let parameters: Record<string, unknown>;\n try {\n parameters = { ...toJSONSchema(entry.parameters) };\n } catch (error) {\n athenaDiagnostics.emit('tools.schema.warning', {\n level: 'warn',\n data: {\n tool: name,\n message: 'Frontend tool schema could not be converted for statewire; the tool is not registered',\n error: error instanceof Error ? error.message : String(error),\n },\n });\n return [];\n }\n\n const execute = (entry as {\n execute: (args: Record<string, unknown>, context: unknown) => unknown;\n }).execute;\n return [\n {\n definition: {\n name,\n description: entry.description ?? '',\n parameters,\n requiresWrite: false,\n },\n handler: async (args, context?: ClientToolHandlerContext) => {\n const controller = new AbortController();\n const output = await execute(args, {\n // The bridge always supplies the parked call's id; a host invoking\n // the handler directly has no call to correlate.\n toolCallId: context?.toolCallId ?? '',\n abortSignal: controller.signal,\n human: () =>\n Promise.reject(\n new Error('Human input is not supported for statewire client tools.'),\n ),\n });\n return output instanceof ToolResponse ? output.result : output;\n },\n },\n ];\n });\n}\n\n/** Wire entries for `runConfig.custom.client_tools` — the shared contract from\n * `@athenaintel/agent-runtime`, so the SDK cannot drift from the other surfaces. */\nexport function statewireClientToolWireEntries(\n tools: readonly RegisteredClientTool[],\n): Record<string, unknown>[] {\n return clientToolRunConfigEntries(tools);\n}\n\n/**\n * The frontend-toolkit entries that must ride `enabled_tools`: declarations\n * WITHOUT a local `execute`, which name registry tools the backend already\n * knows how to bind (Olympus's frontend-executed tool ids).\n *\n * Executable entries are deliberately left out. They are declared through\n * `client_tools`, and the run binds them by name; putting the same name into\n * `enabled_tools` would also switch on any backend toolkit that happens to\n * own a tool of that name, and the backend wins the resulting collision —\n * the client tool is dropped and the model calls the backend one instead.\n */\nexport function backendDeclaredFrontendToolIds(toolkit: Toolkit): string[] {\n return Object.entries(toolkit)\n .filter(([, entry]) => !hasCallableExecute(entry))\n .map(([name]) => name);\n}\n\n/**\n * The `app_id` values the deep-agent runtime accepts as a client-tool surface.\n * Mirrors `agora/agora/graphs/athena_deep_agent/client_tools.py`: the fixed\n * first-party ids plus the `computer_asset_<uuid>` shape Athena's provisioning\n * injects into a computer-asset app. Any other `app_id` (or none) gets no\n * surface, and the runtime drops every `client_tools` declaration for the run.\n */\nexport const CLIENT_TOOL_SURFACE_APP_IDS = {\n olympus: 'olympus',\n officeAddin: 'athena-office-addin',\n mobile: 'athena-mobile',\n} as const;\n\nconst SDK_APP_ID_PATTERN = /^computer_asset_[0-9a-f-]{8,64}$/;\n\nexport type ClientToolSurface = 'web' | 'office' | 'mobile' | 'sdk';\n\n/** The surface an `app_id` selects, or null when the runtime would refuse it. */\nexport function resolveClientToolSurface(appId: string | undefined | null): ClientToolSurface | null {\n if (typeof appId !== 'string' || appId.length === 0) return null;\n if (appId === CLIENT_TOOL_SURFACE_APP_IDS.olympus) return 'web';\n if (appId === CLIENT_TOOL_SURFACE_APP_IDS.officeAddin) return 'office';\n if (appId === CLIENT_TOOL_SURFACE_APP_IDS.mobile) return 'mobile';\n if (SDK_APP_ID_PATTERN.test(appId)) return 'sdk';\n return null;\n}\n"],"names":["toJSONSchema","athenaDiagnostics","ToolResponse","clientToolRunConfigEntries"],"mappings":";;;;;AAgBA,SAAS,mBACP,OACoF;AACpF,SACE,CAAC,CAAC,SACF,OAAO,UAAU,YACjB,OAAQ,MAAgC,YAAY;AAExD;AAeO,SAAS,4BAA4B,SAA0C;AACpF,QAAM,aAAa,OAAO,QAAQ,OAAO,EAAE,OAAO,CAAC,GAAG,KAAK,MAAM,mBAAmB,KAAK,CAAC;AAC1F,MAAI,WAAW,WAAW,EAAG,QAAO,CAAA;AAEpC,SAAO,WAAW,QAAQ,CAAC,CAAC,MAAM,KAAK,MAAM;AAC3C,QAAI,MAAM,eAAe,OAAW,QAAO,CAAA;AAE3C,QAAI;AACJ,QAAI;AACF,mBAAa,EAAE,GAAGA,gBAAAA,aAAa,MAAM,UAAU,EAAA;AAAA,IACjD,SAAS,OAAO;AACdC,wBAAAA,kBAAkB,KAAK,wBAAwB;AAAA,QAC7C,OAAO;AAAA,QACP,MAAM;AAAA,UACJ,MAAM;AAAA,UACN,SAAS;AAAA,UACT,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,QAAA;AAAA,MAC9D,CACD;AACD,aAAO,CAAA;AAAA,IACT;AAEA,UAAM,UAAW,MAEd;AACH,WAAO;AAAA,MACL;AAAA,QACE,YAAY;AAAA,UACV;AAAA,UACA,aAAa,MAAM,eAAe;AAAA,UAClC;AAAA,UACA,eAAe;AAAA,QAAA;AAAA,QAEjB,SAAS,OAAO,MAAM,YAAuC;AAC3D,gBAAM,aAAa,IAAI,gBAAA;AACvB,gBAAM,SAAS,MAAM,QAAQ,MAAM;AAAA;AAAA;AAAA,YAGjC,aAAY,mCAAS,eAAc;AAAA,YACnC,aAAa,WAAW;AAAA,YACxB,OAAO,MACL,QAAQ;AAAA,cACN,IAAI,MAAM,0DAA0D;AAAA,YAAA;AAAA,UACtE,CACH;AACD,iBAAO,kBAAkBC,gBAAAA,eAAe,OAAO,SAAS;AAAA,QAC1D;AAAA,MAAA;AAAA,IACF;AAAA,EAEJ,CAAC;AACH;AAIO,SAAS,+BACd,OAC2B;AAC3B,SAAOC,YAAAA,2BAA2B,KAAK;AACzC;AAaO,SAAS,+BAA+B,SAA4B;AACzE,SAAO,OAAO,QAAQ,OAAO,EAC1B,OAAO,CAAC,CAAA,EAAG,KAAK,MAAM,CAAC,mBAAmB,KAAK,CAAC,EAChD,IAAI,CAAC,CAAC,IAAI,MAAM,IAAI;AACzB;AASO,MAAM,8BAA8B;AAAA,EACzC,SAAS;AAAA,EACT,aAAa;AAAA,EACb,QAAQ;AACV;AAEA,MAAM,qBAAqB;AAKpB,SAAS,yBAAyB,OAA4D;AACnG,MAAI,OAAO,UAAU,YAAY,MAAM,WAAW,EAAG,QAAO;AAC5D,MAAI,UAAU,4BAA4B,QAAS,QAAO;AAC1D,MAAI,UAAU,4BAA4B,YAAa,QAAO;AAC9D,MAAI,UAAU,4BAA4B,OAAQ,QAAO;AACzD,MAAI,mBAAmB,KAAK,KAAK,EAAG,QAAO;AAC3C,SAAO;AACT;;;;;;"}
|
|
1
|
+
{"version":3,"file":"statewire-client-tools.cjs","sources":["../../src/chat/statewire-client-tools.ts"],"sourcesContent":["import { athenaDiagnostics } from '../diagnostics/athena-diagnostics';\nimport type { Toolkit } from '@assistant-ui/react';\nimport {\n type ClientToolHandlerContext,\n clientToolRunConfigEntries,\n type RegisteredClientTool,\n} from '@athenaintel/agent-runtime';\nimport { ToolResponse, toJSONSchema } from 'assistant-stream';\n\n/**\n * @deprecated The SDK's client tools ARE the shared `RegisteredClientTool`\n * shape from `@athenaintel/agent-runtime` (`definition` + `handler`); import\n * that type instead. Retained for one release so existing imports type-check.\n */\nexport type StatewireClientTool = RegisteredClientTool;\n\n/**\n * Marks a frontend tool as one that changes something for the person, so its\n * calls wait for their consent. Set by {@link requiresWriteConsent}. A\n * registered symbol (not a module-local one), so the mark is read by whichever\n * copy of the SDK collects the toolkit.\n */\nconst CLIENT_TOOL_WRITE_CONSENT = Symbol.for('athena.clientTool.writeConsent');\n\nexport interface ClientToolWriteConsentOptions {\n /**\n * What the tool does, as the rest of the sentence \"… wants to ___\" on the\n * consent card (e.g. `\"click and type on this page\"`). The card names the\n * tool when this is absent.\n */\n summary?: string;\n}\n\n/**\n * Mark a `frontendTools` entry as a write: the person is asked before its\n * first call in a conversation runs, the answer covers the rest of that\n * conversation, and a declined call answers the model with an error instead\n * of running. Unmarked tools run without asking, as before.\n *\n * ```ts\n * const frontendTools = {\n * save_note: requiresWriteConsent(\n * { description: '…', parameters, execute: saveNote },\n * { summary: 'save a note on this record' },\n * ),\n * };\n * ```\n *\n * The mark also travels to Athena (`requires_write`), which then never\n * answers the call on the person's behalf while their tab is away.\n */\nexport function requiresWriteConsent<T extends object>(\n tool: T,\n options: ClientToolWriteConsentOptions = {},\n): T {\n const summary = options.summary?.trim();\n return { ...tool, [CLIENT_TOOL_WRITE_CONSENT]: summary ? { summary } : {} };\n}\n\n/** The consent mark on a toolkit entry, or `null` for a tool that never asks. */\nexport function clientToolWriteConsent(entry: unknown): ClientToolWriteConsentOptions | null {\n if (!entry || typeof entry !== 'object') return null;\n const mark = (entry as Record<symbol, unknown>)[CLIENT_TOOL_WRITE_CONSENT];\n if (!mark || typeof mark !== 'object') return null;\n const summary = (mark as { summary?: unknown }).summary;\n return typeof summary === 'string' && summary ? { summary } : {};\n}\n\n/** Each consent-marked tool's own words for what it does, by tool name. */\nexport function clientToolWriteConsentSummaries(\n toolkit: Toolkit | undefined,\n): Readonly<Record<string, string>> {\n const summaries: Record<string, string> = {};\n for (const [name, entry] of Object.entries(toolkit ?? {})) {\n const summary = clientToolWriteConsent(entry)?.summary;\n if (summary) summaries[name] = summary;\n }\n return summaries;\n}\n\nfunction hasCallableExecute(\n entry: unknown,\n): entry is { execute: (args: Record<string, unknown>, context: unknown) => unknown } {\n return (\n !!entry &&\n typeof entry === 'object' &&\n typeof (entry as { execute?: unknown }).execute === 'function'\n );\n}\n\n/**\n * Collect the client-executable tools from a frontend toolkit, in the shared\n * `RegisteredClientTool` shape every statewire surface declares and executes.\n *\n * The deep-agent runtime drops frontend (`ui_*`) tool definitions, so tools\n * with a local `execute` must instead be declared through\n * `runConfig.custom.client_tools` and answered by the statewire client-tool\n * bridge. Schema conversion reuses `toJSONSchema` — the same conversion\n * primitive as the legacy assistant-transport — and a tool whose schema cannot\n * be converted is skipped with a warning rather than failing the whole set.\n * A tool gates on write consent only when its entry is marked with\n * `requiresWriteConsent`: an unmarked toolkit `execute` is the host app's own\n * code and runs without asking.\n */\nexport function collectStatewireClientTools(toolkit: Toolkit): RegisteredClientTool[] {\n const executable = Object.entries(toolkit).filter(([, entry]) => hasCallableExecute(entry));\n if (executable.length === 0) return [];\n\n return executable.flatMap(([name, entry]) => {\n if (entry.parameters === undefined) return [];\n\n let parameters: Record<string, unknown>;\n try {\n parameters = { ...toJSONSchema(entry.parameters) };\n } catch (error) {\n athenaDiagnostics.emit('tools.schema.warning', {\n level: 'warn',\n data: {\n tool: name,\n message: 'Frontend tool schema could not be converted for statewire; the tool is not registered',\n error: error instanceof Error ? error.message : String(error),\n },\n });\n return [];\n }\n\n const execute = (entry as {\n execute: (args: Record<string, unknown>, context: unknown) => unknown;\n }).execute;\n return [\n {\n definition: {\n name,\n description: entry.description ?? '',\n parameters,\n requiresWrite: clientToolWriteConsent(entry) !== null,\n },\n handler: async (args, context?: ClientToolHandlerContext) => {\n const output = await execute(args, {\n // The bridge always supplies the parked call's id and its signal,\n // which aborts when the call's request goes away (the run was\n // stopped); a host invoking the handler directly has neither.\n toolCallId: context?.toolCallId ?? '',\n abortSignal: context?.signal ?? new AbortController().signal,\n human: () =>\n Promise.reject(\n new Error('Human input is not supported for statewire client tools.'),\n ),\n });\n return output instanceof ToolResponse ? output.result : output;\n },\n },\n ];\n });\n}\n\n/** Wire entries for `runConfig.custom.client_tools` — the shared contract from\n * `@athenaintel/agent-runtime`, so the SDK cannot drift from the other surfaces. */\nexport function statewireClientToolWireEntries(\n tools: readonly RegisteredClientTool[],\n): Record<string, unknown>[] {\n return clientToolRunConfigEntries(tools);\n}\n\n/**\n * The frontend-toolkit entries that must ride `enabled_tools`: declarations\n * WITHOUT a local `execute`, which name registry tools the backend already\n * knows how to bind (Olympus's frontend-executed tool ids).\n *\n * Executable entries are deliberately left out. They are declared through\n * `client_tools`, and the run binds them by name; putting the same name into\n * `enabled_tools` would also switch on any backend toolkit that happens to\n * own a tool of that name, and the backend wins the resulting collision —\n * the client tool is dropped and the model calls the backend one instead.\n */\nexport function backendDeclaredFrontendToolIds(toolkit: Toolkit): string[] {\n return Object.entries(toolkit)\n .filter(([, entry]) => !hasCallableExecute(entry))\n .map(([name]) => name);\n}\n\n/**\n * The `app_id` values the deep-agent runtime accepts as a client-tool surface.\n * Mirrors `agora/agora/graphs/athena_deep_agent/client_tools.py`: the fixed\n * first-party ids plus the `computer_asset_<uuid>` shape Athena's provisioning\n * injects into a computer-asset app. Any other `app_id` (or none) gets no\n * surface, and the runtime drops every `client_tools` declaration for the run.\n */\nexport const CLIENT_TOOL_SURFACE_APP_IDS = {\n olympus: 'olympus',\n officeAddin: 'athena-office-addin',\n mobile: 'athena-mobile',\n} as const;\n\nconst SDK_APP_ID_PATTERN = /^computer_asset_[0-9a-f-]{8,64}$/;\n\nexport type ClientToolSurface = 'web' | 'office' | 'mobile' | 'sdk';\n\n/** The surface an `app_id` selects, or null when the runtime would refuse it. */\nexport function resolveClientToolSurface(appId: string | undefined | null): ClientToolSurface | null {\n if (typeof appId !== 'string' || appId.length === 0) return null;\n if (appId === CLIENT_TOOL_SURFACE_APP_IDS.olympus) return 'web';\n if (appId === CLIENT_TOOL_SURFACE_APP_IDS.officeAddin) return 'office';\n if (appId === CLIENT_TOOL_SURFACE_APP_IDS.mobile) return 'mobile';\n if (SDK_APP_ID_PATTERN.test(appId)) return 'sdk';\n return null;\n}\n"],"names":["toJSONSchema","athenaDiagnostics","ToolResponse","clientToolRunConfigEntries"],"mappings":"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"}
|
|
@@ -6,6 +6,37 @@ import { type RegisteredClientTool } from '../agent-runtime/src/index';
|
|
|
6
6
|
* that type instead. Retained for one release so existing imports type-check.
|
|
7
7
|
*/
|
|
8
8
|
export type StatewireClientTool = RegisteredClientTool;
|
|
9
|
+
export interface ClientToolWriteConsentOptions {
|
|
10
|
+
/**
|
|
11
|
+
* What the tool does, as the rest of the sentence "… wants to ___" on the
|
|
12
|
+
* consent card (e.g. `"click and type on this page"`). The card names the
|
|
13
|
+
* tool when this is absent.
|
|
14
|
+
*/
|
|
15
|
+
summary?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Mark a `frontendTools` entry as a write: the person is asked before its
|
|
19
|
+
* first call in a conversation runs, the answer covers the rest of that
|
|
20
|
+
* conversation, and a declined call answers the model with an error instead
|
|
21
|
+
* of running. Unmarked tools run without asking, as before.
|
|
22
|
+
*
|
|
23
|
+
* ```ts
|
|
24
|
+
* const frontendTools = {
|
|
25
|
+
* save_note: requiresWriteConsent(
|
|
26
|
+
* { description: '…', parameters, execute: saveNote },
|
|
27
|
+
* { summary: 'save a note on this record' },
|
|
28
|
+
* ),
|
|
29
|
+
* };
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* The mark also travels to Athena (`requires_write`), which then never
|
|
33
|
+
* answers the call on the person's behalf while their tab is away.
|
|
34
|
+
*/
|
|
35
|
+
export declare function requiresWriteConsent<T extends object>(tool: T, options?: ClientToolWriteConsentOptions): T;
|
|
36
|
+
/** The consent mark on a toolkit entry, or `null` for a tool that never asks. */
|
|
37
|
+
export declare function clientToolWriteConsent(entry: unknown): ClientToolWriteConsentOptions | null;
|
|
38
|
+
/** Each consent-marked tool's own words for what it does, by tool name. */
|
|
39
|
+
export declare function clientToolWriteConsentSummaries(toolkit: Toolkit | undefined): Readonly<Record<string, string>>;
|
|
9
40
|
/**
|
|
10
41
|
* Collect the client-executable tools from a frontend toolkit, in the shared
|
|
11
42
|
* `RegisteredClientTool` shape every statewire surface declares and executes.
|
|
@@ -16,8 +47,9 @@ export type StatewireClientTool = RegisteredClientTool;
|
|
|
16
47
|
* bridge. Schema conversion reuses `toJSONSchema` — the same conversion
|
|
17
48
|
* primitive as the legacy assistant-transport — and a tool whose schema cannot
|
|
18
49
|
* be converted is skipped with a warning rather than failing the whole set.
|
|
19
|
-
*
|
|
20
|
-
* `execute` is the host app's own
|
|
50
|
+
* A tool gates on write consent only when its entry is marked with
|
|
51
|
+
* `requiresWriteConsent`: an unmarked toolkit `execute` is the host app's own
|
|
52
|
+
* code and runs without asking.
|
|
21
53
|
*/
|
|
22
54
|
export declare function collectStatewireClientTools(toolkit: Toolkit): RegisteredClientTool[];
|
|
23
55
|
/** Wire entries for `runConfig.custom.client_tools` — the shared contract from
|
|
@@ -1,6 +1,28 @@
|
|
|
1
1
|
import { athenaDiagnostics } from "../diagnostics/athena-diagnostics.js";
|
|
2
2
|
import { toJSONSchema, ToolResponse } from "assistant-stream";
|
|
3
3
|
import { clientToolRunConfigEntries } from "../agent-runtime/src/client-tools.js";
|
|
4
|
+
const CLIENT_TOOL_WRITE_CONSENT = Symbol.for("athena.clientTool.writeConsent");
|
|
5
|
+
function requiresWriteConsent(tool, options = {}) {
|
|
6
|
+
var _a;
|
|
7
|
+
const summary = (_a = options.summary) == null ? void 0 : _a.trim();
|
|
8
|
+
return { ...tool, [CLIENT_TOOL_WRITE_CONSENT]: summary ? { summary } : {} };
|
|
9
|
+
}
|
|
10
|
+
function clientToolWriteConsent(entry) {
|
|
11
|
+
if (!entry || typeof entry !== "object") return null;
|
|
12
|
+
const mark = entry[CLIENT_TOOL_WRITE_CONSENT];
|
|
13
|
+
if (!mark || typeof mark !== "object") return null;
|
|
14
|
+
const summary = mark.summary;
|
|
15
|
+
return typeof summary === "string" && summary ? { summary } : {};
|
|
16
|
+
}
|
|
17
|
+
function clientToolWriteConsentSummaries(toolkit) {
|
|
18
|
+
var _a;
|
|
19
|
+
const summaries = {};
|
|
20
|
+
for (const [name, entry] of Object.entries(toolkit ?? {})) {
|
|
21
|
+
const summary = (_a = clientToolWriteConsent(entry)) == null ? void 0 : _a.summary;
|
|
22
|
+
if (summary) summaries[name] = summary;
|
|
23
|
+
}
|
|
24
|
+
return summaries;
|
|
25
|
+
}
|
|
4
26
|
function hasCallableExecute(entry) {
|
|
5
27
|
return !!entry && typeof entry === "object" && typeof entry.execute === "function";
|
|
6
28
|
}
|
|
@@ -30,15 +52,15 @@ function collectStatewireClientTools(toolkit) {
|
|
|
30
52
|
name,
|
|
31
53
|
description: entry.description ?? "",
|
|
32
54
|
parameters,
|
|
33
|
-
requiresWrite:
|
|
55
|
+
requiresWrite: clientToolWriteConsent(entry) !== null
|
|
34
56
|
},
|
|
35
57
|
handler: async (args, context) => {
|
|
36
|
-
const controller = new AbortController();
|
|
37
58
|
const output = await execute(args, {
|
|
38
|
-
// The bridge always supplies the parked call's id
|
|
39
|
-
// the
|
|
59
|
+
// The bridge always supplies the parked call's id and its signal,
|
|
60
|
+
// which aborts when the call's request goes away (the run was
|
|
61
|
+
// stopped); a host invoking the handler directly has neither.
|
|
40
62
|
toolCallId: (context == null ? void 0 : context.toolCallId) ?? "",
|
|
41
|
-
abortSignal:
|
|
63
|
+
abortSignal: (context == null ? void 0 : context.signal) ?? new AbortController().signal,
|
|
42
64
|
human: () => Promise.reject(
|
|
43
65
|
new Error("Human input is not supported for statewire client tools.")
|
|
44
66
|
)
|
|
@@ -72,7 +94,10 @@ function resolveClientToolSurface(appId) {
|
|
|
72
94
|
export {
|
|
73
95
|
CLIENT_TOOL_SURFACE_APP_IDS,
|
|
74
96
|
backendDeclaredFrontendToolIds,
|
|
97
|
+
clientToolWriteConsent,
|
|
98
|
+
clientToolWriteConsentSummaries,
|
|
75
99
|
collectStatewireClientTools,
|
|
100
|
+
requiresWriteConsent,
|
|
76
101
|
resolveClientToolSurface,
|
|
77
102
|
statewireClientToolWireEntries
|
|
78
103
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"statewire-client-tools.js","sources":["../../src/chat/statewire-client-tools.ts"],"sourcesContent":["import { athenaDiagnostics } from '../diagnostics/athena-diagnostics';\nimport type { Toolkit } from '@assistant-ui/react';\nimport {\n type ClientToolHandlerContext,\n clientToolRunConfigEntries,\n type RegisteredClientTool,\n} from '@athenaintel/agent-runtime';\nimport { ToolResponse, toJSONSchema } from 'assistant-stream';\n\n/**\n * @deprecated The SDK's client tools ARE the shared `RegisteredClientTool`\n * shape from `@athenaintel/agent-runtime` (`definition` + `handler`); import\n * that type instead. Retained for one release so existing imports type-check.\n */\nexport type StatewireClientTool = RegisteredClientTool;\n\nfunction hasCallableExecute(\n entry: unknown,\n): entry is { execute: (args: Record<string, unknown>, context: unknown) => unknown } {\n return (\n !!entry &&\n typeof entry === 'object' &&\n typeof (entry as { execute?: unknown }).execute === 'function'\n );\n}\n\n/**\n * Collect the client-executable tools from a frontend toolkit, in the shared\n * `RegisteredClientTool` shape every statewire surface declares and executes.\n *\n * The deep-agent runtime drops frontend (`ui_*`) tool definitions, so tools\n * with a local `execute` must instead be declared through\n * `runConfig.custom.client_tools` and answered by the statewire client-tool\n * bridge. Schema conversion reuses `toJSONSchema` — the same conversion\n * primitive as the legacy assistant-transport — and a tool whose schema cannot\n * be converted is skipped with a warning rather than failing the whole set.\n * SDK tools never gate on write consent (`requiresWrite: false`): a toolkit\n * `execute` is the host app's own code.\n */\nexport function collectStatewireClientTools(toolkit: Toolkit): RegisteredClientTool[] {\n const executable = Object.entries(toolkit).filter(([, entry]) => hasCallableExecute(entry));\n if (executable.length === 0) return [];\n\n return executable.flatMap(([name, entry]) => {\n if (entry.parameters === undefined) return [];\n\n let parameters: Record<string, unknown>;\n try {\n parameters = { ...toJSONSchema(entry.parameters) };\n } catch (error) {\n athenaDiagnostics.emit('tools.schema.warning', {\n level: 'warn',\n data: {\n tool: name,\n message: 'Frontend tool schema could not be converted for statewire; the tool is not registered',\n error: error instanceof Error ? error.message : String(error),\n },\n });\n return [];\n }\n\n const execute = (entry as {\n execute: (args: Record<string, unknown>, context: unknown) => unknown;\n }).execute;\n return [\n {\n definition: {\n name,\n description: entry.description ?? '',\n parameters,\n requiresWrite: false,\n },\n handler: async (args, context?: ClientToolHandlerContext) => {\n const controller = new AbortController();\n const output = await execute(args, {\n // The bridge always supplies the parked call's id; a host invoking\n // the handler directly has no call to correlate.\n toolCallId: context?.toolCallId ?? '',\n abortSignal: controller.signal,\n human: () =>\n Promise.reject(\n new Error('Human input is not supported for statewire client tools.'),\n ),\n });\n return output instanceof ToolResponse ? output.result : output;\n },\n },\n ];\n });\n}\n\n/** Wire entries for `runConfig.custom.client_tools` — the shared contract from\n * `@athenaintel/agent-runtime`, so the SDK cannot drift from the other surfaces. */\nexport function statewireClientToolWireEntries(\n tools: readonly RegisteredClientTool[],\n): Record<string, unknown>[] {\n return clientToolRunConfigEntries(tools);\n}\n\n/**\n * The frontend-toolkit entries that must ride `enabled_tools`: declarations\n * WITHOUT a local `execute`, which name registry tools the backend already\n * knows how to bind (Olympus's frontend-executed tool ids).\n *\n * Executable entries are deliberately left out. They are declared through\n * `client_tools`, and the run binds them by name; putting the same name into\n * `enabled_tools` would also switch on any backend toolkit that happens to\n * own a tool of that name, and the backend wins the resulting collision —\n * the client tool is dropped and the model calls the backend one instead.\n */\nexport function backendDeclaredFrontendToolIds(toolkit: Toolkit): string[] {\n return Object.entries(toolkit)\n .filter(([, entry]) => !hasCallableExecute(entry))\n .map(([name]) => name);\n}\n\n/**\n * The `app_id` values the deep-agent runtime accepts as a client-tool surface.\n * Mirrors `agora/agora/graphs/athena_deep_agent/client_tools.py`: the fixed\n * first-party ids plus the `computer_asset_<uuid>` shape Athena's provisioning\n * injects into a computer-asset app. Any other `app_id` (or none) gets no\n * surface, and the runtime drops every `client_tools` declaration for the run.\n */\nexport const CLIENT_TOOL_SURFACE_APP_IDS = {\n olympus: 'olympus',\n officeAddin: 'athena-office-addin',\n mobile: 'athena-mobile',\n} as const;\n\nconst SDK_APP_ID_PATTERN = /^computer_asset_[0-9a-f-]{8,64}$/;\n\nexport type ClientToolSurface = 'web' | 'office' | 'mobile' | 'sdk';\n\n/** The surface an `app_id` selects, or null when the runtime would refuse it. */\nexport function resolveClientToolSurface(appId: string | undefined | null): ClientToolSurface | null {\n if (typeof appId !== 'string' || appId.length === 0) return null;\n if (appId === CLIENT_TOOL_SURFACE_APP_IDS.olympus) return 'web';\n if (appId === CLIENT_TOOL_SURFACE_APP_IDS.officeAddin) return 'office';\n if (appId === CLIENT_TOOL_SURFACE_APP_IDS.mobile) return 'mobile';\n if (SDK_APP_ID_PATTERN.test(appId)) return 'sdk';\n return null;\n}\n"],"names":[],"mappings":";;;AAgBA,SAAS,mBACP,OACoF;AACpF,SACE,CAAC,CAAC,SACF,OAAO,UAAU,YACjB,OAAQ,MAAgC,YAAY;AAExD;AAeO,SAAS,4BAA4B,SAA0C;AACpF,QAAM,aAAa,OAAO,QAAQ,OAAO,EAAE,OAAO,CAAC,GAAG,KAAK,MAAM,mBAAmB,KAAK,CAAC;AAC1F,MAAI,WAAW,WAAW,EAAG,QAAO,CAAA;AAEpC,SAAO,WAAW,QAAQ,CAAC,CAAC,MAAM,KAAK,MAAM;AAC3C,QAAI,MAAM,eAAe,OAAW,QAAO,CAAA;AAE3C,QAAI;AACJ,QAAI;AACF,mBAAa,EAAE,GAAG,aAAa,MAAM,UAAU,EAAA;AAAA,IACjD,SAAS,OAAO;AACd,wBAAkB,KAAK,wBAAwB;AAAA,QAC7C,OAAO;AAAA,QACP,MAAM;AAAA,UACJ,MAAM;AAAA,UACN,SAAS;AAAA,UACT,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,QAAA;AAAA,MAC9D,CACD;AACD,aAAO,CAAA;AAAA,IACT;AAEA,UAAM,UAAW,MAEd;AACH,WAAO;AAAA,MACL;AAAA,QACE,YAAY;AAAA,UACV;AAAA,UACA,aAAa,MAAM,eAAe;AAAA,UAClC;AAAA,UACA,eAAe;AAAA,QAAA;AAAA,QAEjB,SAAS,OAAO,MAAM,YAAuC;AAC3D,gBAAM,aAAa,IAAI,gBAAA;AACvB,gBAAM,SAAS,MAAM,QAAQ,MAAM;AAAA;AAAA;AAAA,YAGjC,aAAY,mCAAS,eAAc;AAAA,YACnC,aAAa,WAAW;AAAA,YACxB,OAAO,MACL,QAAQ;AAAA,cACN,IAAI,MAAM,0DAA0D;AAAA,YAAA;AAAA,UACtE,CACH;AACD,iBAAO,kBAAkB,eAAe,OAAO,SAAS;AAAA,QAC1D;AAAA,MAAA;AAAA,IACF;AAAA,EAEJ,CAAC;AACH;AAIO,SAAS,+BACd,OAC2B;AAC3B,SAAO,2BAA2B,KAAK;AACzC;AAaO,SAAS,+BAA+B,SAA4B;AACzE,SAAO,OAAO,QAAQ,OAAO,EAC1B,OAAO,CAAC,CAAA,EAAG,KAAK,MAAM,CAAC,mBAAmB,KAAK,CAAC,EAChD,IAAI,CAAC,CAAC,IAAI,MAAM,IAAI;AACzB;AASO,MAAM,8BAA8B;AAAA,EACzC,SAAS;AAAA,EACT,aAAa;AAAA,EACb,QAAQ;AACV;AAEA,MAAM,qBAAqB;AAKpB,SAAS,yBAAyB,OAA4D;AACnG,MAAI,OAAO,UAAU,YAAY,MAAM,WAAW,EAAG,QAAO;AAC5D,MAAI,UAAU,4BAA4B,QAAS,QAAO;AAC1D,MAAI,UAAU,4BAA4B,YAAa,QAAO;AAC9D,MAAI,UAAU,4BAA4B,OAAQ,QAAO;AACzD,MAAI,mBAAmB,KAAK,KAAK,EAAG,QAAO;AAC3C,SAAO;AACT;"}
|
|
1
|
+
{"version":3,"file":"statewire-client-tools.js","sources":["../../src/chat/statewire-client-tools.ts"],"sourcesContent":["import { athenaDiagnostics } from '../diagnostics/athena-diagnostics';\nimport type { Toolkit } from '@assistant-ui/react';\nimport {\n type ClientToolHandlerContext,\n clientToolRunConfigEntries,\n type RegisteredClientTool,\n} from '@athenaintel/agent-runtime';\nimport { ToolResponse, toJSONSchema } from 'assistant-stream';\n\n/**\n * @deprecated The SDK's client tools ARE the shared `RegisteredClientTool`\n * shape from `@athenaintel/agent-runtime` (`definition` + `handler`); import\n * that type instead. Retained for one release so existing imports type-check.\n */\nexport type StatewireClientTool = RegisteredClientTool;\n\n/**\n * Marks a frontend tool as one that changes something for the person, so its\n * calls wait for their consent. Set by {@link requiresWriteConsent}. A\n * registered symbol (not a module-local one), so the mark is read by whichever\n * copy of the SDK collects the toolkit.\n */\nconst CLIENT_TOOL_WRITE_CONSENT = Symbol.for('athena.clientTool.writeConsent');\n\nexport interface ClientToolWriteConsentOptions {\n /**\n * What the tool does, as the rest of the sentence \"… wants to ___\" on the\n * consent card (e.g. `\"click and type on this page\"`). The card names the\n * tool when this is absent.\n */\n summary?: string;\n}\n\n/**\n * Mark a `frontendTools` entry as a write: the person is asked before its\n * first call in a conversation runs, the answer covers the rest of that\n * conversation, and a declined call answers the model with an error instead\n * of running. Unmarked tools run without asking, as before.\n *\n * ```ts\n * const frontendTools = {\n * save_note: requiresWriteConsent(\n * { description: '…', parameters, execute: saveNote },\n * { summary: 'save a note on this record' },\n * ),\n * };\n * ```\n *\n * The mark also travels to Athena (`requires_write`), which then never\n * answers the call on the person's behalf while their tab is away.\n */\nexport function requiresWriteConsent<T extends object>(\n tool: T,\n options: ClientToolWriteConsentOptions = {},\n): T {\n const summary = options.summary?.trim();\n return { ...tool, [CLIENT_TOOL_WRITE_CONSENT]: summary ? { summary } : {} };\n}\n\n/** The consent mark on a toolkit entry, or `null` for a tool that never asks. */\nexport function clientToolWriteConsent(entry: unknown): ClientToolWriteConsentOptions | null {\n if (!entry || typeof entry !== 'object') return null;\n const mark = (entry as Record<symbol, unknown>)[CLIENT_TOOL_WRITE_CONSENT];\n if (!mark || typeof mark !== 'object') return null;\n const summary = (mark as { summary?: unknown }).summary;\n return typeof summary === 'string' && summary ? { summary } : {};\n}\n\n/** Each consent-marked tool's own words for what it does, by tool name. */\nexport function clientToolWriteConsentSummaries(\n toolkit: Toolkit | undefined,\n): Readonly<Record<string, string>> {\n const summaries: Record<string, string> = {};\n for (const [name, entry] of Object.entries(toolkit ?? {})) {\n const summary = clientToolWriteConsent(entry)?.summary;\n if (summary) summaries[name] = summary;\n }\n return summaries;\n}\n\nfunction hasCallableExecute(\n entry: unknown,\n): entry is { execute: (args: Record<string, unknown>, context: unknown) => unknown } {\n return (\n !!entry &&\n typeof entry === 'object' &&\n typeof (entry as { execute?: unknown }).execute === 'function'\n );\n}\n\n/**\n * Collect the client-executable tools from a frontend toolkit, in the shared\n * `RegisteredClientTool` shape every statewire surface declares and executes.\n *\n * The deep-agent runtime drops frontend (`ui_*`) tool definitions, so tools\n * with a local `execute` must instead be declared through\n * `runConfig.custom.client_tools` and answered by the statewire client-tool\n * bridge. Schema conversion reuses `toJSONSchema` — the same conversion\n * primitive as the legacy assistant-transport — and a tool whose schema cannot\n * be converted is skipped with a warning rather than failing the whole set.\n * A tool gates on write consent only when its entry is marked with\n * `requiresWriteConsent`: an unmarked toolkit `execute` is the host app's own\n * code and runs without asking.\n */\nexport function collectStatewireClientTools(toolkit: Toolkit): RegisteredClientTool[] {\n const executable = Object.entries(toolkit).filter(([, entry]) => hasCallableExecute(entry));\n if (executable.length === 0) return [];\n\n return executable.flatMap(([name, entry]) => {\n if (entry.parameters === undefined) return [];\n\n let parameters: Record<string, unknown>;\n try {\n parameters = { ...toJSONSchema(entry.parameters) };\n } catch (error) {\n athenaDiagnostics.emit('tools.schema.warning', {\n level: 'warn',\n data: {\n tool: name,\n message: 'Frontend tool schema could not be converted for statewire; the tool is not registered',\n error: error instanceof Error ? error.message : String(error),\n },\n });\n return [];\n }\n\n const execute = (entry as {\n execute: (args: Record<string, unknown>, context: unknown) => unknown;\n }).execute;\n return [\n {\n definition: {\n name,\n description: entry.description ?? '',\n parameters,\n requiresWrite: clientToolWriteConsent(entry) !== null,\n },\n handler: async (args, context?: ClientToolHandlerContext) => {\n const output = await execute(args, {\n // The bridge always supplies the parked call's id and its signal,\n // which aborts when the call's request goes away (the run was\n // stopped); a host invoking the handler directly has neither.\n toolCallId: context?.toolCallId ?? '',\n abortSignal: context?.signal ?? new AbortController().signal,\n human: () =>\n Promise.reject(\n new Error('Human input is not supported for statewire client tools.'),\n ),\n });\n return output instanceof ToolResponse ? output.result : output;\n },\n },\n ];\n });\n}\n\n/** Wire entries for `runConfig.custom.client_tools` — the shared contract from\n * `@athenaintel/agent-runtime`, so the SDK cannot drift from the other surfaces. */\nexport function statewireClientToolWireEntries(\n tools: readonly RegisteredClientTool[],\n): Record<string, unknown>[] {\n return clientToolRunConfigEntries(tools);\n}\n\n/**\n * The frontend-toolkit entries that must ride `enabled_tools`: declarations\n * WITHOUT a local `execute`, which name registry tools the backend already\n * knows how to bind (Olympus's frontend-executed tool ids).\n *\n * Executable entries are deliberately left out. They are declared through\n * `client_tools`, and the run binds them by name; putting the same name into\n * `enabled_tools` would also switch on any backend toolkit that happens to\n * own a tool of that name, and the backend wins the resulting collision —\n * the client tool is dropped and the model calls the backend one instead.\n */\nexport function backendDeclaredFrontendToolIds(toolkit: Toolkit): string[] {\n return Object.entries(toolkit)\n .filter(([, entry]) => !hasCallableExecute(entry))\n .map(([name]) => name);\n}\n\n/**\n * The `app_id` values the deep-agent runtime accepts as a client-tool surface.\n * Mirrors `agora/agora/graphs/athena_deep_agent/client_tools.py`: the fixed\n * first-party ids plus the `computer_asset_<uuid>` shape Athena's provisioning\n * injects into a computer-asset app. Any other `app_id` (or none) gets no\n * surface, and the runtime drops every `client_tools` declaration for the run.\n */\nexport const CLIENT_TOOL_SURFACE_APP_IDS = {\n olympus: 'olympus',\n officeAddin: 'athena-office-addin',\n mobile: 'athena-mobile',\n} as const;\n\nconst SDK_APP_ID_PATTERN = /^computer_asset_[0-9a-f-]{8,64}$/;\n\nexport type ClientToolSurface = 'web' | 'office' | 'mobile' | 'sdk';\n\n/** The surface an `app_id` selects, or null when the runtime would refuse it. */\nexport function resolveClientToolSurface(appId: string | undefined | null): ClientToolSurface | null {\n if (typeof appId !== 'string' || appId.length === 0) return null;\n if (appId === CLIENT_TOOL_SURFACE_APP_IDS.olympus) return 'web';\n if (appId === CLIENT_TOOL_SURFACE_APP_IDS.officeAddin) return 'office';\n if (appId === CLIENT_TOOL_SURFACE_APP_IDS.mobile) return 'mobile';\n if (SDK_APP_ID_PATTERN.test(appId)) return 'sdk';\n return null;\n}\n"],"names":[],"mappings":"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"}
|
package/dist/chat-ui/index.cjs
CHANGED
|
@@ -25,6 +25,7 @@ const apiClient = require("./mentions/suggestions/data/api-client.cjs");
|
|
|
25
25
|
const useMentionSuggestions = require("./mentions/suggestions/data/use-mention-suggestions.cjs");
|
|
26
26
|
const index = require("./mentions/suggestions/data/sources/index.cjs");
|
|
27
27
|
const assetTypeIcons = require("./mentions/suggestions/data/sources/asset-type-icons.cjs");
|
|
28
|
+
const useAssetIconAppUrl = require("./mentions/use-asset-icon-app-url.cjs");
|
|
28
29
|
const types = require("./mentions/suggestions/data/types.cjs");
|
|
29
30
|
const assetUrls = require("./lib/asset-urls.cjs");
|
|
30
31
|
const messageComponents = require("./messages/message-components.cjs");
|
|
@@ -108,6 +109,7 @@ exports.buildRootMenuItems = index.buildRootMenuItems;
|
|
|
108
109
|
exports.assetTypeToIcon = assetTypeIcons.assetTypeToIcon;
|
|
109
110
|
exports.assetTypeToThumb = assetTypeIcons.assetTypeToThumb;
|
|
110
111
|
exports.configureAssetIconBasePath = assetTypeIcons.configureAssetIconBasePath;
|
|
112
|
+
exports.useAssetIconAppUrl = useAssetIconAppUrl.useAssetIconAppUrl;
|
|
111
113
|
exports.MentionMenuSection = types.MentionMenuSection;
|
|
112
114
|
exports.createOlympusAssetUrl = assetUrls.createOlympusAssetUrl;
|
|
113
115
|
exports.BranchPicker = messageComponents.BranchPicker;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/dist/chat-ui/index.d.ts
CHANGED
|
@@ -35,6 +35,7 @@ export { AthenaGraphQLError, MentionAuthCtx, fetchGraphQL, SIMPLE_SEARCH_QUERY,
|
|
|
35
35
|
export { ROOT_MENTION_CATEGORIES, useMentionSuggestions, webpageMentionFingerprint, type MentionRootCategory, type MentionSuggestionsStore, } from "./mentions/suggestions/data/use-mention-suggestions";
|
|
36
36
|
export { buildRootMenuItems } from "./mentions/suggestions/data/sources";
|
|
37
37
|
export { assetTypeToIcon, assetTypeToThumb, configureAssetIconBasePath, } from "./mentions/suggestions/data/sources/asset-type-icons";
|
|
38
|
+
export { useAssetIconAppUrl } from "./mentions/use-asset-icon-app-url";
|
|
38
39
|
export { MentionMenuSection, type MentionChatAgent, type MentionChatAgents, type MentionTool, type MenuItem, type MenuScope, type WebpageMention, type WebpageMentionScope, } from "./mentions/suggestions/data/types";
|
|
39
40
|
export { createOlympusAssetUrl } from "./lib/asset-urls";
|
|
40
41
|
export { COMPOSER_DOCK_CLASS, COMPOSER_EDITOR_ROW_CLASS, COMPOSER_PAPER_DOCK_CLASS, COMPOSER_PAPER_EDITOR_ROW_CLASS, COMPOSER_PAPER_SURFACE_CLASS, COMPOSER_PAPER_TOOLBAR_CLASS, COMPOSER_SURFACE_CLASS, COMPOSER_TOOLBAR_CLASS, BranchPicker, ComposerSendButton, ComposerStopButton, type ComposerSendButtonProps, type ComposerStopButtonProps, MarkdownText, THREAD_SCROLL_TO_BOTTOM_CLASS, UserMessage, UserMessageText, createAssistantMessage, type CreateAssistantMessageOptions, } from "./messages/message-components";
|
package/dist/chat-ui/index.js
CHANGED
|
@@ -23,6 +23,7 @@ import { AthenaGraphQLError, MentionAuthCtx, SIMPLE_SEARCH_QUERY, fetchGraphQL,
|
|
|
23
23
|
import { ROOT_MENTION_CATEGORIES, useMentionSuggestions, webpageMentionFingerprint } from "./mentions/suggestions/data/use-mention-suggestions.js";
|
|
24
24
|
import { buildRootMenuItems } from "./mentions/suggestions/data/sources/index.js";
|
|
25
25
|
import { assetTypeToIcon, assetTypeToThumb, configureAssetIconBasePath } from "./mentions/suggestions/data/sources/asset-type-icons.js";
|
|
26
|
+
import { useAssetIconAppUrl } from "./mentions/use-asset-icon-app-url.js";
|
|
26
27
|
import { MentionMenuSection } from "./mentions/suggestions/data/types.js";
|
|
27
28
|
import { createOlympusAssetUrl } from "./lib/asset-urls.js";
|
|
28
29
|
import { BranchPicker, COMPOSER_DOCK_CLASS, COMPOSER_EDITOR_ROW_CLASS, COMPOSER_PAPER_DOCK_CLASS, COMPOSER_PAPER_EDITOR_ROW_CLASS, COMPOSER_PAPER_SURFACE_CLASS, COMPOSER_PAPER_TOOLBAR_CLASS, COMPOSER_SURFACE_CLASS, COMPOSER_TOOLBAR_CLASS, ComposerSendButton, ComposerStopButton, MarkdownText, THREAD_SCROLL_TO_BOTTOM_CLASS, UserMessage, UserMessageText, createAssistantMessage } from "./messages/message-components.js";
|
|
@@ -164,6 +165,7 @@ export {
|
|
|
164
165
|
serializeMention,
|
|
165
166
|
slashCommandRows,
|
|
166
167
|
startHandoffCursorReveal,
|
|
168
|
+
useAssetIconAppUrl,
|
|
167
169
|
useAthenaBrowserDecisionConfirmer,
|
|
168
170
|
useBrowserDecisionConfirmer,
|
|
169
171
|
useMentionAuth,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -9,6 +9,8 @@ const lucideReact = require("lucide-react");
|
|
|
9
9
|
const assetUrls = require("../lib/asset-urls.cjs");
|
|
10
10
|
const apiClient = require("./suggestions/data/api-client.cjs");
|
|
11
11
|
const assetTypeIcons = require("./suggestions/data/sources/asset-type-icons.cjs");
|
|
12
|
+
const useAssetIconAppUrl = require("./use-asset-icon-app-url.cjs");
|
|
13
|
+
const mentionIcon = require("./suggestions/components/mention-icon.cjs");
|
|
12
14
|
const useMemberAvatar = require("./use-member-avatar.cjs");
|
|
13
15
|
const useMentionAsset = require("./use-mention-asset.cjs");
|
|
14
16
|
const screenPoint = require("../../agent-runtime/src/screen-point.cjs");
|
|
@@ -211,6 +213,7 @@ const MentionChip = ({
|
|
|
211
213
|
interactive = true
|
|
212
214
|
}) => {
|
|
213
215
|
const auth = require$$0.useContext(apiClient.MentionAuthCtx);
|
|
216
|
+
const iconAppUrl = useAssetIconAppUrl.useAssetIconAppUrl();
|
|
214
217
|
const memberAvatarUrl = useMemberAvatar.useMemberAvatarUrl(personEmail);
|
|
215
218
|
const [erroredAvatarUrl, setErroredAvatarUrl] = require$$0.useState();
|
|
216
219
|
const excerpt = sourceHref ? readSourceExcerpt(sourceHref) : null;
|
|
@@ -249,20 +252,23 @@ const MentionChip = ({
|
|
|
249
252
|
}
|
|
250
253
|
})();
|
|
251
254
|
const className = cnfast.cn(CHIP_PILL_CLASS, "max-w-[275px]", href && CHIP_LINK_CLASS);
|
|
252
|
-
const
|
|
255
|
+
const typeThumbnailUrl = repositoryFile ? void 0 : (asset == null ? void 0 : asset.assetType) && assetTypeIcons.assetTypeToThumb(asset.assetType, { appUrl: iconAppUrl }) || thumbnailUrl || (type === "asset" || type === "folder" ? assetTypeIcons.assetTypeToThumb(type === "folder" ? "folder" : assetType, { appUrl: iconAppUrl }) : void 0);
|
|
256
|
+
const typeThumbnail = mentionIcon.useThumbnailLoadFailure(typeThumbnailUrl);
|
|
257
|
+
const chipThumbnailUrl = typeThumbnail.failed ? void 0 : typeThumbnailUrl;
|
|
253
258
|
const AssetIcon = assetChipIcon((asset == null ? void 0 : asset.assetType) || assetType);
|
|
254
259
|
const contents = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
255
260
|
repositoryFile ? /* @__PURE__ */ jsxRuntime.jsx(GitHubMarkIcon, { className: CHIP_ICON_CLASS }) : chipThumbnailUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
256
261
|
"img",
|
|
257
262
|
{
|
|
263
|
+
ref: typeThumbnail.imageRef,
|
|
258
264
|
src: chipThumbnailUrl,
|
|
259
265
|
alt: "",
|
|
260
266
|
className: CHIP_ICON_CLASS,
|
|
261
267
|
draggable: false
|
|
262
268
|
}
|
|
263
269
|
) : type === "asset" || type === "folder" ? (
|
|
264
|
-
//
|
|
265
|
-
//
|
|
270
|
+
// Outside a provider with no host copy of the SVGs, or when one
|
|
271
|
+
// cannot load, the chip still draws the type's icon.
|
|
266
272
|
/* @__PURE__ */ jsxRuntime.jsx(AssetIcon, { className: CHIP_ICON_CLASS, "aria-hidden": "true" })
|
|
267
273
|
) : type === "webpage" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Globe2Icon, { className: CHIP_ICON_CLASS, "aria-hidden": "true" }) : type === "person" ? memberAvatarUrl && erroredAvatarUrl !== memberAvatarUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
268
274
|
"img",
|