@athenaintel/react 0.21.0 → 0.21.2
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 +13 -1
- package/dist/chat/tiptap-text.cjs +28 -23
- package/dist/chat/tiptap-text.cjs.map +1 -1
- package/dist/chat/tiptap-text.js +28 -23
- package/dist/chat/tiptap-text.js.map +1 -1
- package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.cjs +6 -6
- 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.js +6 -6
- package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.js.map +1 -1
- package/dist/packages/agent-runtime/src/conversation-index.cjs +19 -10
- package/dist/packages/agent-runtime/src/conversation-index.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/conversation-index.js +19 -10
- package/dist/packages/agent-runtime/src/conversation-index.js.map +1 -1
- package/dist/packages/react/package.json.cjs +1 -1
- package/dist/packages/react/package.json.js +1 -1
- package/dist/threads/api.cjs +29 -0
- package/dist/threads/api.cjs.map +1 -1
- package/dist/threads/api.d.ts +15 -0
- package/dist/threads/api.js +29 -0
- package/dist/threads/api.js.map +1 -1
- package/dist/threads/credential-shape.cjs +15 -0
- package/dist/threads/credential-shape.cjs.map +1 -0
- package/dist/threads/credential-shape.d.ts +17 -0
- package/dist/threads/credential-shape.js +15 -0
- package/dist/threads/credential-shape.js.map +1 -0
- package/dist/threads/statewire-thread-list.cjs +35 -3
- package/dist/threads/statewire-thread-list.cjs.map +1 -1
- package/dist/threads/statewire-thread-list.js +36 -4
- package/dist/threads/statewire-thread-list.js.map +1 -1
- package/dist/threads/use-statewire-conversation-history.cjs +3 -1
- package/dist/threads/use-statewire-conversation-history.cjs.map +1 -1
- package/dist/threads/use-statewire-conversation-history.d.ts +3 -1
- package/dist/threads/use-statewire-conversation-history.js +3 -1
- package/dist/threads/use-statewire-conversation-history.js.map +1 -1
- package/dist/toolkits/index.cjs +0 -7
- package/dist/toolkits/index.cjs.map +1 -1
- package/dist/toolkits/index.d.ts +0 -2
- package/dist/toolkits/index.js +0 -7
- package/dist/toolkits/index.js.map +1 -1
- package/dist/voice/index.d.ts +1 -1
- package/dist/voice/use-athena-voice-session.cjs.map +1 -1
- package/dist/voice/use-athena-voice-session.d.ts +37 -2
- package/dist/voice/use-athena-voice-session.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -178,7 +178,7 @@ const voice = useAthenaVoiceSession({
|
|
|
178
178
|
provider: 'openai', // "google" | "openai"
|
|
179
179
|
model: 'gpt-realtime-2',
|
|
180
180
|
voice: 'marin',
|
|
181
|
-
enabled_tools: [], // [] =
|
|
181
|
+
enabled_tools: [], // [] = no tools; omit for the server default set
|
|
182
182
|
},
|
|
183
183
|
onTranscript: (turn) => console.log(turn.speaker, turn.text),
|
|
184
184
|
});
|
|
@@ -189,6 +189,18 @@ const voice = useAthenaVoiceSession({
|
|
|
189
189
|
<button onClick={voice.stop}>Hang up</button>
|
|
190
190
|
```
|
|
191
191
|
|
|
192
|
+
**Voice tools.** `enabled_tools` takes voice tool names (`AthenaVoiceToolId`),
|
|
193
|
+
not the chat `Toolkits` ids — `'web_search_browse_toolkit'` is not a voice tool,
|
|
194
|
+
`'web_search'` is. Available: `web_search` (Exa; `search_web` is a legacy alias),
|
|
195
|
+
`browse` (read a URL), `search_knowledge_base` and `search_knowledge_base_lr`
|
|
196
|
+
(the workspace knowledge base; `_lr` is the thorough long-running retrieval),
|
|
197
|
+
`read_full_asset` and `search_assets` (as the viewer), `ask_agent` (hand a
|
|
198
|
+
question to the general agent), `save_note`, `get_current_time_est`. Tools that
|
|
199
|
+
call back into the client over LiveKit RPC (`write_to_clipboard`, the desktop and
|
|
200
|
+
mobile tools) are not part of the hook's contract. Omit the field for the
|
|
201
|
+
server's default set; pass `[]` for pure conversation. Agora refuses unknown
|
|
202
|
+
names with a 422 (#35466).
|
|
203
|
+
|
|
192
204
|
**Authentication.** Served from a Marathon computer-asset URL, the hook is
|
|
193
205
|
zero-config: it takes the viewer bearer from `useParentBridge()` and creates
|
|
194
206
|
the session **as the person using the app**, so it shows up under their
|
|
@@ -61,31 +61,36 @@ const TiptapText = ({ text }) => {
|
|
|
61
61
|
attributes: {
|
|
62
62
|
class: "prose prose-sm dark:prose-invert max-w-none focus:outline-none"
|
|
63
63
|
},
|
|
64
|
-
handleClick
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
64
|
+
// ProseMirror's `handleClick` fires on `mouseup`, where `preventDefault`
|
|
65
|
+
// cannot cancel the anchor's navigation; the native `click` event can.
|
|
66
|
+
handleDOMEvents: {
|
|
67
|
+
click: (_view, event) => {
|
|
68
|
+
var _a;
|
|
69
|
+
if (event.button !== 0 || event.defaultPrevented) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
const target = event.target;
|
|
73
|
+
const targetElement = target instanceof HTMLElement ? target : target instanceof Node ? target.parentElement : null;
|
|
74
|
+
if (!targetElement) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
const link = targetElement.closest("a[href]");
|
|
78
|
+
if (!(link instanceof HTMLAnchorElement)) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
if (handleLinkClickRef.current({
|
|
82
|
+
href: link.getAttribute("href") ?? link.href,
|
|
83
|
+
linkText: link.textContent,
|
|
84
|
+
target: link.target,
|
|
85
|
+
nativeEvent: event
|
|
86
|
+
})) {
|
|
87
|
+
event.preventDefault();
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
event.preventDefault();
|
|
91
|
+
(_a = window.open(link.href, link.target || "_blank")) == null ? void 0 : _a.focus();
|
|
84
92
|
return true;
|
|
85
93
|
}
|
|
86
|
-
event.preventDefault();
|
|
87
|
-
(_a = window.open(link.href, link.target || "_blank")) == null ? void 0 : _a.focus();
|
|
88
|
-
return true;
|
|
89
94
|
}
|
|
90
95
|
}
|
|
91
96
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tiptap-text.cjs","sources":["../../src/chat/tiptap-text.tsx"],"sourcesContent":["import { useEditor, EditorContent } from \"@tiptap/react\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport Link from \"@tiptap/extension-link\";\nimport { Table } from \"@tiptap/extension-table\";\nimport { TableRow } from \"@tiptap/extension-table-row\";\nimport { TableCell } from \"@tiptap/extension-table-cell\";\nimport { TableHeader } from \"@tiptap/extension-table-header\";\nimport { Markdown } from \"tiptap-markdown\";\nimport type { FC } from \"react\";\nimport { useEffect, useRef } from \"react\";\nimport { MentionExtension } from \"../chat-ui/mentions/mention-extension\";\nimport { useAthenaLinkClickHandler } from \"./athena-citation-links\";\n\nconst extensions = [\n StarterKit.configure({\n codeBlock: {\n HTMLAttributes: {\n class: \"rounded-md bg-muted p-3 font-mono text-sm\",\n },\n },\n }),\n Link.configure({\n openOnClick: false,\n autolink: true,\n HTMLAttributes: {\n class: \"text-primary underline\",\n target: \"_blank\",\n rel: \"noopener noreferrer\",\n },\n }),\n Table.configure({\n resizable: false,\n HTMLAttributes: {\n class: \"aui-table\",\n },\n }),\n TableRow,\n TableCell,\n TableHeader,\n Markdown.configure({\n html: true,\n tightLists: true,\n linkify: true,\n breaks: true,\n transformPastedText: false,\n transformCopiedText: false,\n }),\n MentionExtension,\n];\n\nexport const TiptapText: FC<{ text: string }> = ({ text }) => {\n const handleLinkClick = useAthenaLinkClickHandler();\n const handleLinkClickRef = useRef(handleLinkClick);\n handleLinkClickRef.current = handleLinkClick;\n\n const editor = useEditor({\n immediatelyRender: true,\n extensions,\n content: text,\n editable: false,\n editorProps: {\n attributes: {\n class: \"prose prose-sm dark:prose-invert max-w-none focus:outline-none\",\n },\n handleClick: (_view,
|
|
1
|
+
{"version":3,"file":"tiptap-text.cjs","sources":["../../src/chat/tiptap-text.tsx"],"sourcesContent":["import { useEditor, EditorContent } from \"@tiptap/react\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport Link from \"@tiptap/extension-link\";\nimport { Table } from \"@tiptap/extension-table\";\nimport { TableRow } from \"@tiptap/extension-table-row\";\nimport { TableCell } from \"@tiptap/extension-table-cell\";\nimport { TableHeader } from \"@tiptap/extension-table-header\";\nimport { Markdown } from \"tiptap-markdown\";\nimport type { FC } from \"react\";\nimport { useEffect, useRef } from \"react\";\nimport { MentionExtension } from \"../chat-ui/mentions/mention-extension\";\nimport { useAthenaLinkClickHandler } from \"./athena-citation-links\";\n\nconst extensions = [\n StarterKit.configure({\n codeBlock: {\n HTMLAttributes: {\n class: \"rounded-md bg-muted p-3 font-mono text-sm\",\n },\n },\n }),\n Link.configure({\n openOnClick: false,\n autolink: true,\n HTMLAttributes: {\n class: \"text-primary underline\",\n target: \"_blank\",\n rel: \"noopener noreferrer\",\n },\n }),\n Table.configure({\n resizable: false,\n HTMLAttributes: {\n class: \"aui-table\",\n },\n }),\n TableRow,\n TableCell,\n TableHeader,\n Markdown.configure({\n html: true,\n tightLists: true,\n linkify: true,\n breaks: true,\n transformPastedText: false,\n transformCopiedText: false,\n }),\n MentionExtension,\n];\n\nexport const TiptapText: FC<{ text: string }> = ({ text }) => {\n const handleLinkClick = useAthenaLinkClickHandler();\n const handleLinkClickRef = useRef(handleLinkClick);\n handleLinkClickRef.current = handleLinkClick;\n\n const editor = useEditor({\n immediatelyRender: true,\n extensions,\n content: text,\n editable: false,\n editorProps: {\n attributes: {\n class: \"prose prose-sm dark:prose-invert max-w-none focus:outline-none\",\n },\n // ProseMirror's `handleClick` fires on `mouseup`, where `preventDefault`\n // cannot cancel the anchor's navigation; the native `click` event can.\n handleDOMEvents: {\n click: (_view, event) => {\n if (event.button !== 0 || event.defaultPrevented) {\n return false;\n }\n\n const target = event.target;\n const targetElement =\n target instanceof HTMLElement\n ? target\n : target instanceof Node\n ? target.parentElement\n : null;\n if (!targetElement) {\n return false;\n }\n\n const link = targetElement.closest(\"a[href]\");\n if (!(link instanceof HTMLAnchorElement)) {\n return false;\n }\n\n if (\n handleLinkClickRef.current({\n href: link.getAttribute(\"href\") ?? link.href,\n linkText: link.textContent,\n target: link.target,\n nativeEvent: event,\n })\n ) {\n event.preventDefault();\n return true;\n }\n\n event.preventDefault();\n window.open(link.href, link.target || \"_blank\")?.focus();\n return true;\n },\n },\n },\n });\n\n useEffect(() => {\n if (editor && text !== (editor.storage as any).markdown?.getMarkdown()) {\n editor.commands.setContent(text);\n }\n }, [text, editor]);\n\n return <EditorContent editor={editor} />;\n};\n"],"names":["Table","TableRow","TableCell","TableHeader","Markdown","MentionExtension","useAthenaLinkClickHandler","useRef","useEditor","useEffect","jsx","EditorContent"],"mappings":";;;;;;;;;;;;;;AAaA,MAAM,aAAa;AAAA,EACjB,WAAW,UAAU;AAAA,IACnB,WAAW;AAAA,MACT,gBAAgB;AAAA,QACd,OAAO;AAAA,MAAA;AAAA,IACT;AAAA,EACF,CACD;AAAA,EACD,KAAK,UAAU;AAAA,IACb,aAAa;AAAA,IACb,UAAU;AAAA,IACV,gBAAgB;AAAA,MACd,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,KAAK;AAAA,IAAA;AAAA,EACP,CACD;AAAA,EACDA,MAAAA,MAAM,UAAU;AAAA,IACd,WAAW;AAAA,IACX,gBAAgB;AAAA,MACd,OAAO;AAAA,IAAA;AAAA,EACT,CACD;AAAA,EACDC,SAAAA;AAAAA,EACAC,UAAAA;AAAAA,EACAC,YAAAA;AAAAA,EACAC,eAAAA,SAAS,UAAU;AAAA,IACjB,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,qBAAqB;AAAA,IACrB,qBAAqB;AAAA,EAAA,CACtB;AAAA,EACDC,iBAAAA;AACF;AAEO,MAAM,aAAmC,CAAC,EAAE,WAAW;AAC5D,QAAM,kBAAkBC,oBAAAA,0BAAA;AACxB,QAAM,qBAAqBC,MAAAA,OAAO,eAAe;AACjD,qBAAmB,UAAU;AAE7B,QAAM,SAASC,QAAAA,UAAU;AAAA,IACvB,mBAAmB;AAAA,IACnB;AAAA,IACA,SAAS;AAAA,IACT,UAAU;AAAA,IACV,aAAa;AAAA,MACX,YAAY;AAAA,QACV,OAAO;AAAA,MAAA;AAAA;AAAA;AAAA,MAIT,iBAAiB;AAAA,QACf,OAAO,CAAC,OAAO,UAAU;;AACvB,cAAI,MAAM,WAAW,KAAK,MAAM,kBAAkB;AAChD,mBAAO;AAAA,UACT;AAEA,gBAAM,SAAS,MAAM;AACrB,gBAAM,gBACJ,kBAAkB,cACd,SACA,kBAAkB,OAChB,OAAO,gBACP;AACR,cAAI,CAAC,eAAe;AAClB,mBAAO;AAAA,UACT;AAEA,gBAAM,OAAO,cAAc,QAAQ,SAAS;AAC5C,cAAI,EAAE,gBAAgB,oBAAoB;AACxC,mBAAO;AAAA,UACT;AAEA,cACE,mBAAmB,QAAQ;AAAA,YACzB,MAAM,KAAK,aAAa,MAAM,KAAK,KAAK;AAAA,YACxC,UAAU,KAAK;AAAA,YACf,QAAQ,KAAK;AAAA,YACb,aAAa;AAAA,UAAA,CACd,GACD;AACA,kBAAM,eAAA;AACN,mBAAO;AAAA,UACT;AAEA,gBAAM,eAAA;AACN,uBAAO,KAAK,KAAK,MAAM,KAAK,UAAU,QAAQ,MAA9C,mBAAiD;AACjD,iBAAO;AAAA,QACT;AAAA,MAAA;AAAA,IACF;AAAA,EACF,CACD;AAEDC,QAAAA,UAAU,MAAM;;AACd,QAAI,UAAU,WAAU,YAAO,QAAgB,aAAvB,mBAAiC,gBAAe;AACtE,aAAO,SAAS,WAAW,IAAI;AAAA,IACjC;AAAA,EACF,GAAG,CAAC,MAAM,MAAM,CAAC;AAEjB,SAAOC,+BAACC,QAAAA,iBAAc,QAAgB;AACxC;;"}
|
package/dist/chat/tiptap-text.js
CHANGED
|
@@ -59,31 +59,36 @@ const TiptapText = ({ text }) => {
|
|
|
59
59
|
attributes: {
|
|
60
60
|
class: "prose prose-sm dark:prose-invert max-w-none focus:outline-none"
|
|
61
61
|
},
|
|
62
|
-
handleClick
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
62
|
+
// ProseMirror's `handleClick` fires on `mouseup`, where `preventDefault`
|
|
63
|
+
// cannot cancel the anchor's navigation; the native `click` event can.
|
|
64
|
+
handleDOMEvents: {
|
|
65
|
+
click: (_view, event) => {
|
|
66
|
+
var _a;
|
|
67
|
+
if (event.button !== 0 || event.defaultPrevented) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
const target = event.target;
|
|
71
|
+
const targetElement = target instanceof HTMLElement ? target : target instanceof Node ? target.parentElement : null;
|
|
72
|
+
if (!targetElement) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
const link = targetElement.closest("a[href]");
|
|
76
|
+
if (!(link instanceof HTMLAnchorElement)) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
if (handleLinkClickRef.current({
|
|
80
|
+
href: link.getAttribute("href") ?? link.href,
|
|
81
|
+
linkText: link.textContent,
|
|
82
|
+
target: link.target,
|
|
83
|
+
nativeEvent: event
|
|
84
|
+
})) {
|
|
85
|
+
event.preventDefault();
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
event.preventDefault();
|
|
89
|
+
(_a = window.open(link.href, link.target || "_blank")) == null ? void 0 : _a.focus();
|
|
82
90
|
return true;
|
|
83
91
|
}
|
|
84
|
-
event.preventDefault();
|
|
85
|
-
(_a = window.open(link.href, link.target || "_blank")) == null ? void 0 : _a.focus();
|
|
86
|
-
return true;
|
|
87
92
|
}
|
|
88
93
|
}
|
|
89
94
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tiptap-text.js","sources":["../../src/chat/tiptap-text.tsx"],"sourcesContent":["import { useEditor, EditorContent } from \"@tiptap/react\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport Link from \"@tiptap/extension-link\";\nimport { Table } from \"@tiptap/extension-table\";\nimport { TableRow } from \"@tiptap/extension-table-row\";\nimport { TableCell } from \"@tiptap/extension-table-cell\";\nimport { TableHeader } from \"@tiptap/extension-table-header\";\nimport { Markdown } from \"tiptap-markdown\";\nimport type { FC } from \"react\";\nimport { useEffect, useRef } from \"react\";\nimport { MentionExtension } from \"../chat-ui/mentions/mention-extension\";\nimport { useAthenaLinkClickHandler } from \"./athena-citation-links\";\n\nconst extensions = [\n StarterKit.configure({\n codeBlock: {\n HTMLAttributes: {\n class: \"rounded-md bg-muted p-3 font-mono text-sm\",\n },\n },\n }),\n Link.configure({\n openOnClick: false,\n autolink: true,\n HTMLAttributes: {\n class: \"text-primary underline\",\n target: \"_blank\",\n rel: \"noopener noreferrer\",\n },\n }),\n Table.configure({\n resizable: false,\n HTMLAttributes: {\n class: \"aui-table\",\n },\n }),\n TableRow,\n TableCell,\n TableHeader,\n Markdown.configure({\n html: true,\n tightLists: true,\n linkify: true,\n breaks: true,\n transformPastedText: false,\n transformCopiedText: false,\n }),\n MentionExtension,\n];\n\nexport const TiptapText: FC<{ text: string }> = ({ text }) => {\n const handleLinkClick = useAthenaLinkClickHandler();\n const handleLinkClickRef = useRef(handleLinkClick);\n handleLinkClickRef.current = handleLinkClick;\n\n const editor = useEditor({\n immediatelyRender: true,\n extensions,\n content: text,\n editable: false,\n editorProps: {\n attributes: {\n class: \"prose prose-sm dark:prose-invert max-w-none focus:outline-none\",\n },\n handleClick: (_view,
|
|
1
|
+
{"version":3,"file":"tiptap-text.js","sources":["../../src/chat/tiptap-text.tsx"],"sourcesContent":["import { useEditor, EditorContent } from \"@tiptap/react\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport Link from \"@tiptap/extension-link\";\nimport { Table } from \"@tiptap/extension-table\";\nimport { TableRow } from \"@tiptap/extension-table-row\";\nimport { TableCell } from \"@tiptap/extension-table-cell\";\nimport { TableHeader } from \"@tiptap/extension-table-header\";\nimport { Markdown } from \"tiptap-markdown\";\nimport type { FC } from \"react\";\nimport { useEffect, useRef } from \"react\";\nimport { MentionExtension } from \"../chat-ui/mentions/mention-extension\";\nimport { useAthenaLinkClickHandler } from \"./athena-citation-links\";\n\nconst extensions = [\n StarterKit.configure({\n codeBlock: {\n HTMLAttributes: {\n class: \"rounded-md bg-muted p-3 font-mono text-sm\",\n },\n },\n }),\n Link.configure({\n openOnClick: false,\n autolink: true,\n HTMLAttributes: {\n class: \"text-primary underline\",\n target: \"_blank\",\n rel: \"noopener noreferrer\",\n },\n }),\n Table.configure({\n resizable: false,\n HTMLAttributes: {\n class: \"aui-table\",\n },\n }),\n TableRow,\n TableCell,\n TableHeader,\n Markdown.configure({\n html: true,\n tightLists: true,\n linkify: true,\n breaks: true,\n transformPastedText: false,\n transformCopiedText: false,\n }),\n MentionExtension,\n];\n\nexport const TiptapText: FC<{ text: string }> = ({ text }) => {\n const handleLinkClick = useAthenaLinkClickHandler();\n const handleLinkClickRef = useRef(handleLinkClick);\n handleLinkClickRef.current = handleLinkClick;\n\n const editor = useEditor({\n immediatelyRender: true,\n extensions,\n content: text,\n editable: false,\n editorProps: {\n attributes: {\n class: \"prose prose-sm dark:prose-invert max-w-none focus:outline-none\",\n },\n // ProseMirror's `handleClick` fires on `mouseup`, where `preventDefault`\n // cannot cancel the anchor's navigation; the native `click` event can.\n handleDOMEvents: {\n click: (_view, event) => {\n if (event.button !== 0 || event.defaultPrevented) {\n return false;\n }\n\n const target = event.target;\n const targetElement =\n target instanceof HTMLElement\n ? target\n : target instanceof Node\n ? target.parentElement\n : null;\n if (!targetElement) {\n return false;\n }\n\n const link = targetElement.closest(\"a[href]\");\n if (!(link instanceof HTMLAnchorElement)) {\n return false;\n }\n\n if (\n handleLinkClickRef.current({\n href: link.getAttribute(\"href\") ?? link.href,\n linkText: link.textContent,\n target: link.target,\n nativeEvent: event,\n })\n ) {\n event.preventDefault();\n return true;\n }\n\n event.preventDefault();\n window.open(link.href, link.target || \"_blank\")?.focus();\n return true;\n },\n },\n },\n });\n\n useEffect(() => {\n if (editor && text !== (editor.storage as any).markdown?.getMarkdown()) {\n editor.commands.setContent(text);\n }\n }, [text, editor]);\n\n return <EditorContent editor={editor} />;\n};\n"],"names":[],"mappings":";;;;;;;;;;;;AAaA,MAAM,aAAa;AAAA,EACjB,WAAW,UAAU;AAAA,IACnB,WAAW;AAAA,MACT,gBAAgB;AAAA,QACd,OAAO;AAAA,MAAA;AAAA,IACT;AAAA,EACF,CACD;AAAA,EACD,KAAK,UAAU;AAAA,IACb,aAAa;AAAA,IACb,UAAU;AAAA,IACV,gBAAgB;AAAA,MACd,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,KAAK;AAAA,IAAA;AAAA,EACP,CACD;AAAA,EACD,MAAM,UAAU;AAAA,IACd,WAAW;AAAA,IACX,gBAAgB;AAAA,MACd,OAAO;AAAA,IAAA;AAAA,EACT,CACD;AAAA,EACD;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS,UAAU;AAAA,IACjB,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,qBAAqB;AAAA,IACrB,qBAAqB;AAAA,EAAA,CACtB;AAAA,EACD;AACF;AAEO,MAAM,aAAmC,CAAC,EAAE,WAAW;AAC5D,QAAM,kBAAkB,0BAAA;AACxB,QAAM,qBAAqB,OAAO,eAAe;AACjD,qBAAmB,UAAU;AAE7B,QAAM,SAAS,UAAU;AAAA,IACvB,mBAAmB;AAAA,IACnB;AAAA,IACA,SAAS;AAAA,IACT,UAAU;AAAA,IACV,aAAa;AAAA,MACX,YAAY;AAAA,QACV,OAAO;AAAA,MAAA;AAAA;AAAA;AAAA,MAIT,iBAAiB;AAAA,QACf,OAAO,CAAC,OAAO,UAAU;;AACvB,cAAI,MAAM,WAAW,KAAK,MAAM,kBAAkB;AAChD,mBAAO;AAAA,UACT;AAEA,gBAAM,SAAS,MAAM;AACrB,gBAAM,gBACJ,kBAAkB,cACd,SACA,kBAAkB,OAChB,OAAO,gBACP;AACR,cAAI,CAAC,eAAe;AAClB,mBAAO;AAAA,UACT;AAEA,gBAAM,OAAO,cAAc,QAAQ,SAAS;AAC5C,cAAI,EAAE,gBAAgB,oBAAoB;AACxC,mBAAO;AAAA,UACT;AAEA,cACE,mBAAmB,QAAQ;AAAA,YACzB,MAAM,KAAK,aAAa,MAAM,KAAK,KAAK;AAAA,YACxC,UAAU,KAAK;AAAA,YACf,QAAQ,KAAK;AAAA,YACb,aAAa;AAAA,UAAA,CACd,GACD;AACA,kBAAM,eAAA;AACN,mBAAO;AAAA,UACT;AAEA,gBAAM,eAAA;AACN,uBAAO,KAAK,KAAK,MAAM,KAAK,UAAU,QAAQ,MAA9C,mBAAiD;AACjD,iBAAO;AAAA,QACT;AAAA,MAAA;AAAA,IACF;AAAA,EACF,CACD;AAED,YAAU,MAAM;;AACd,QAAI,UAAU,WAAU,YAAO,QAAgB,aAAvB,mBAAiC,gBAAe;AACtE,aAAO,SAAS,WAAW,IAAI;AAAA,IACjC;AAAA,EACF,GAAG,CAAC,MAAM,MAAM,CAAC;AAEjB,SAAO,oBAAC,iBAAc,QAAgB;AACxC;"}
|
|
@@ -25,13 +25,10 @@ const ASSET_TYPE_ICON_FILE = {
|
|
|
25
25
|
image: "ic-img",
|
|
26
26
|
audio: "ic-audio",
|
|
27
27
|
video: "ic-video",
|
|
28
|
-
voice_memo: "ic-
|
|
28
|
+
voice_memo: "ic-voice_memo",
|
|
29
29
|
// Presentations
|
|
30
|
-
presentation: "ic-power_point",
|
|
31
|
-
office_presentation: "ic-power_point",
|
|
32
30
|
powerpoint_deck: "ic-power_point",
|
|
33
31
|
presentation_pptx_studio: "ic-power_point",
|
|
34
|
-
presentation_onlyoffice: "ic-power_point",
|
|
35
32
|
pptx: "ic-power_point",
|
|
36
33
|
// Email & Calendar
|
|
37
34
|
email: "ic-mail",
|
|
@@ -81,14 +78,16 @@ const ASSET_TYPE_ICON_FILE = {
|
|
|
81
78
|
browse_profile: "ic-profile",
|
|
82
79
|
// Workflows & Automation
|
|
83
80
|
aop: "ic-flow",
|
|
84
|
-
playbook: "ic-
|
|
81
|
+
playbook: "ic-playbook",
|
|
82
|
+
policy: "ic-policy",
|
|
85
83
|
flow: "ic-flow",
|
|
86
|
-
transform_project: "ic-
|
|
84
|
+
transform_project: "ic-transform_project",
|
|
87
85
|
// Collections & Notebooks
|
|
88
86
|
collection: "ic-collection",
|
|
89
87
|
notebook: "ic-notebook",
|
|
90
88
|
// UI & Apps
|
|
91
89
|
user_interface: "ic-app",
|
|
90
|
+
application: "ic-application",
|
|
92
91
|
canvas: "ic-canvas",
|
|
93
92
|
dashboard: "ic-dashboard",
|
|
94
93
|
figure: "ic_figure",
|
|
@@ -97,6 +96,7 @@ const ASSET_TYPE_ICON_FILE = {
|
|
|
97
96
|
company: "ic-company",
|
|
98
97
|
// Misc
|
|
99
98
|
prompt: "ic-ai",
|
|
99
|
+
skill: "ic-skill",
|
|
100
100
|
task_item: "ic-task",
|
|
101
101
|
spaces: "ic-spaces",
|
|
102
102
|
secret: "ic-secret",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asset-type-icons.cjs","sources":["../../../../../../src/chat-ui/mentions/suggestions/data/sources/asset-type-icons.ts"],"sourcesContent":["/**\n * Maps Athena asset types to SVG icon file names.\n *\n * The SVG files are bundled by the host app, which declares where they are\n * served from via `configureAssetIconBasePath`. Hosts that don't bundle them\n * fall back to the Lucide icons carried on every menu item.\n */\n\nlet iconBasePath: string | null = null;\n\n/**\n * Point the mention UI at the host's copy of the asset-type SVGs.\n * `basePath` is resolved against the host document; a trailing \"/\" is added\n * when the caller omits it.\n */\nexport function configureAssetIconBasePath({\n basePath,\n}: {\n basePath: string | null;\n}): void {\n iconBasePath = basePath ? basePath.replace(/\\/*$/, \"/\") : basePath;\n}\n\nconst ASSET_TYPE_ICON_FILE: Record<string, string> = {\n // Documents\n folder: \"ic-folder\",\n txt: \"ic-txt\",\n document: \"ic-word\",\n word_document: \"ic-word\",\n markdown: \"ic-md\",\n pdf: \"ic-pdf\",\n html: \"ic-html\",\n web_page: \"ic-html\",\n yaml: \"ic-yaml\",\n json: \"ic-json\",\n\n // Spreadsheets\n spreadsheet: \"ic-excel\",\n table: \"ic-table\",\n\n // Media\n image: \"ic-img\",\n audio: \"ic-audio\",\n video: \"ic-video\",\n voice_memo: \"ic-
|
|
1
|
+
{"version":3,"file":"asset-type-icons.cjs","sources":["../../../../../../src/chat-ui/mentions/suggestions/data/sources/asset-type-icons.ts"],"sourcesContent":["/**\n * Maps Athena asset types to SVG icon file names.\n *\n * The SVG files are bundled by the host app, which declares where they are\n * served from via `configureAssetIconBasePath`. Hosts that don't bundle them\n * fall back to the Lucide icons carried on every menu item.\n */\n\nlet iconBasePath: string | null = null;\n\n/**\n * Point the mention UI at the host's copy of the asset-type SVGs.\n * `basePath` is resolved against the host document; a trailing \"/\" is added\n * when the caller omits it.\n */\nexport function configureAssetIconBasePath({\n basePath,\n}: {\n basePath: string | null;\n}): void {\n iconBasePath = basePath ? basePath.replace(/\\/*$/, \"/\") : basePath;\n}\n\nconst ASSET_TYPE_ICON_FILE: Record<string, string> = {\n // Documents\n folder: \"ic-folder\",\n txt: \"ic-txt\",\n document: \"ic-word\",\n word_document: \"ic-word\",\n markdown: \"ic-md\",\n pdf: \"ic-pdf\",\n html: \"ic-html\",\n web_page: \"ic-html\",\n yaml: \"ic-yaml\",\n json: \"ic-json\",\n\n // Spreadsheets\n spreadsheet: \"ic-excel\",\n table: \"ic-table\",\n\n // Media\n image: \"ic-img\",\n audio: \"ic-audio\",\n video: \"ic-video\",\n voice_memo: \"ic-voice_memo\",\n\n // Presentations\n powerpoint_deck: \"ic-power_point\",\n presentation_pptx_studio: \"ic-power_point\",\n pptx: \"ic-power_point\",\n\n // Email & Calendar\n email: \"ic-mail\",\n email_message: \"ic-mail\",\n live_email_thread: \"ic-mail\",\n live_calendar_event: \"ic-mail\",\n\n // Code\n python: \"ic-py\",\n javascript: \"ic-js\",\n typescript: \"ic-code\",\n jsx: \"ic-code\",\n tsx: \"ic-code\",\n css: \"ic-code\",\n java: \"ic-code\",\n c: \"ic-code\",\n cpp: \"ic-code\",\n csharp: \"ic-code\",\n ruby: \"ic-code\",\n go: \"ic-code\",\n swift: \"ic-code\",\n php: \"ic-code\",\n rust: \"ic-code\",\n vue: \"ic-code\",\n wm_script: \"ic-code\",\n\n // Structured data\n database: \"ic-snippet\",\n schema: \"ic-schema\",\n catalog: \"ic-db\",\n drive: \"ic-db\",\n query_snippet: \"ic-snippet\",\n semantic_query: \"ic-snippet\",\n semantic_model: \"ic-db\",\n dataset: \"ic-db\",\n\n // Meetings & Calls\n meeting: \"ic-meeting\",\n video_session: \"ic-meeting\",\n phone_call: \"ic-phonecall\",\n\n // Sessions & Devices\n session: \"ic-session\",\n browse_session: \"ic-html\",\n virtual_machine: \"ic-html\",\n computer: \"ic-computer\",\n desktop_session: \"ic-desktop_session\",\n mobile_session: \"ic-mobile_session\",\n\n // Projects & Profiles\n project: \"ic-project\",\n browse_profile: \"ic-profile\",\n\n // Workflows & Automation\n aop: \"ic-flow\",\n playbook: \"ic-playbook\",\n policy: \"ic-policy\",\n flow: \"ic-flow\",\n transform_project: \"ic-transform_project\",\n\n // Collections & Notebooks\n collection: \"ic-collection\",\n notebook: \"ic-notebook\",\n\n // UI & Apps\n user_interface: \"ic-app\",\n application: \"ic-application\",\n canvas: \"ic-canvas\",\n dashboard: \"ic-dashboard\",\n figure: \"ic_figure\",\n\n // People & Companies\n person: \"ic-person\",\n company: \"ic-company\",\n\n // Misc\n prompt: \"ic-ai\",\n skill: \"ic-skill\",\n task_item: \"ic-task\",\n spaces: \"ic-spaces\",\n secret: \"ic-secret\",\n photoshop: \"ic-pts\",\n illustrator: \"ic-ai\",\n zip: \"ic-zip\",\n sevenzip: \"ic-zip\",\n\n // Google Drive / SharePoint / Salesforce\n gdrive_folder: \"ic-folder\",\n gdrive_file: \"ic-file\",\n salesforce_record: \"ic-db\",\n salesforce_file: \"ic-db\",\n};\n\nconst ASSET_TYPE_LUCIDE_FALLBACK: Record<string, string> = {\n folder: \"folder\",\n spreadsheet: \"sheet\",\n image: \"image\",\n video: \"video\",\n audio: \"headphones\",\n email: \"mail\",\n database: \"database\",\n project: \"folder-kanban\",\n meeting: \"video\",\n user_interface: \"layout\",\n};\n\n/**\n * Get the SVG thumbnail URL for an Athena asset type, or undefined when the\n * host app does not bundle the icon files.\n */\nexport function assetTypeToThumb(\n assetType: string | null | undefined,\n): string | undefined {\n if (!iconBasePath) return undefined;\n const name: string =\n (assetType ? ASSET_TYPE_ICON_FILE[assetType.toLowerCase()] : undefined) ??\n \"ic-file\";\n return `${iconBasePath}${name}.svg`;\n}\n\n/**\n * Get Lucide icon name fallback for an asset type.\n * Used when thumbnailUrl can't load.\n */\nexport function assetTypeToIcon(assetType: string | null | undefined): string {\n if (!assetType) return \"file\";\n return ASSET_TYPE_LUCIDE_FALLBACK[assetType.toLowerCase()] ?? \"file\";\n}\n"],"names":[],"mappings":";;AAQA,IAAI,eAA8B;AAO3B,SAAS,2BAA2B;AAAA,EACzC;AACF,GAES;AACP,iBAAe,WAAW,SAAS,QAAQ,QAAQ,GAAG,IAAI;AAC5D;AAEA,MAAM,uBAA+C;AAAA;AAAA,EAEnD,QAAQ;AAAA,EACR,KAAK;AAAA,EACL,UAAU;AAAA,EACV,eAAe;AAAA,EACf,UAAU;AAAA,EACV,KAAK;AAAA,EACL,MAAM;AAAA,EACN,UAAU;AAAA,EACV,MAAM;AAAA,EACN,MAAM;AAAA;AAAA,EAGN,aAAa;AAAA,EACb,OAAO;AAAA;AAAA,EAGP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,YAAY;AAAA;AAAA,EAGZ,iBAAiB;AAAA,EACjB,0BAA0B;AAAA,EAC1B,MAAM;AAAA;AAAA,EAGN,OAAO;AAAA,EACP,eAAe;AAAA,EACf,mBAAmB;AAAA,EACnB,qBAAqB;AAAA;AAAA,EAGrB,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,GAAG;AAAA,EACH,KAAK;AAAA,EACL,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,WAAW;AAAA;AAAA,EAGX,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,OAAO;AAAA,EACP,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,SAAS;AAAA;AAAA,EAGT,SAAS;AAAA,EACT,eAAe;AAAA,EACf,YAAY;AAAA;AAAA,EAGZ,SAAS;AAAA,EACT,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,UAAU;AAAA,EACV,iBAAiB;AAAA,EACjB,gBAAgB;AAAA;AAAA,EAGhB,SAAS;AAAA,EACT,gBAAgB;AAAA;AAAA,EAGhB,KAAK;AAAA,EACL,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,mBAAmB;AAAA;AAAA,EAGnB,YAAY;AAAA,EACZ,UAAU;AAAA;AAAA,EAGV,gBAAgB;AAAA,EAChB,aAAa;AAAA,EACb,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,QAAQ;AAAA;AAAA,EAGR,QAAQ;AAAA,EACR,SAAS;AAAA;AAAA,EAGT,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,aAAa;AAAA,EACb,KAAK;AAAA,EACL,UAAU;AAAA;AAAA,EAGV,eAAe;AAAA,EACf,aAAa;AAAA,EACb,mBAAmB;AAAA,EACnB,iBAAiB;AACnB;AAEA,MAAM,6BAAqD;AAAA,EACzD,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,UAAU;AAAA,EACV,SAAS;AAAA,EACT,SAAS;AAAA,EACT,gBAAgB;AAClB;AAMO,SAAS,iBACd,WACoB;AACpB,MAAI,CAAC,aAAc,QAAO;AAC1B,QAAM,QACH,YAAY,qBAAqB,UAAU,YAAA,CAAa,IAAI,WAC7D;AACF,SAAO,GAAG,YAAY,GAAG,IAAI;AAC/B;AAMO,SAAS,gBAAgB,WAA8C;AAC5E,MAAI,CAAC,UAAW,QAAO;AACvB,SAAO,2BAA2B,UAAU,YAAA,CAAa,KAAK;AAChE;;;;"}
|
|
@@ -23,13 +23,10 @@ const ASSET_TYPE_ICON_FILE = {
|
|
|
23
23
|
image: "ic-img",
|
|
24
24
|
audio: "ic-audio",
|
|
25
25
|
video: "ic-video",
|
|
26
|
-
voice_memo: "ic-
|
|
26
|
+
voice_memo: "ic-voice_memo",
|
|
27
27
|
// Presentations
|
|
28
|
-
presentation: "ic-power_point",
|
|
29
|
-
office_presentation: "ic-power_point",
|
|
30
28
|
powerpoint_deck: "ic-power_point",
|
|
31
29
|
presentation_pptx_studio: "ic-power_point",
|
|
32
|
-
presentation_onlyoffice: "ic-power_point",
|
|
33
30
|
pptx: "ic-power_point",
|
|
34
31
|
// Email & Calendar
|
|
35
32
|
email: "ic-mail",
|
|
@@ -79,14 +76,16 @@ const ASSET_TYPE_ICON_FILE = {
|
|
|
79
76
|
browse_profile: "ic-profile",
|
|
80
77
|
// Workflows & Automation
|
|
81
78
|
aop: "ic-flow",
|
|
82
|
-
playbook: "ic-
|
|
79
|
+
playbook: "ic-playbook",
|
|
80
|
+
policy: "ic-policy",
|
|
83
81
|
flow: "ic-flow",
|
|
84
|
-
transform_project: "ic-
|
|
82
|
+
transform_project: "ic-transform_project",
|
|
85
83
|
// Collections & Notebooks
|
|
86
84
|
collection: "ic-collection",
|
|
87
85
|
notebook: "ic-notebook",
|
|
88
86
|
// UI & Apps
|
|
89
87
|
user_interface: "ic-app",
|
|
88
|
+
application: "ic-application",
|
|
90
89
|
canvas: "ic-canvas",
|
|
91
90
|
dashboard: "ic-dashboard",
|
|
92
91
|
figure: "ic_figure",
|
|
@@ -95,6 +94,7 @@ const ASSET_TYPE_ICON_FILE = {
|
|
|
95
94
|
company: "ic-company",
|
|
96
95
|
// Misc
|
|
97
96
|
prompt: "ic-ai",
|
|
97
|
+
skill: "ic-skill",
|
|
98
98
|
task_item: "ic-task",
|
|
99
99
|
spaces: "ic-spaces",
|
|
100
100
|
secret: "ic-secret",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asset-type-icons.js","sources":["../../../../../../src/chat-ui/mentions/suggestions/data/sources/asset-type-icons.ts"],"sourcesContent":["/**\n * Maps Athena asset types to SVG icon file names.\n *\n * The SVG files are bundled by the host app, which declares where they are\n * served from via `configureAssetIconBasePath`. Hosts that don't bundle them\n * fall back to the Lucide icons carried on every menu item.\n */\n\nlet iconBasePath: string | null = null;\n\n/**\n * Point the mention UI at the host's copy of the asset-type SVGs.\n * `basePath` is resolved against the host document; a trailing \"/\" is added\n * when the caller omits it.\n */\nexport function configureAssetIconBasePath({\n basePath,\n}: {\n basePath: string | null;\n}): void {\n iconBasePath = basePath ? basePath.replace(/\\/*$/, \"/\") : basePath;\n}\n\nconst ASSET_TYPE_ICON_FILE: Record<string, string> = {\n // Documents\n folder: \"ic-folder\",\n txt: \"ic-txt\",\n document: \"ic-word\",\n word_document: \"ic-word\",\n markdown: \"ic-md\",\n pdf: \"ic-pdf\",\n html: \"ic-html\",\n web_page: \"ic-html\",\n yaml: \"ic-yaml\",\n json: \"ic-json\",\n\n // Spreadsheets\n spreadsheet: \"ic-excel\",\n table: \"ic-table\",\n\n // Media\n image: \"ic-img\",\n audio: \"ic-audio\",\n video: \"ic-video\",\n voice_memo: \"ic-
|
|
1
|
+
{"version":3,"file":"asset-type-icons.js","sources":["../../../../../../src/chat-ui/mentions/suggestions/data/sources/asset-type-icons.ts"],"sourcesContent":["/**\n * Maps Athena asset types to SVG icon file names.\n *\n * The SVG files are bundled by the host app, which declares where they are\n * served from via `configureAssetIconBasePath`. Hosts that don't bundle them\n * fall back to the Lucide icons carried on every menu item.\n */\n\nlet iconBasePath: string | null = null;\n\n/**\n * Point the mention UI at the host's copy of the asset-type SVGs.\n * `basePath` is resolved against the host document; a trailing \"/\" is added\n * when the caller omits it.\n */\nexport function configureAssetIconBasePath({\n basePath,\n}: {\n basePath: string | null;\n}): void {\n iconBasePath = basePath ? basePath.replace(/\\/*$/, \"/\") : basePath;\n}\n\nconst ASSET_TYPE_ICON_FILE: Record<string, string> = {\n // Documents\n folder: \"ic-folder\",\n txt: \"ic-txt\",\n document: \"ic-word\",\n word_document: \"ic-word\",\n markdown: \"ic-md\",\n pdf: \"ic-pdf\",\n html: \"ic-html\",\n web_page: \"ic-html\",\n yaml: \"ic-yaml\",\n json: \"ic-json\",\n\n // Spreadsheets\n spreadsheet: \"ic-excel\",\n table: \"ic-table\",\n\n // Media\n image: \"ic-img\",\n audio: \"ic-audio\",\n video: \"ic-video\",\n voice_memo: \"ic-voice_memo\",\n\n // Presentations\n powerpoint_deck: \"ic-power_point\",\n presentation_pptx_studio: \"ic-power_point\",\n pptx: \"ic-power_point\",\n\n // Email & Calendar\n email: \"ic-mail\",\n email_message: \"ic-mail\",\n live_email_thread: \"ic-mail\",\n live_calendar_event: \"ic-mail\",\n\n // Code\n python: \"ic-py\",\n javascript: \"ic-js\",\n typescript: \"ic-code\",\n jsx: \"ic-code\",\n tsx: \"ic-code\",\n css: \"ic-code\",\n java: \"ic-code\",\n c: \"ic-code\",\n cpp: \"ic-code\",\n csharp: \"ic-code\",\n ruby: \"ic-code\",\n go: \"ic-code\",\n swift: \"ic-code\",\n php: \"ic-code\",\n rust: \"ic-code\",\n vue: \"ic-code\",\n wm_script: \"ic-code\",\n\n // Structured data\n database: \"ic-snippet\",\n schema: \"ic-schema\",\n catalog: \"ic-db\",\n drive: \"ic-db\",\n query_snippet: \"ic-snippet\",\n semantic_query: \"ic-snippet\",\n semantic_model: \"ic-db\",\n dataset: \"ic-db\",\n\n // Meetings & Calls\n meeting: \"ic-meeting\",\n video_session: \"ic-meeting\",\n phone_call: \"ic-phonecall\",\n\n // Sessions & Devices\n session: \"ic-session\",\n browse_session: \"ic-html\",\n virtual_machine: \"ic-html\",\n computer: \"ic-computer\",\n desktop_session: \"ic-desktop_session\",\n mobile_session: \"ic-mobile_session\",\n\n // Projects & Profiles\n project: \"ic-project\",\n browse_profile: \"ic-profile\",\n\n // Workflows & Automation\n aop: \"ic-flow\",\n playbook: \"ic-playbook\",\n policy: \"ic-policy\",\n flow: \"ic-flow\",\n transform_project: \"ic-transform_project\",\n\n // Collections & Notebooks\n collection: \"ic-collection\",\n notebook: \"ic-notebook\",\n\n // UI & Apps\n user_interface: \"ic-app\",\n application: \"ic-application\",\n canvas: \"ic-canvas\",\n dashboard: \"ic-dashboard\",\n figure: \"ic_figure\",\n\n // People & Companies\n person: \"ic-person\",\n company: \"ic-company\",\n\n // Misc\n prompt: \"ic-ai\",\n skill: \"ic-skill\",\n task_item: \"ic-task\",\n spaces: \"ic-spaces\",\n secret: \"ic-secret\",\n photoshop: \"ic-pts\",\n illustrator: \"ic-ai\",\n zip: \"ic-zip\",\n sevenzip: \"ic-zip\",\n\n // Google Drive / SharePoint / Salesforce\n gdrive_folder: \"ic-folder\",\n gdrive_file: \"ic-file\",\n salesforce_record: \"ic-db\",\n salesforce_file: \"ic-db\",\n};\n\nconst ASSET_TYPE_LUCIDE_FALLBACK: Record<string, string> = {\n folder: \"folder\",\n spreadsheet: \"sheet\",\n image: \"image\",\n video: \"video\",\n audio: \"headphones\",\n email: \"mail\",\n database: \"database\",\n project: \"folder-kanban\",\n meeting: \"video\",\n user_interface: \"layout\",\n};\n\n/**\n * Get the SVG thumbnail URL for an Athena asset type, or undefined when the\n * host app does not bundle the icon files.\n */\nexport function assetTypeToThumb(\n assetType: string | null | undefined,\n): string | undefined {\n if (!iconBasePath) return undefined;\n const name: string =\n (assetType ? ASSET_TYPE_ICON_FILE[assetType.toLowerCase()] : undefined) ??\n \"ic-file\";\n return `${iconBasePath}${name}.svg`;\n}\n\n/**\n * Get Lucide icon name fallback for an asset type.\n * Used when thumbnailUrl can't load.\n */\nexport function assetTypeToIcon(assetType: string | null | undefined): string {\n if (!assetType) return \"file\";\n return ASSET_TYPE_LUCIDE_FALLBACK[assetType.toLowerCase()] ?? \"file\";\n}\n"],"names":[],"mappings":"AAQA,IAAI,eAA8B;AAO3B,SAAS,2BAA2B;AAAA,EACzC;AACF,GAES;AACP,iBAAe,WAAW,SAAS,QAAQ,QAAQ,GAAG,IAAI;AAC5D;AAEA,MAAM,uBAA+C;AAAA;AAAA,EAEnD,QAAQ;AAAA,EACR,KAAK;AAAA,EACL,UAAU;AAAA,EACV,eAAe;AAAA,EACf,UAAU;AAAA,EACV,KAAK;AAAA,EACL,MAAM;AAAA,EACN,UAAU;AAAA,EACV,MAAM;AAAA,EACN,MAAM;AAAA;AAAA,EAGN,aAAa;AAAA,EACb,OAAO;AAAA;AAAA,EAGP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,YAAY;AAAA;AAAA,EAGZ,iBAAiB;AAAA,EACjB,0BAA0B;AAAA,EAC1B,MAAM;AAAA;AAAA,EAGN,OAAO;AAAA,EACP,eAAe;AAAA,EACf,mBAAmB;AAAA,EACnB,qBAAqB;AAAA;AAAA,EAGrB,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,GAAG;AAAA,EACH,KAAK;AAAA,EACL,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,WAAW;AAAA;AAAA,EAGX,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,OAAO;AAAA,EACP,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,SAAS;AAAA;AAAA,EAGT,SAAS;AAAA,EACT,eAAe;AAAA,EACf,YAAY;AAAA;AAAA,EAGZ,SAAS;AAAA,EACT,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,UAAU;AAAA,EACV,iBAAiB;AAAA,EACjB,gBAAgB;AAAA;AAAA,EAGhB,SAAS;AAAA,EACT,gBAAgB;AAAA;AAAA,EAGhB,KAAK;AAAA,EACL,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,mBAAmB;AAAA;AAAA,EAGnB,YAAY;AAAA,EACZ,UAAU;AAAA;AAAA,EAGV,gBAAgB;AAAA,EAChB,aAAa;AAAA,EACb,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,QAAQ;AAAA;AAAA,EAGR,QAAQ;AAAA,EACR,SAAS;AAAA;AAAA,EAGT,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,aAAa;AAAA,EACb,KAAK;AAAA,EACL,UAAU;AAAA;AAAA,EAGV,eAAe;AAAA,EACf,aAAa;AAAA,EACb,mBAAmB;AAAA,EACnB,iBAAiB;AACnB;AAEA,MAAM,6BAAqD;AAAA,EACzD,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,UAAU;AAAA,EACV,SAAS;AAAA,EACT,SAAS;AAAA,EACT,gBAAgB;AAClB;AAMO,SAAS,iBACd,WACoB;AACpB,MAAI,CAAC,aAAc,QAAO;AAC1B,QAAM,QACH,YAAY,qBAAqB,UAAU,YAAA,CAAa,IAAI,WAC7D;AACF,SAAO,GAAG,YAAY,GAAG,IAAI;AAC/B;AAMO,SAAS,gBAAgB,WAA8C;AAC5E,MAAI,CAAC,UAAW,QAAO;AACvB,SAAO,2BAA2B,UAAU,YAAA,CAAa,KAAK;AAChE;"}
|
|
@@ -10,8 +10,15 @@ const statewireTransport = require("./statewire-transport.cjs");
|
|
|
10
10
|
function conversationIndexUrlFromStatewireUrl(statewireUrl) {
|
|
11
11
|
return statewireUrl.replace(/\/threads\/?$/, "/conversation-index");
|
|
12
12
|
}
|
|
13
|
+
function conversationIndexId({
|
|
14
|
+
workspaceId,
|
|
15
|
+
userId
|
|
16
|
+
}) {
|
|
17
|
+
return `${workspaceId}-${userId}`;
|
|
18
|
+
}
|
|
13
19
|
function useConversationIndex({
|
|
14
20
|
baseUrl,
|
|
21
|
+
workspaceId,
|
|
15
22
|
userId,
|
|
16
23
|
sse = false,
|
|
17
24
|
headers,
|
|
@@ -20,28 +27,29 @@ function useConversationIndex({
|
|
|
20
27
|
var _a, _b, _c;
|
|
21
28
|
const headersRef = react.useRef(headers);
|
|
22
29
|
headersRef.current = headers;
|
|
30
|
+
const indexId = conversationIndexId({ workspaceId, userId });
|
|
23
31
|
const [parked, setParked] = react.useState(
|
|
24
32
|
null
|
|
25
33
|
);
|
|
26
|
-
const denialRef = react.useRef({
|
|
27
|
-
if (denialRef.current.
|
|
28
|
-
denialRef.current = {
|
|
34
|
+
const denialRef = react.useRef({ indexId, state: authDenial.initialAuthDenialTrackerState() });
|
|
35
|
+
if (denialRef.current.indexId !== indexId) {
|
|
36
|
+
denialRef.current = { indexId, state: authDenial.initialAuthDenialTrackerState() };
|
|
29
37
|
}
|
|
30
|
-
const isParked = parked !== null && parked.
|
|
38
|
+
const isParked = parked !== null && parked.indexId === indexId;
|
|
31
39
|
const client = statewire.useStatewire({
|
|
32
40
|
transport: tap.withKey(
|
|
33
|
-
|
|
41
|
+
indexId,
|
|
34
42
|
!enabled || isParked ? statewireSuspended.StatewireSuspended({
|
|
35
|
-
message: (parked == null ? void 0 : parked.message) ?? (enabled ? "Conversation index authorization was refused." : "Conversation index is waiting for a signed-in user."),
|
|
43
|
+
message: (parked == null ? void 0 : parked.message) ?? (enabled ? "Conversation index authorization was refused." : "Conversation index is waiting for a signed-in user and workspace."),
|
|
36
44
|
...enabled ? {
|
|
37
45
|
onReconnectRequested: () => {
|
|
38
|
-
denialRef.current = {
|
|
46
|
+
denialRef.current = { indexId, state: authDenial.initialAuthDenialTrackerState() };
|
|
39
47
|
setParked(null);
|
|
40
48
|
}
|
|
41
49
|
} : {}
|
|
42
50
|
}) : statewireTransport.statewireTransport(
|
|
43
51
|
{
|
|
44
|
-
url: `${baseUrl.replace(/\/+$/, "")}/${encodeURIComponent(
|
|
52
|
+
url: `${baseUrl.replace(/\/+$/, "")}/${encodeURIComponent(indexId)}`,
|
|
45
53
|
headers: (ctx) => headersRef.current(ctx)
|
|
46
54
|
},
|
|
47
55
|
{ sse }
|
|
@@ -59,9 +67,9 @@ function useConversationIndex({
|
|
|
59
67
|
react.useEffect(() => {
|
|
60
68
|
if (!enabled || !shouldPark) return;
|
|
61
69
|
setParked(
|
|
62
|
-
(prev) => (prev == null ? void 0 : prev.
|
|
70
|
+
(prev) => (prev == null ? void 0 : prev.indexId) === indexId ? prev : { indexId, message: parkMessage }
|
|
63
71
|
);
|
|
64
|
-
}, [enabled, shouldPark, parkMessage,
|
|
72
|
+
}, [enabled, shouldPark, parkMessage, indexId]);
|
|
65
73
|
const commandsRef = react.useRef(client.commands);
|
|
66
74
|
commandsRef.current = client.commands;
|
|
67
75
|
const refresh = react.useCallback(() => commandsRef.current.refresh(), []);
|
|
@@ -75,6 +83,7 @@ function useConversationIndex({
|
|
|
75
83
|
loadMore
|
|
76
84
|
};
|
|
77
85
|
}
|
|
86
|
+
exports.conversationIndexId = conversationIndexId;
|
|
78
87
|
exports.conversationIndexUrlFromStatewireUrl = conversationIndexUrlFromStatewireUrl;
|
|
79
88
|
exports.useConversationIndex = useConversationIndex;
|
|
80
89
|
//# sourceMappingURL=conversation-index.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation-index.cjs","sources":["../../../../../agent-runtime/src/conversation-index.ts"],"sourcesContent":["import { withKey } from '@assistant-ui/tap';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { useStatewire, type StatewireClient } from 'statewire';\nimport { initialAuthDenialTrackerState, observeAuthDenial } from './auth-denial';\nimport { projectDeepAgentConnection } from './errors';\nimport { StatewireSuspended } from './statewire-suspended';\nimport { statewireTransport } from './statewire-transport';\nimport type { DeepAgentHeadersResolver } from './use-deep-agent-thread';\n\n/**\n * Client side of the per-user conversation-index statewire (issue #29088):\n * agora replicates the newest page of the user's conversation list as one\n * state object, so thread creation / lifecycle / title changes arrive as ops\n * instead of newest-page polling. The replicated rows are renderable and the\n * typed commands extend or refresh the inclusive window; reconnects are free\n * because the protocol's snapshot-first attach IS the newest-page reconcile.\n */\nexport interface ConversationIndexEntry {\n thread_id: string;\n title: string;\n updated_at: string;\n state: string | null;\n runtime: string | null;\n start_channel: string | null;\n asset_type: string;\n asset_id: string | null;\n app_id: string | null;\n trigger_type: string | null;\n last_message_preview: string | null;\n message_count: number | null;\n is_unread: boolean | null;\n /**\n * Canonical status_v2 planes, present once the publishing agora reports them\n * (absent from snapshots replicated by an older pod during a rollout).\n */\n session_status?: string | null;\n run_status?: string | null;\n}\n\ninterface ConversationIndexState {\n conversations?: ConversationIndexEntry[];\n has_more?: boolean;\n total_count?: number;\n}\n\ninterface ConversationIndexCommands {\n [key: string]: (...args: never[]) => unknown;\n refresh(): void;\n load_more(): void;\n}\n\nexport interface UseConversationIndexOptions {\n /** Base URL of the conversation-index host (the `…/v2/conversation-index` mount). */\n baseUrl: string;\n /** The signed-in user's id — the index instance id. Callers gate mounting on it. */\n userId: string;\n /** Transport selection: SSE when true, WebSocket otherwise. */\n sse?: boolean;\n headers: DeepAgentHeadersResolver;\n /** Park the transport until the caller has a resolved principal. */\n enabled?: boolean;\n}\n\nexport interface UseConversationIndexResult {\n /** Newest-page entries, or `null` until the attach snapshot applies. */\n entries: ConversationIndexEntry[] | null;\n hasNextPage: boolean;\n totalCount: number | null;\n connection: StatewireClient.Connection;\n refresh: () => Promise<void>;\n loadMore: () => Promise<void>;\n}\n\n/** The conversation-index host is mounted beside the threads host. */\nexport function conversationIndexUrlFromStatewireUrl(statewireUrl: string): string {\n return statewireUrl.replace(/\\/threads\\/?$/, '/conversation-index');\n}\n\n/**\n * Attach to the user's live conversation index.\n */\nexport function useConversationIndex({\n baseUrl,\n userId,\n sse = false,\n headers,\n enabled = true,\n}: UseConversationIndexOptions): UseConversationIndexResult {\n const headersRef = useRef(headers);\n headersRef.current = headers;\n // Auth-denial park (same policy as the thread transport): the index is a\n // pure push signal, so after a streak of `unauthorized` denials the retry\n // loop stops instead of hammering the attach endpoint every 30s for as long\n // as the tab lives. A remount or the parked connection's `reconnect()`\n // re-arms it.\n const [parked, setParked] = useState<{ userId: string; message: string | undefined } | null>(\n null\n );\n const denialRef = useRef({ userId, state: initialAuthDenialTrackerState() });\n if (denialRef.current.userId !== userId) {\n denialRef.current = { userId, state: initialAuthDenialTrackerState() };\n }\n const isParked = parked !== null && parked.userId === userId;\n const client = useStatewire<ConversationIndexState | undefined, ConversationIndexCommands>({\n transport: withKey(\n userId,\n !enabled || isParked\n ? StatewireSuspended<ConversationIndexCommands>({\n message:\n parked?.message ??\n (enabled\n ? 'Conversation index authorization was refused.'\n : 'Conversation index is waiting for a signed-in user.'),\n ...(enabled\n ? {\n onReconnectRequested: () => {\n denialRef.current = { userId, state: initialAuthDenialTrackerState() };\n setParked(null);\n },\n }\n : {}),\n })\n : statewireTransport<ConversationIndexState, ConversationIndexCommands>(\n {\n url: `${baseUrl.replace(/\\/+$/, '')}/${encodeURIComponent(userId)}`,\n headers: (ctx: { staleAuth: boolean }) => headersRef.current(ctx),\n },\n { sse }\n )\n ),\n initialState: undefined,\n });\n // Observing during render is idempotent (the tracker dedupes by attempt\n // number), so a re-render of the same connection state cannot double-count.\n // The tracker consumes the projected vocabulary, never the raw connection.\n const denial = observeAuthDenial(\n denialRef.current.state,\n projectDeepAgentConnection(client.connection)\n );\n denialRef.current.state = denial.state;\n const shouldPark = denial.park;\n const parkMessage = denial.state.message;\n useEffect(() => {\n if (!enabled || !shouldPark) return;\n setParked((prev) =>\n prev?.userId === userId ? prev : { userId, message: parkMessage }\n );\n }, [enabled, shouldPark, parkMessage, userId]);\n const commandsRef = useRef(client.commands);\n commandsRef.current = client.commands;\n const refresh = useCallback(() => commandsRef.current.refresh(), []);\n const loadMore = useCallback(() => commandsRef.current.load_more(), []);\n return {\n entries: client.state?.conversations ?? null,\n hasNextPage: client.state?.has_more ?? false,\n totalCount: client.state?.total_count ?? null,\n connection: client.connection,\n refresh,\n loadMore,\n };\n}\n\n/**\n * Stable change fingerprint over the entries a list surface reacts to. An\n * empty string means \"no snapshot yet\" — effects keyed on the fingerprint\n * should treat it as a no-op rather than an empty list. An applied-but-empty\n * snapshot serializes to `'[]'`, so deleting the last conversation still\n * changes the fingerprint. JSON-encoding keeps arbitrary titles from\n * colliding across field or entry boundaries.\n */\nexport function conversationIndexFingerprint(\n entries: ConversationIndexEntry[] | null\n): string {\n if (entries === null) return '';\n return JSON.stringify(\n entries.map((entry) => [\n entry.thread_id,\n entry.updated_at,\n entry.state,\n entry.title,\n entry.run_status ?? null,\n entry.is_unread ?? null,\n ])\n );\n}\n"],"names":["useRef","useState","initialAuthDenialTrackerState","useStatewire","withKey","StatewireSuspended","statewireTransport","observeAuthDenial","projectDeepAgentConnection","useEffect","useCallback"],"mappings":";;;;;;;;;AA0EO,SAAS,qCAAqC,cAA8B;AACjF,SAAO,aAAa,QAAQ,iBAAiB,qBAAqB;AACpE;AAKO,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA;AAAA,EACA,MAAM;AAAA,EACN;AAAA,EACA,UAAU;AACZ,GAA4D;;AAC1D,QAAM,aAAaA,MAAAA,OAAO,OAAO;AACjC,aAAW,UAAU;AAMrB,QAAM,CAAC,QAAQ,SAAS,IAAIC,MAAAA;AAAAA,IAC1B;AAAA,EAAA;AAEF,QAAM,YAAYD,MAAAA,OAAO,EAAE,QAAQ,OAAOE,WAAAA,8BAAA,GAAiC;AAC3E,MAAI,UAAU,QAAQ,WAAW,QAAQ;AACvC,cAAU,UAAU,EAAE,QAAQ,OAAOA,WAAAA,gCAA8B;AAAA,EACrE;AACA,QAAM,WAAW,WAAW,QAAQ,OAAO,WAAW;AACtD,QAAM,SAASC,UAAAA,aAA4E;AAAA,IACzF,WAAWC,IAAAA;AAAAA,MACT;AAAA,MACA,CAAC,WAAW,WACRC,sCAA8C;AAAA,QAC5C,UACE,iCAAQ,aACP,UACG,kDACA;AAAA,QACN,GAAI,UACA;AAAA,UACE,sBAAsB,MAAM;AAC1B,sBAAU,UAAU,EAAE,QAAQ,OAAOH,WAAAA,gCAA8B;AACnE,sBAAU,IAAI;AAAA,UAChB;AAAA,QAAA,IAEF,CAAA;AAAA,MAAC,CACN,IACDI,mBAAAA;AAAAA,QACE;AAAA,UACE,KAAK,GAAG,QAAQ,QAAQ,QAAQ,EAAE,CAAC,IAAI,mBAAmB,MAAM,CAAC;AAAA,UACjE,SAAS,CAAC,QAAgC,WAAW,QAAQ,GAAG;AAAA,QAAA;AAAA,QAElE,EAAE,IAAA;AAAA,MAAI;AAAA,IACR;AAAA,IAEN,cAAc;AAAA,EAAA,CACf;AAID,QAAM,SAASC,WAAAA;AAAAA,IACb,UAAU,QAAQ;AAAA,IAClBC,OAAAA,2BAA2B,OAAO,UAAU;AAAA,EAAA;AAE9C,YAAU,QAAQ,QAAQ,OAAO;AACjC,QAAM,aAAa,OAAO;AAC1B,QAAM,cAAc,OAAO,MAAM;AACjCC,QAAAA,UAAU,MAAM;AACd,QAAI,CAAC,WAAW,CAAC,WAAY;AAC7B;AAAA,MAAU,CAAC,UACT,6BAAM,YAAW,SAAS,OAAO,EAAE,QAAQ,SAAS,YAAA;AAAA,IAAY;AAAA,EAEpE,GAAG,CAAC,SAAS,YAAY,aAAa,MAAM,CAAC;AAC7C,QAAM,cAAcT,MAAAA,OAAO,OAAO,QAAQ;AAC1C,cAAY,UAAU,OAAO;AAC7B,QAAM,UAAUU,MAAAA,YAAY,MAAM,YAAY,QAAQ,QAAA,GAAW,EAAE;AACnE,QAAM,WAAWA,MAAAA,YAAY,MAAM,YAAY,QAAQ,UAAA,GAAa,EAAE;AACtE,SAAO;AAAA,IACL,WAAS,YAAO,UAAP,mBAAc,kBAAiB;AAAA,IACxC,eAAa,YAAO,UAAP,mBAAc,aAAY;AAAA,IACvC,cAAY,YAAO,UAAP,mBAAc,gBAAe;AAAA,IACzC,YAAY,OAAO;AAAA,IACnB;AAAA,IACA;AAAA,EAAA;AAEJ;;;"}
|
|
1
|
+
{"version":3,"file":"conversation-index.cjs","sources":["../../../../../agent-runtime/src/conversation-index.ts"],"sourcesContent":["import { withKey } from '@assistant-ui/tap';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { useStatewire, type StatewireClient } from 'statewire';\nimport { initialAuthDenialTrackerState, observeAuthDenial } from './auth-denial';\nimport { projectDeepAgentConnection } from './errors';\nimport { StatewireSuspended } from './statewire-suspended';\nimport { statewireTransport } from './statewire-transport';\nimport type { DeepAgentHeadersResolver } from './use-deep-agent-thread';\n\n/**\n * Client side of the conversation-index statewire (issue #29088): agora\n * replicates the newest page of one viewer's conversation list as one state\n * object, so thread creation / lifecycle / title changes arrive as ops\n * instead of newest-page polling. The replicated rows are renderable and the\n * typed commands extend or refresh the inclusive window; reconnects are free\n * because the protocol's snapshot-first attach IS the newest-page reconcile.\n *\n * A viewer is a `(workspaceId, userId)` pair and every pair is its own\n * instance (`/conversation-index/<workspaceId>-<userId>`): workspaces are\n * tenants, so a tab in one workspace never shares a window with a tab in\n * another, and switching workspaces attaches to a different instance instead\n * of retagging the one other tabs are still reading.\n */\nexport interface ConversationIndexEntry {\n thread_id: string;\n title: string;\n updated_at: string;\n state: string | null;\n runtime: string | null;\n start_channel: string | null;\n asset_type: string;\n asset_id: string | null;\n app_id: string | null;\n trigger_type: string | null;\n last_message_preview: string | null;\n message_count: number | null;\n is_unread: boolean | null;\n /**\n * Canonical status_v2 planes, present once the publishing agora reports them\n * (absent from snapshots replicated by an older pod during a rollout).\n */\n session_status?: string | null;\n run_status?: string | null;\n}\n\ninterface ConversationIndexState {\n conversations?: ConversationIndexEntry[];\n has_more?: boolean;\n total_count?: number;\n}\n\ninterface ConversationIndexCommands {\n [key: string]: (...args: never[]) => unknown;\n refresh(): void;\n load_more(): void;\n}\n\nexport interface UseConversationIndexOptions {\n /** Base URL of the conversation-index host (the `…/v2/conversation-index` mount). */\n baseUrl: string;\n /** The workspace being viewed — half of the instance id. Callers gate mounting on it. */\n workspaceId: string;\n /** The signed-in user's id — the other half. Callers gate mounting on it. */\n userId: string;\n /** Transport selection: SSE when true, WebSocket otherwise. */\n sse?: boolean;\n headers: DeepAgentHeadersResolver;\n /** Park the transport until the caller has a resolved principal. */\n enabled?: boolean;\n}\n\nexport interface UseConversationIndexResult {\n /** Newest-page entries, or `null` until the attach snapshot applies. */\n entries: ConversationIndexEntry[] | null;\n hasNextPage: boolean;\n totalCount: number | null;\n connection: StatewireClient.Connection;\n refresh: () => Promise<void>;\n loadMore: () => Promise<void>;\n}\n\n/** The conversation-index host is mounted beside the threads host. */\nexport function conversationIndexUrlFromStatewireUrl(statewireUrl: string): string {\n return statewireUrl.replace(/\\/threads\\/?$/, '/conversation-index');\n}\n\n/** Instance id of one viewer's index; mirrors agora's `conversation_index_id`. */\nexport function conversationIndexId({\n workspaceId,\n userId,\n}: {\n workspaceId: string;\n userId: string;\n}): string {\n return `${workspaceId}-${userId}`;\n}\n\n/**\n * Attach to the live conversation index of one `(workspace, user)` viewer.\n */\nexport function useConversationIndex({\n baseUrl,\n workspaceId,\n userId,\n sse = false,\n headers,\n enabled = true,\n}: UseConversationIndexOptions): UseConversationIndexResult {\n const headersRef = useRef(headers);\n headersRef.current = headers;\n const indexId = conversationIndexId({ workspaceId, userId });\n // Auth-denial park (same policy as the thread transport): the index is a\n // pure push signal, so after a streak of `unauthorized` denials the retry\n // loop stops instead of hammering the attach endpoint every 30s for as long\n // as the tab lives. A remount or the parked connection's `reconnect()`\n // re-arms it.\n const [parked, setParked] = useState<{ indexId: string; message: string | undefined } | null>(\n null\n );\n const denialRef = useRef({ indexId, state: initialAuthDenialTrackerState() });\n if (denialRef.current.indexId !== indexId) {\n denialRef.current = { indexId, state: initialAuthDenialTrackerState() };\n }\n const isParked = parked !== null && parked.indexId === indexId;\n const client = useStatewire<ConversationIndexState | undefined, ConversationIndexCommands>({\n transport: withKey(\n indexId,\n !enabled || isParked\n ? StatewireSuspended<ConversationIndexCommands>({\n message:\n parked?.message ??\n (enabled\n ? 'Conversation index authorization was refused.'\n : 'Conversation index is waiting for a signed-in user and workspace.'),\n ...(enabled\n ? {\n onReconnectRequested: () => {\n denialRef.current = { indexId, state: initialAuthDenialTrackerState() };\n setParked(null);\n },\n }\n : {}),\n })\n : statewireTransport<ConversationIndexState, ConversationIndexCommands>(\n {\n url: `${baseUrl.replace(/\\/+$/, '')}/${encodeURIComponent(indexId)}`,\n headers: (ctx: { staleAuth: boolean }) => headersRef.current(ctx),\n },\n { sse }\n )\n ),\n initialState: undefined,\n });\n // Observing during render is idempotent (the tracker dedupes by attempt\n // number), so a re-render of the same connection state cannot double-count.\n // The tracker consumes the projected vocabulary, never the raw connection.\n const denial = observeAuthDenial(\n denialRef.current.state,\n projectDeepAgentConnection(client.connection)\n );\n denialRef.current.state = denial.state;\n const shouldPark = denial.park;\n const parkMessage = denial.state.message;\n useEffect(() => {\n if (!enabled || !shouldPark) return;\n setParked((prev) =>\n prev?.indexId === indexId ? prev : { indexId, message: parkMessage }\n );\n }, [enabled, shouldPark, parkMessage, indexId]);\n const commandsRef = useRef(client.commands);\n commandsRef.current = client.commands;\n const refresh = useCallback(() => commandsRef.current.refresh(), []);\n const loadMore = useCallback(() => commandsRef.current.load_more(), []);\n return {\n entries: client.state?.conversations ?? null,\n hasNextPage: client.state?.has_more ?? false,\n totalCount: client.state?.total_count ?? null,\n connection: client.connection,\n refresh,\n loadMore,\n };\n}\n\n/**\n * Stable change fingerprint over the entries a list surface reacts to. An\n * empty string means \"no snapshot yet\" — effects keyed on the fingerprint\n * should treat it as a no-op rather than an empty list. An applied-but-empty\n * snapshot serializes to `'[]'`, so deleting the last conversation still\n * changes the fingerprint. JSON-encoding keeps arbitrary titles from\n * colliding across field or entry boundaries.\n */\nexport function conversationIndexFingerprint(\n entries: ConversationIndexEntry[] | null\n): string {\n if (entries === null) return '';\n return JSON.stringify(\n entries.map((entry) => [\n entry.thread_id,\n entry.updated_at,\n entry.state,\n entry.title,\n entry.run_status ?? null,\n entry.is_unread ?? null,\n ])\n );\n}\n"],"names":["useRef","useState","initialAuthDenialTrackerState","useStatewire","withKey","StatewireSuspended","statewireTransport","observeAuthDenial","projectDeepAgentConnection","useEffect","useCallback"],"mappings":";;;;;;;;;AAkFO,SAAS,qCAAqC,cAA8B;AACjF,SAAO,aAAa,QAAQ,iBAAiB,qBAAqB;AACpE;AAGO,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA;AACF,GAGW;AACT,SAAO,GAAG,WAAW,IAAI,MAAM;AACjC;AAKO,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA,MAAM;AAAA,EACN;AAAA,EACA,UAAU;AACZ,GAA4D;;AAC1D,QAAM,aAAaA,MAAAA,OAAO,OAAO;AACjC,aAAW,UAAU;AACrB,QAAM,UAAU,oBAAoB,EAAE,aAAa,QAAQ;AAM3D,QAAM,CAAC,QAAQ,SAAS,IAAIC,MAAAA;AAAAA,IAC1B;AAAA,EAAA;AAEF,QAAM,YAAYD,MAAAA,OAAO,EAAE,SAAS,OAAOE,WAAAA,8BAAA,GAAiC;AAC5E,MAAI,UAAU,QAAQ,YAAY,SAAS;AACzC,cAAU,UAAU,EAAE,SAAS,OAAOA,WAAAA,gCAA8B;AAAA,EACtE;AACA,QAAM,WAAW,WAAW,QAAQ,OAAO,YAAY;AACvD,QAAM,SAASC,UAAAA,aAA4E;AAAA,IACzF,WAAWC,IAAAA;AAAAA,MACT;AAAA,MACA,CAAC,WAAW,WACRC,sCAA8C;AAAA,QAC5C,UACE,iCAAQ,aACP,UACG,kDACA;AAAA,QACN,GAAI,UACA;AAAA,UACE,sBAAsB,MAAM;AAC1B,sBAAU,UAAU,EAAE,SAAS,OAAOH,WAAAA,gCAA8B;AACpE,sBAAU,IAAI;AAAA,UAChB;AAAA,QAAA,IAEF,CAAA;AAAA,MAAC,CACN,IACDI,mBAAAA;AAAAA,QACE;AAAA,UACE,KAAK,GAAG,QAAQ,QAAQ,QAAQ,EAAE,CAAC,IAAI,mBAAmB,OAAO,CAAC;AAAA,UAClE,SAAS,CAAC,QAAgC,WAAW,QAAQ,GAAG;AAAA,QAAA;AAAA,QAElE,EAAE,IAAA;AAAA,MAAI;AAAA,IACR;AAAA,IAEN,cAAc;AAAA,EAAA,CACf;AAID,QAAM,SAASC,WAAAA;AAAAA,IACb,UAAU,QAAQ;AAAA,IAClBC,OAAAA,2BAA2B,OAAO,UAAU;AAAA,EAAA;AAE9C,YAAU,QAAQ,QAAQ,OAAO;AACjC,QAAM,aAAa,OAAO;AAC1B,QAAM,cAAc,OAAO,MAAM;AACjCC,QAAAA,UAAU,MAAM;AACd,QAAI,CAAC,WAAW,CAAC,WAAY;AAC7B;AAAA,MAAU,CAAC,UACT,6BAAM,aAAY,UAAU,OAAO,EAAE,SAAS,SAAS,YAAA;AAAA,IAAY;AAAA,EAEvE,GAAG,CAAC,SAAS,YAAY,aAAa,OAAO,CAAC;AAC9C,QAAM,cAAcT,MAAAA,OAAO,OAAO,QAAQ;AAC1C,cAAY,UAAU,OAAO;AAC7B,QAAM,UAAUU,MAAAA,YAAY,MAAM,YAAY,QAAQ,QAAA,GAAW,EAAE;AACnE,QAAM,WAAWA,MAAAA,YAAY,MAAM,YAAY,QAAQ,UAAA,GAAa,EAAE;AACtE,SAAO;AAAA,IACL,WAAS,YAAO,UAAP,mBAAc,kBAAiB;AAAA,IACxC,eAAa,YAAO,UAAP,mBAAc,aAAY;AAAA,IACvC,cAAY,YAAO,UAAP,mBAAc,gBAAe;AAAA,IACzC,YAAY,OAAO;AAAA,IACnB;AAAA,IACA;AAAA,EAAA;AAEJ;;;;"}
|
|
@@ -8,8 +8,15 @@ import { statewireTransport } from "./statewire-transport.js";
|
|
|
8
8
|
function conversationIndexUrlFromStatewireUrl(statewireUrl) {
|
|
9
9
|
return statewireUrl.replace(/\/threads\/?$/, "/conversation-index");
|
|
10
10
|
}
|
|
11
|
+
function conversationIndexId({
|
|
12
|
+
workspaceId,
|
|
13
|
+
userId
|
|
14
|
+
}) {
|
|
15
|
+
return `${workspaceId}-${userId}`;
|
|
16
|
+
}
|
|
11
17
|
function useConversationIndex({
|
|
12
18
|
baseUrl,
|
|
19
|
+
workspaceId,
|
|
13
20
|
userId,
|
|
14
21
|
sse = false,
|
|
15
22
|
headers,
|
|
@@ -18,28 +25,29 @@ function useConversationIndex({
|
|
|
18
25
|
var _a, _b, _c;
|
|
19
26
|
const headersRef = useRef(headers);
|
|
20
27
|
headersRef.current = headers;
|
|
28
|
+
const indexId = conversationIndexId({ workspaceId, userId });
|
|
21
29
|
const [parked, setParked] = useState(
|
|
22
30
|
null
|
|
23
31
|
);
|
|
24
|
-
const denialRef = useRef({
|
|
25
|
-
if (denialRef.current.
|
|
26
|
-
denialRef.current = {
|
|
32
|
+
const denialRef = useRef({ indexId, state: initialAuthDenialTrackerState() });
|
|
33
|
+
if (denialRef.current.indexId !== indexId) {
|
|
34
|
+
denialRef.current = { indexId, state: initialAuthDenialTrackerState() };
|
|
27
35
|
}
|
|
28
|
-
const isParked = parked !== null && parked.
|
|
36
|
+
const isParked = parked !== null && parked.indexId === indexId;
|
|
29
37
|
const client = useStatewire({
|
|
30
38
|
transport: withKey(
|
|
31
|
-
|
|
39
|
+
indexId,
|
|
32
40
|
!enabled || isParked ? StatewireSuspended({
|
|
33
|
-
message: (parked == null ? void 0 : parked.message) ?? (enabled ? "Conversation index authorization was refused." : "Conversation index is waiting for a signed-in user."),
|
|
41
|
+
message: (parked == null ? void 0 : parked.message) ?? (enabled ? "Conversation index authorization was refused." : "Conversation index is waiting for a signed-in user and workspace."),
|
|
34
42
|
...enabled ? {
|
|
35
43
|
onReconnectRequested: () => {
|
|
36
|
-
denialRef.current = {
|
|
44
|
+
denialRef.current = { indexId, state: initialAuthDenialTrackerState() };
|
|
37
45
|
setParked(null);
|
|
38
46
|
}
|
|
39
47
|
} : {}
|
|
40
48
|
}) : statewireTransport(
|
|
41
49
|
{
|
|
42
|
-
url: `${baseUrl.replace(/\/+$/, "")}/${encodeURIComponent(
|
|
50
|
+
url: `${baseUrl.replace(/\/+$/, "")}/${encodeURIComponent(indexId)}`,
|
|
43
51
|
headers: (ctx) => headersRef.current(ctx)
|
|
44
52
|
},
|
|
45
53
|
{ sse }
|
|
@@ -57,9 +65,9 @@ function useConversationIndex({
|
|
|
57
65
|
useEffect(() => {
|
|
58
66
|
if (!enabled || !shouldPark) return;
|
|
59
67
|
setParked(
|
|
60
|
-
(prev) => (prev == null ? void 0 : prev.
|
|
68
|
+
(prev) => (prev == null ? void 0 : prev.indexId) === indexId ? prev : { indexId, message: parkMessage }
|
|
61
69
|
);
|
|
62
|
-
}, [enabled, shouldPark, parkMessage,
|
|
70
|
+
}, [enabled, shouldPark, parkMessage, indexId]);
|
|
63
71
|
const commandsRef = useRef(client.commands);
|
|
64
72
|
commandsRef.current = client.commands;
|
|
65
73
|
const refresh = useCallback(() => commandsRef.current.refresh(), []);
|
|
@@ -74,6 +82,7 @@ function useConversationIndex({
|
|
|
74
82
|
};
|
|
75
83
|
}
|
|
76
84
|
export {
|
|
85
|
+
conversationIndexId,
|
|
77
86
|
conversationIndexUrlFromStatewireUrl,
|
|
78
87
|
useConversationIndex
|
|
79
88
|
};
|