@athenaintel/react 0.22.0 → 0.22.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 CHANGED
@@ -160,13 +160,13 @@ unless a collab agent supplies one.
160
160
  ## Voice Sessions (`@athenaintel/react/voice`)
161
161
 
162
162
  `useAthenaVoiceSession` runs a real-time voice conversation with an Athena
163
- voice agent (Orpheus, over LiveKit) and streams the transcript into React
164
- state. It owns the whole browser side — session creation, room connect, mic,
163
+ voice agent and streams the transcript into React state. It owns the whole
164
+ browser side — session creation, room connect, mic,
165
165
  agent audio, live/interim transcript turns, reconnects, StrictMode-safe
166
166
  teardown — so an app is a `start()` button and a transcript list.
167
167
 
168
168
  ```bash
169
- bun add @athenaintel/react livekit-client # livekit-client is an optional peer dep
169
+ bun add @athenaintel/react livekit-client # optional peer dep, only needed for voice
170
170
  ```
171
171
 
172
172
  ```tsx
@@ -191,15 +191,15 @@ const voice = useAthenaVoiceSession({
191
191
 
192
192
  **Voice tools.** `enabled_tools` takes voice tool names (`AthenaVoiceToolId`),
193
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),
194
+ `'web_search'` is. Available: `web_search` (`search_web` is a legacy alias),
195
195
  `browse` (read a URL), `search_knowledge_base` and `search_knowledge_base_lr`
196
196
  (the workspace knowledge base; `_lr` is the thorough long-running retrieval),
197
197
  `read_full_asset` and `search_assets` (as the viewer), `ask_agent` (hand a
198
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
199
+ call back into the client over room RPC (`write_to_clipboard`, the desktop and
200
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).
201
+ server's default set; pass `[]` for pure conversation. The server refuses
202
+ unknown names with a 422.
203
203
 
204
204
  **Authentication.** Served from a Marathon computer-asset URL, the hook is
205
205
  zero-config: it takes the viewer bearer from `useParentBridge()` and creates
@@ -234,6 +234,14 @@ Both use the same bearer / API key as session creation. The full recipe
234
234
  template skill `voice-session-guide.md`. `createAthenaVoiceSession` is also
235
235
  exported if you hold the bridge state yourself.
236
236
 
237
+ ## What's new in 0.22.2
238
+
239
+ Public voice docs, JSDoc/`.d.ts` and the session-creation error message now use product-neutral wording (no internal service or transport names). No API changes.
240
+
241
+ ## What's new in 0.22.1
242
+
243
+ Tailwind Typography colors now follow theme tokens inside SDK roots; `userBubbleForeground` / `assistantForeground` apply to message text, headings, bold, inline code and blockquotes.
244
+
237
245
  ## What's new in 0.22.0
238
246
 
239
247
  0.22.0 adds a read-only **workspace presence** feed to `@athenaintel/react/collab`:
@@ -247,7 +255,7 @@ carries into titles. Admin-only. See
247
255
  ## What's new in 0.21.0
248
256
 
249
257
  0.21.0 adds the `@athenaintel/react/voice` entry: `useAthenaVoiceSession`
250
- (LiveKit lifecycle, mic, agent audio, live transcript turns, reconnect,
258
+ (room lifecycle, mic, agent audio, live transcript turns, reconnect,
251
259
  StrictMode-safe) and `createAthenaVoiceSession`. On Marathon it is
252
260
  zero-config and attributes the session to the viewer (workspace members and
253
261
  external SSO viewers); elsewhere pass `createSession`. See
@@ -5,39 +5,90 @@ const react = require("react");
5
5
  const editCommand = require("./edit-command.cjs");
6
6
  function useDeepAgentMessageEditSubmit() {
7
7
  const aui = store.useAui();
8
+ const alive = react.useRef(true);
9
+ const submitting = react.useRef(false);
10
+ react.useEffect(() => {
11
+ alive.current = true;
12
+ return () => {
13
+ alive.current = false;
14
+ };
15
+ }, []);
8
16
  return react.useCallback(async () => {
9
- var _a, _b;
17
+ var _a, _b, _c;
18
+ if (submitting.current) return false;
10
19
  const composer = aui.message().composer();
11
- const text = composer.getState().text.trim();
12
- if (!text) return false;
13
- const sourceId = aui.message().getState().id;
14
- const readExtras = () => editCommand.readDeepAgentStatewireExtras(aui.thread().getState().extras);
15
- const extras = readExtras();
16
- if (extras) {
17
- const preparedMetadata = (_a = extras.prepareMessageMetadata) == null ? void 0 : _a.call(extras);
18
- try {
19
- const command = await editCommand.resolveDeepAgentEditCommand({ readExtras, sourceId, text });
20
- if (command) {
21
- void Promise.resolve(
22
- extras.sendCommand(
23
- command,
24
- preparedMetadata === void 0 ? void 0 : { preparedMetadata }
25
- )
26
- ).catch(() => void 0);
27
- composer.cancel();
28
- return true;
29
- }
30
- if (preparedMetadata && extras.sendPreparedEdit) {
31
- extras.sendPreparedEdit(sourceId, preparedMetadata);
32
- return true;
20
+ const draft = composer.getState();
21
+ const text = draft.text.trim();
22
+ if (!text && draft.attachments.length === 0) return false;
23
+ const files = [];
24
+ const texts = text ? [text] : [];
25
+ for (const attachment of draft.attachments) {
26
+ if (attachment.status.type !== "complete")
27
+ throw new Error("Wait for attachments to finish before sending your edit.");
28
+ for (const part of attachment.content ?? []) {
29
+ if (part.type === "text") texts.push(part.text);
30
+ else if (part.type === "image")
31
+ files.push({ type: "file", mediaType: "image/*", url: part.image });
32
+ else if (part.type === "file")
33
+ files.push({
34
+ type: "file",
35
+ mediaType: part.mimeType,
36
+ url: part.data,
37
+ ...part.filename && { filename: part.filename }
38
+ });
39
+ else throw new Error("This attachment cannot be edited here.");
40
+ }
41
+ }
42
+ submitting.current = true;
43
+ try {
44
+ const sourceId = aui.message().getState().id;
45
+ const readExtras = () => alive.current ? editCommand.readDeepAgentStatewireExtras(aui.thread().getState().extras) : null;
46
+ const extras = readExtras();
47
+ if (extras) {
48
+ const preparedMetadata = (_a = extras.prepareMessageMetadata) == null ? void 0 : _a.call(extras);
49
+ try {
50
+ const command = await editCommand.resolveDeepAgentEditCommand({
51
+ readExtras,
52
+ sourceId,
53
+ text
54
+ });
55
+ if (!alive.current || !composer.getState().isEditing) {
56
+ if (preparedMetadata) (_b = extras.discardPreparedMessageMetadata) == null ? void 0 : _b.call(extras, preparedMetadata);
57
+ return false;
58
+ }
59
+ if (command) {
60
+ void Promise.resolve(
61
+ extras.sendCommand(
62
+ {
63
+ ...command,
64
+ message: {
65
+ ...command.message,
66
+ parts: [
67
+ ...files,
68
+ ...texts.length ? [{ type: "text", text: texts.join("\n\n") }] : []
69
+ ]
70
+ }
71
+ },
72
+ preparedMetadata === void 0 ? void 0 : { preparedMetadata }
73
+ )
74
+ ).catch(() => void 0);
75
+ composer.cancel();
76
+ return true;
77
+ }
78
+ if (preparedMetadata && extras.sendPreparedEdit) {
79
+ extras.sendPreparedEdit(sourceId, preparedMetadata);
80
+ return true;
81
+ }
82
+ } catch (error) {
83
+ if (preparedMetadata) (_c = extras.discardPreparedMessageMetadata) == null ? void 0 : _c.call(extras, preparedMetadata);
84
+ throw error;
33
85
  }
34
- } catch (error) {
35
- if (preparedMetadata) (_b = extras.discardPreparedMessageMetadata) == null ? void 0 : _b.call(extras, preparedMetadata);
36
- throw error;
37
86
  }
87
+ composer.send();
88
+ return true;
89
+ } finally {
90
+ submitting.current = false;
38
91
  }
39
- composer.send();
40
- return true;
41
92
  }, [aui]);
42
93
  }
43
94
  exports.useDeepAgentMessageEditSubmit = useDeepAgentMessageEditSubmit;
@@ -1 +1 @@
1
- {"version":3,"file":"use-deep-agent-edit-submit.cjs","sources":["../../../../../agent-runtime/src/use-deep-agent-edit-submit.ts"],"sourcesContent":["import { useAui } from '@assistant-ui/store';\nimport { useCallback } from 'react';\nimport { readDeepAgentStatewireExtras, resolveDeepAgentEditCommand } from './edit-command';\n\n/**\n * The submit handler of a message's edit composer on a deep-agent thread: the\n * edited text leaves as a `run/edit` anchored on the host's own parent of the\n * message (see `edit-command.ts`) — on a windowed wire the lane's\n * `anchorBefore` at the window edge, and older pages loaded first for a\n * message below the loaded range — instead of the library's converted-message\n * anchor, which the host rejects `wrong-anchor` after a chunked assistant turn\n * and at the window edge.\n *\n * Read inside the message scope (`aui.message()`): the library's optimistic\n * fold shows the edited bubble and rewinds the visible history the moment the\n * command is dispatched, so the edit composer closes right away. Falls back\n * to the composer's own `send()` when the thread is not a statewire thread or\n * the host has not settled the message yet. Resolves `false` only for an\n * empty draft.\n */\nexport function useDeepAgentMessageEditSubmit(): () => Promise<boolean> {\n const aui = useAui();\n return useCallback(async () => {\n const composer = aui.message().composer();\n const text = composer.getState().text.trim();\n if (!text) return false;\n const sourceId = aui.message().getState().id;\n const readExtras = () => readDeepAgentStatewireExtras(aui.thread().getState().extras);\n const extras = readExtras();\n if (extras) {\n const preparedMetadata = extras.prepareMessageMetadata?.();\n try {\n const command = await resolveDeepAgentEditCommand({ readExtras, sourceId, text });\n if (command) {\n void Promise.resolve(\n extras.sendCommand(\n command,\n preparedMetadata === undefined ? undefined : { preparedMetadata },\n ),\n ).catch(() => undefined);\n composer.cancel();\n return true;\n }\n if (preparedMetadata && extras.sendPreparedEdit) {\n extras.sendPreparedEdit(sourceId, preparedMetadata);\n return true;\n }\n } catch (error) {\n if (preparedMetadata) extras.discardPreparedMessageMetadata?.(preparedMetadata);\n throw error;\n }\n }\n composer.send();\n return true;\n }, [aui]);\n}\n"],"names":["useAui","useCallback","readDeepAgentStatewireExtras","resolveDeepAgentEditCommand"],"mappings":";;;;;AAoBO,SAAS,gCAAwD;AACtE,QAAM,MAAMA,MAAAA,OAAA;AACZ,SAAOC,MAAAA,YAAY,YAAY;;AAC7B,UAAM,WAAW,IAAI,QAAA,EAAU,SAAA;AAC/B,UAAM,OAAO,SAAS,SAAA,EAAW,KAAK,KAAA;AACtC,QAAI,CAAC,KAAM,QAAO;AAClB,UAAM,WAAW,IAAI,QAAA,EAAU,WAAW;AAC1C,UAAM,aAAa,MAAMC,yCAA6B,IAAI,SAAS,SAAA,EAAW,MAAM;AACpF,UAAM,SAAS,WAAA;AACf,QAAI,QAAQ;AACV,YAAM,oBAAmB,YAAO,2BAAP;AACzB,UAAI;AACF,cAAM,UAAU,MAAMC,YAAAA,4BAA4B,EAAE,YAAY,UAAU,MAAM;AAChF,YAAI,SAAS;AACX,eAAK,QAAQ;AAAA,YACX,OAAO;AAAA,cACL;AAAA,cACA,qBAAqB,SAAY,SAAY,EAAE,iBAAA;AAAA,YAAiB;AAAA,UAClE,EACA,MAAM,MAAM,MAAS;AACvB,mBAAS,OAAA;AACT,iBAAO;AAAA,QACT;AACA,YAAI,oBAAoB,OAAO,kBAAkB;AAC/C,iBAAO,iBAAiB,UAAU,gBAAgB;AAClD,iBAAO;AAAA,QACT;AAAA,MACF,SAAS,OAAO;AACd,YAAI,iBAAkB,cAAO,mCAAP,gCAAwC;AAC9D,cAAM;AAAA,MACR;AAAA,IACF;AACA,aAAS,KAAA;AACT,WAAO;AAAA,EACT,GAAG,CAAC,GAAG,CAAC;AACV;;"}
1
+ {"version":3,"file":"use-deep-agent-edit-submit.cjs","sources":["../../../../../agent-runtime/src/use-deep-agent-edit-submit.ts"],"sourcesContent":["import { useAui } from '@assistant-ui/store';\nimport type { StatewireThread } from '@assistant-ui/react-statewire';\nimport { useCallback, useEffect, useRef } from 'react';\nimport { readDeepAgentStatewireExtras, resolveDeepAgentEditCommand } from './edit-command';\n\n/**\n * The submit handler of a message's edit composer on a deep-agent thread: the\n * edited text leaves as a `run/edit` anchored on the host's own parent of the\n * message (see `edit-command.ts`) — on a windowed wire the lane's\n * `anchorBefore` at the window edge, and older pages loaded first for a\n * message below the loaded range — instead of the library's converted-message\n * anchor, which the host rejects `wrong-anchor` after a chunked assistant turn\n * and at the window edge.\n *\n * Read inside the message scope (`aui.message()`): the library's optimistic\n * fold shows the edited bubble and rewinds the visible history the moment the\n * command is dispatched, so the edit composer closes right away. Falls back\n * to the composer's own `send()` when the thread is not a statewire thread or\n * the host has not settled the message yet. Resolves `false` for an empty\n * draft, a duplicate submission, or an edit cancelled during preparation.\n */\nexport function useDeepAgentMessageEditSubmit(): () => Promise<boolean> {\n const aui = useAui();\n const alive = useRef(true);\n const submitting = useRef(false);\n useEffect(() => {\n alive.current = true;\n return () => {\n alive.current = false;\n };\n }, []);\n return useCallback(async () => {\n if (submitting.current) return false;\n const composer = aui.message().composer();\n const draft = composer.getState();\n const text = draft.text.trim();\n if (!text && draft.attachments.length === 0) return false;\n const files: StatewireThread.FilePart[] = [];\n const texts = text ? [text] : [];\n // beginEdit lifts original inline images/files into completed composer\n // attachments. The anchored command must carry those too: a text-only\n // reconstruction silently deleted them when editing a cross-device turn.\n for (const attachment of draft.attachments) {\n if (attachment.status.type !== 'complete')\n throw new Error('Wait for attachments to finish before sending your edit.');\n for (const part of attachment.content ?? []) {\n if (part.type === 'text') texts.push(part.text);\n else if (part.type === 'image')\n files.push({ type: 'file', mediaType: 'image/*', url: part.image });\n else if (part.type === 'file')\n files.push({\n type: 'file',\n mediaType: part.mimeType,\n url: part.data,\n ...(part.filename && { filename: part.filename }),\n });\n else throw new Error('This attachment cannot be edited here.');\n }\n }\n submitting.current = true;\n try {\n const sourceId = aui.message().getState().id;\n const readExtras = () =>\n alive.current ? readDeepAgentStatewireExtras(aui.thread().getState().extras) : null;\n const extras = readExtras();\n if (extras) {\n const preparedMetadata = extras.prepareMessageMetadata?.();\n try {\n const command = await resolveDeepAgentEditCommand({\n readExtras,\n sourceId,\n text,\n });\n if (!alive.current || !composer.getState().isEditing) {\n if (preparedMetadata) extras.discardPreparedMessageMetadata?.(preparedMetadata);\n return false;\n }\n if (command) {\n void Promise.resolve(\n extras.sendCommand(\n {\n ...command,\n message: {\n ...command.message,\n parts: [\n ...files,\n ...(texts.length\n ? [{ type: 'text' as const, text: texts.join('\\n\\n') }]\n : []),\n ],\n },\n },\n preparedMetadata === undefined ? undefined : { preparedMetadata },\n ),\n ).catch(() => undefined);\n composer.cancel();\n return true;\n }\n if (preparedMetadata && extras.sendPreparedEdit) {\n extras.sendPreparedEdit(sourceId, preparedMetadata);\n return true;\n }\n } catch (error) {\n if (preparedMetadata) extras.discardPreparedMessageMetadata?.(preparedMetadata);\n throw error;\n }\n }\n composer.send();\n return true;\n } finally {\n submitting.current = false;\n }\n }, [aui]);\n}\n"],"names":["useAui","useRef","useEffect","useCallback","readDeepAgentStatewireExtras","resolveDeepAgentEditCommand"],"mappings":";;;;;AAqBO,SAAS,gCAAwD;AACtE,QAAM,MAAMA,MAAAA,OAAA;AACZ,QAAM,QAAQC,MAAAA,OAAO,IAAI;AACzB,QAAM,aAAaA,MAAAA,OAAO,KAAK;AAC/BC,QAAAA,UAAU,MAAM;AACd,UAAM,UAAU;AAChB,WAAO,MAAM;AACX,YAAM,UAAU;AAAA,IAClB;AAAA,EACF,GAAG,CAAA,CAAE;AACL,SAAOC,MAAAA,YAAY,YAAY;;AAC7B,QAAI,WAAW,QAAS,QAAO;AAC/B,UAAM,WAAW,IAAI,QAAA,EAAU,SAAA;AAC/B,UAAM,QAAQ,SAAS,SAAA;AACvB,UAAM,OAAO,MAAM,KAAK,KAAA;AACxB,QAAI,CAAC,QAAQ,MAAM,YAAY,WAAW,EAAG,QAAO;AACpD,UAAM,QAAoC,CAAA;AAC1C,UAAM,QAAQ,OAAO,CAAC,IAAI,IAAI,CAAA;AAI9B,eAAW,cAAc,MAAM,aAAa;AAC1C,UAAI,WAAW,OAAO,SAAS;AAC7B,cAAM,IAAI,MAAM,0DAA0D;AAC5E,iBAAW,QAAQ,WAAW,WAAW,CAAA,GAAI;AAC3C,YAAI,KAAK,SAAS,OAAQ,OAAM,KAAK,KAAK,IAAI;AAAA,iBACrC,KAAK,SAAS;AACrB,gBAAM,KAAK,EAAE,MAAM,QAAQ,WAAW,WAAW,KAAK,KAAK,OAAO;AAAA,iBAC3D,KAAK,SAAS;AACrB,gBAAM,KAAK;AAAA,YACT,MAAM;AAAA,YACN,WAAW,KAAK;AAAA,YAChB,KAAK,KAAK;AAAA,YACV,GAAI,KAAK,YAAY,EAAE,UAAU,KAAK,SAAA;AAAA,UAAS,CAChD;AAAA,YACE,OAAM,IAAI,MAAM,wCAAwC;AAAA,MAC/D;AAAA,IACF;AACA,eAAW,UAAU;AACrB,QAAI;AACF,YAAM,WAAW,IAAI,QAAA,EAAU,WAAW;AAC1C,YAAM,aAAa,MACjB,MAAM,UAAUC,YAAAA,6BAA6B,IAAI,SAAS,WAAW,MAAM,IAAI;AACjF,YAAM,SAAS,WAAA;AACf,UAAI,QAAQ;AACV,cAAM,oBAAmB,YAAO,2BAAP;AACzB,YAAI;AACF,gBAAM,UAAU,MAAMC,wCAA4B;AAAA,YAChD;AAAA,YACA;AAAA,YACA;AAAA,UAAA,CACD;AACD,cAAI,CAAC,MAAM,WAAW,CAAC,SAAS,SAAA,EAAW,WAAW;AACpD,gBAAI,iBAAkB,cAAO,mCAAP,gCAAwC;AAC9D,mBAAO;AAAA,UACT;AACA,cAAI,SAAS;AACX,iBAAK,QAAQ;AAAA,cACX,OAAO;AAAA,gBACL;AAAA,kBACE,GAAG;AAAA,kBACH,SAAS;AAAA,oBACP,GAAG,QAAQ;AAAA,oBACX,OAAO;AAAA,sBACL,GAAG;AAAA,sBACH,GAAI,MAAM,SACN,CAAC,EAAE,MAAM,QAAiB,MAAM,MAAM,KAAK,MAAM,EAAA,CAAG,IACpD,CAAA;AAAA,oBAAC;AAAA,kBACP;AAAA,gBACF;AAAA,gBAEF,qBAAqB,SAAY,SAAY,EAAE,iBAAA;AAAA,cAAiB;AAAA,YAClE,EACA,MAAM,MAAM,MAAS;AACvB,qBAAS,OAAA;AACT,mBAAO;AAAA,UACT;AACA,cAAI,oBAAoB,OAAO,kBAAkB;AAC/C,mBAAO,iBAAiB,UAAU,gBAAgB;AAClD,mBAAO;AAAA,UACT;AAAA,QACF,SAAS,OAAO;AACd,cAAI,iBAAkB,cAAO,mCAAP,gCAAwC;AAC9D,gBAAM;AAAA,QACR;AAAA,MACF;AACA,eAAS,KAAA;AACT,aAAO;AAAA,IACT,UAAA;AACE,iBAAW,UAAU;AAAA,IACvB;AAAA,EACF,GAAG,CAAC,GAAG,CAAC;AACV;;"}
@@ -1,41 +1,92 @@
1
1
  import { useAui } from "@assistant-ui/store";
2
- import { useCallback } from "react";
3
- import { resolveDeepAgentEditCommand, readDeepAgentStatewireExtras } from "./edit-command.js";
2
+ import { useRef, useEffect, useCallback } from "react";
3
+ import { readDeepAgentStatewireExtras, resolveDeepAgentEditCommand } from "./edit-command.js";
4
4
  function useDeepAgentMessageEditSubmit() {
5
5
  const aui = useAui();
6
+ const alive = useRef(true);
7
+ const submitting = useRef(false);
8
+ useEffect(() => {
9
+ alive.current = true;
10
+ return () => {
11
+ alive.current = false;
12
+ };
13
+ }, []);
6
14
  return useCallback(async () => {
7
- var _a, _b;
15
+ var _a, _b, _c;
16
+ if (submitting.current) return false;
8
17
  const composer = aui.message().composer();
9
- const text = composer.getState().text.trim();
10
- if (!text) return false;
11
- const sourceId = aui.message().getState().id;
12
- const readExtras = () => readDeepAgentStatewireExtras(aui.thread().getState().extras);
13
- const extras = readExtras();
14
- if (extras) {
15
- const preparedMetadata = (_a = extras.prepareMessageMetadata) == null ? void 0 : _a.call(extras);
16
- try {
17
- const command = await resolveDeepAgentEditCommand({ readExtras, sourceId, text });
18
- if (command) {
19
- void Promise.resolve(
20
- extras.sendCommand(
21
- command,
22
- preparedMetadata === void 0 ? void 0 : { preparedMetadata }
23
- )
24
- ).catch(() => void 0);
25
- composer.cancel();
26
- return true;
27
- }
28
- if (preparedMetadata && extras.sendPreparedEdit) {
29
- extras.sendPreparedEdit(sourceId, preparedMetadata);
30
- return true;
18
+ const draft = composer.getState();
19
+ const text = draft.text.trim();
20
+ if (!text && draft.attachments.length === 0) return false;
21
+ const files = [];
22
+ const texts = text ? [text] : [];
23
+ for (const attachment of draft.attachments) {
24
+ if (attachment.status.type !== "complete")
25
+ throw new Error("Wait for attachments to finish before sending your edit.");
26
+ for (const part of attachment.content ?? []) {
27
+ if (part.type === "text") texts.push(part.text);
28
+ else if (part.type === "image")
29
+ files.push({ type: "file", mediaType: "image/*", url: part.image });
30
+ else if (part.type === "file")
31
+ files.push({
32
+ type: "file",
33
+ mediaType: part.mimeType,
34
+ url: part.data,
35
+ ...part.filename && { filename: part.filename }
36
+ });
37
+ else throw new Error("This attachment cannot be edited here.");
38
+ }
39
+ }
40
+ submitting.current = true;
41
+ try {
42
+ const sourceId = aui.message().getState().id;
43
+ const readExtras = () => alive.current ? readDeepAgentStatewireExtras(aui.thread().getState().extras) : null;
44
+ const extras = readExtras();
45
+ if (extras) {
46
+ const preparedMetadata = (_a = extras.prepareMessageMetadata) == null ? void 0 : _a.call(extras);
47
+ try {
48
+ const command = await resolveDeepAgentEditCommand({
49
+ readExtras,
50
+ sourceId,
51
+ text
52
+ });
53
+ if (!alive.current || !composer.getState().isEditing) {
54
+ if (preparedMetadata) (_b = extras.discardPreparedMessageMetadata) == null ? void 0 : _b.call(extras, preparedMetadata);
55
+ return false;
56
+ }
57
+ if (command) {
58
+ void Promise.resolve(
59
+ extras.sendCommand(
60
+ {
61
+ ...command,
62
+ message: {
63
+ ...command.message,
64
+ parts: [
65
+ ...files,
66
+ ...texts.length ? [{ type: "text", text: texts.join("\n\n") }] : []
67
+ ]
68
+ }
69
+ },
70
+ preparedMetadata === void 0 ? void 0 : { preparedMetadata }
71
+ )
72
+ ).catch(() => void 0);
73
+ composer.cancel();
74
+ return true;
75
+ }
76
+ if (preparedMetadata && extras.sendPreparedEdit) {
77
+ extras.sendPreparedEdit(sourceId, preparedMetadata);
78
+ return true;
79
+ }
80
+ } catch (error) {
81
+ if (preparedMetadata) (_c = extras.discardPreparedMessageMetadata) == null ? void 0 : _c.call(extras, preparedMetadata);
82
+ throw error;
31
83
  }
32
- } catch (error) {
33
- if (preparedMetadata) (_b = extras.discardPreparedMessageMetadata) == null ? void 0 : _b.call(extras, preparedMetadata);
34
- throw error;
35
84
  }
85
+ composer.send();
86
+ return true;
87
+ } finally {
88
+ submitting.current = false;
36
89
  }
37
- composer.send();
38
- return true;
39
90
  }, [aui]);
40
91
  }
41
92
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"use-deep-agent-edit-submit.js","sources":["../../../../../agent-runtime/src/use-deep-agent-edit-submit.ts"],"sourcesContent":["import { useAui } from '@assistant-ui/store';\nimport { useCallback } from 'react';\nimport { readDeepAgentStatewireExtras, resolveDeepAgentEditCommand } from './edit-command';\n\n/**\n * The submit handler of a message's edit composer on a deep-agent thread: the\n * edited text leaves as a `run/edit` anchored on the host's own parent of the\n * message (see `edit-command.ts`) — on a windowed wire the lane's\n * `anchorBefore` at the window edge, and older pages loaded first for a\n * message below the loaded range — instead of the library's converted-message\n * anchor, which the host rejects `wrong-anchor` after a chunked assistant turn\n * and at the window edge.\n *\n * Read inside the message scope (`aui.message()`): the library's optimistic\n * fold shows the edited bubble and rewinds the visible history the moment the\n * command is dispatched, so the edit composer closes right away. Falls back\n * to the composer's own `send()` when the thread is not a statewire thread or\n * the host has not settled the message yet. Resolves `false` only for an\n * empty draft.\n */\nexport function useDeepAgentMessageEditSubmit(): () => Promise<boolean> {\n const aui = useAui();\n return useCallback(async () => {\n const composer = aui.message().composer();\n const text = composer.getState().text.trim();\n if (!text) return false;\n const sourceId = aui.message().getState().id;\n const readExtras = () => readDeepAgentStatewireExtras(aui.thread().getState().extras);\n const extras = readExtras();\n if (extras) {\n const preparedMetadata = extras.prepareMessageMetadata?.();\n try {\n const command = await resolveDeepAgentEditCommand({ readExtras, sourceId, text });\n if (command) {\n void Promise.resolve(\n extras.sendCommand(\n command,\n preparedMetadata === undefined ? undefined : { preparedMetadata },\n ),\n ).catch(() => undefined);\n composer.cancel();\n return true;\n }\n if (preparedMetadata && extras.sendPreparedEdit) {\n extras.sendPreparedEdit(sourceId, preparedMetadata);\n return true;\n }\n } catch (error) {\n if (preparedMetadata) extras.discardPreparedMessageMetadata?.(preparedMetadata);\n throw error;\n }\n }\n composer.send();\n return true;\n }, [aui]);\n}\n"],"names":[],"mappings":";;;AAoBO,SAAS,gCAAwD;AACtE,QAAM,MAAM,OAAA;AACZ,SAAO,YAAY,YAAY;;AAC7B,UAAM,WAAW,IAAI,QAAA,EAAU,SAAA;AAC/B,UAAM,OAAO,SAAS,SAAA,EAAW,KAAK,KAAA;AACtC,QAAI,CAAC,KAAM,QAAO;AAClB,UAAM,WAAW,IAAI,QAAA,EAAU,WAAW;AAC1C,UAAM,aAAa,MAAM,6BAA6B,IAAI,SAAS,SAAA,EAAW,MAAM;AACpF,UAAM,SAAS,WAAA;AACf,QAAI,QAAQ;AACV,YAAM,oBAAmB,YAAO,2BAAP;AACzB,UAAI;AACF,cAAM,UAAU,MAAM,4BAA4B,EAAE,YAAY,UAAU,MAAM;AAChF,YAAI,SAAS;AACX,eAAK,QAAQ;AAAA,YACX,OAAO;AAAA,cACL;AAAA,cACA,qBAAqB,SAAY,SAAY,EAAE,iBAAA;AAAA,YAAiB;AAAA,UAClE,EACA,MAAM,MAAM,MAAS;AACvB,mBAAS,OAAA;AACT,iBAAO;AAAA,QACT;AACA,YAAI,oBAAoB,OAAO,kBAAkB;AAC/C,iBAAO,iBAAiB,UAAU,gBAAgB;AAClD,iBAAO;AAAA,QACT;AAAA,MACF,SAAS,OAAO;AACd,YAAI,iBAAkB,cAAO,mCAAP,gCAAwC;AAC9D,cAAM;AAAA,MACR;AAAA,IACF;AACA,aAAS,KAAA;AACT,WAAO;AAAA,EACT,GAAG,CAAC,GAAG,CAAC;AACV;"}
1
+ {"version":3,"file":"use-deep-agent-edit-submit.js","sources":["../../../../../agent-runtime/src/use-deep-agent-edit-submit.ts"],"sourcesContent":["import { useAui } from '@assistant-ui/store';\nimport type { StatewireThread } from '@assistant-ui/react-statewire';\nimport { useCallback, useEffect, useRef } from 'react';\nimport { readDeepAgentStatewireExtras, resolveDeepAgentEditCommand } from './edit-command';\n\n/**\n * The submit handler of a message's edit composer on a deep-agent thread: the\n * edited text leaves as a `run/edit` anchored on the host's own parent of the\n * message (see `edit-command.ts`) — on a windowed wire the lane's\n * `anchorBefore` at the window edge, and older pages loaded first for a\n * message below the loaded range — instead of the library's converted-message\n * anchor, which the host rejects `wrong-anchor` after a chunked assistant turn\n * and at the window edge.\n *\n * Read inside the message scope (`aui.message()`): the library's optimistic\n * fold shows the edited bubble and rewinds the visible history the moment the\n * command is dispatched, so the edit composer closes right away. Falls back\n * to the composer's own `send()` when the thread is not a statewire thread or\n * the host has not settled the message yet. Resolves `false` for an empty\n * draft, a duplicate submission, or an edit cancelled during preparation.\n */\nexport function useDeepAgentMessageEditSubmit(): () => Promise<boolean> {\n const aui = useAui();\n const alive = useRef(true);\n const submitting = useRef(false);\n useEffect(() => {\n alive.current = true;\n return () => {\n alive.current = false;\n };\n }, []);\n return useCallback(async () => {\n if (submitting.current) return false;\n const composer = aui.message().composer();\n const draft = composer.getState();\n const text = draft.text.trim();\n if (!text && draft.attachments.length === 0) return false;\n const files: StatewireThread.FilePart[] = [];\n const texts = text ? [text] : [];\n // beginEdit lifts original inline images/files into completed composer\n // attachments. The anchored command must carry those too: a text-only\n // reconstruction silently deleted them when editing a cross-device turn.\n for (const attachment of draft.attachments) {\n if (attachment.status.type !== 'complete')\n throw new Error('Wait for attachments to finish before sending your edit.');\n for (const part of attachment.content ?? []) {\n if (part.type === 'text') texts.push(part.text);\n else if (part.type === 'image')\n files.push({ type: 'file', mediaType: 'image/*', url: part.image });\n else if (part.type === 'file')\n files.push({\n type: 'file',\n mediaType: part.mimeType,\n url: part.data,\n ...(part.filename && { filename: part.filename }),\n });\n else throw new Error('This attachment cannot be edited here.');\n }\n }\n submitting.current = true;\n try {\n const sourceId = aui.message().getState().id;\n const readExtras = () =>\n alive.current ? readDeepAgentStatewireExtras(aui.thread().getState().extras) : null;\n const extras = readExtras();\n if (extras) {\n const preparedMetadata = extras.prepareMessageMetadata?.();\n try {\n const command = await resolveDeepAgentEditCommand({\n readExtras,\n sourceId,\n text,\n });\n if (!alive.current || !composer.getState().isEditing) {\n if (preparedMetadata) extras.discardPreparedMessageMetadata?.(preparedMetadata);\n return false;\n }\n if (command) {\n void Promise.resolve(\n extras.sendCommand(\n {\n ...command,\n message: {\n ...command.message,\n parts: [\n ...files,\n ...(texts.length\n ? [{ type: 'text' as const, text: texts.join('\\n\\n') }]\n : []),\n ],\n },\n },\n preparedMetadata === undefined ? undefined : { preparedMetadata },\n ),\n ).catch(() => undefined);\n composer.cancel();\n return true;\n }\n if (preparedMetadata && extras.sendPreparedEdit) {\n extras.sendPreparedEdit(sourceId, preparedMetadata);\n return true;\n }\n } catch (error) {\n if (preparedMetadata) extras.discardPreparedMessageMetadata?.(preparedMetadata);\n throw error;\n }\n }\n composer.send();\n return true;\n } finally {\n submitting.current = false;\n }\n }, [aui]);\n}\n"],"names":[],"mappings":";;;AAqBO,SAAS,gCAAwD;AACtE,QAAM,MAAM,OAAA;AACZ,QAAM,QAAQ,OAAO,IAAI;AACzB,QAAM,aAAa,OAAO,KAAK;AAC/B,YAAU,MAAM;AACd,UAAM,UAAU;AAChB,WAAO,MAAM;AACX,YAAM,UAAU;AAAA,IAClB;AAAA,EACF,GAAG,CAAA,CAAE;AACL,SAAO,YAAY,YAAY;;AAC7B,QAAI,WAAW,QAAS,QAAO;AAC/B,UAAM,WAAW,IAAI,QAAA,EAAU,SAAA;AAC/B,UAAM,QAAQ,SAAS,SAAA;AACvB,UAAM,OAAO,MAAM,KAAK,KAAA;AACxB,QAAI,CAAC,QAAQ,MAAM,YAAY,WAAW,EAAG,QAAO;AACpD,UAAM,QAAoC,CAAA;AAC1C,UAAM,QAAQ,OAAO,CAAC,IAAI,IAAI,CAAA;AAI9B,eAAW,cAAc,MAAM,aAAa;AAC1C,UAAI,WAAW,OAAO,SAAS;AAC7B,cAAM,IAAI,MAAM,0DAA0D;AAC5E,iBAAW,QAAQ,WAAW,WAAW,CAAA,GAAI;AAC3C,YAAI,KAAK,SAAS,OAAQ,OAAM,KAAK,KAAK,IAAI;AAAA,iBACrC,KAAK,SAAS;AACrB,gBAAM,KAAK,EAAE,MAAM,QAAQ,WAAW,WAAW,KAAK,KAAK,OAAO;AAAA,iBAC3D,KAAK,SAAS;AACrB,gBAAM,KAAK;AAAA,YACT,MAAM;AAAA,YACN,WAAW,KAAK;AAAA,YAChB,KAAK,KAAK;AAAA,YACV,GAAI,KAAK,YAAY,EAAE,UAAU,KAAK,SAAA;AAAA,UAAS,CAChD;AAAA,YACE,OAAM,IAAI,MAAM,wCAAwC;AAAA,MAC/D;AAAA,IACF;AACA,eAAW,UAAU;AACrB,QAAI;AACF,YAAM,WAAW,IAAI,QAAA,EAAU,WAAW;AAC1C,YAAM,aAAa,MACjB,MAAM,UAAU,6BAA6B,IAAI,SAAS,WAAW,MAAM,IAAI;AACjF,YAAM,SAAS,WAAA;AACf,UAAI,QAAQ;AACV,cAAM,oBAAmB,YAAO,2BAAP;AACzB,YAAI;AACF,gBAAM,UAAU,MAAM,4BAA4B;AAAA,YAChD;AAAA,YACA;AAAA,YACA;AAAA,UAAA,CACD;AACD,cAAI,CAAC,MAAM,WAAW,CAAC,SAAS,SAAA,EAAW,WAAW;AACpD,gBAAI,iBAAkB,cAAO,mCAAP,gCAAwC;AAC9D,mBAAO;AAAA,UACT;AACA,cAAI,SAAS;AACX,iBAAK,QAAQ;AAAA,cACX,OAAO;AAAA,gBACL;AAAA,kBACE,GAAG;AAAA,kBACH,SAAS;AAAA,oBACP,GAAG,QAAQ;AAAA,oBACX,OAAO;AAAA,sBACL,GAAG;AAAA,sBACH,GAAI,MAAM,SACN,CAAC,EAAE,MAAM,QAAiB,MAAM,MAAM,KAAK,MAAM,EAAA,CAAG,IACpD,CAAA;AAAA,oBAAC;AAAA,kBACP;AAAA,gBACF;AAAA,gBAEF,qBAAqB,SAAY,SAAY,EAAE,iBAAA;AAAA,cAAiB;AAAA,YAClE,EACA,MAAM,MAAM,MAAS;AACvB,qBAAS,OAAA;AACT,mBAAO;AAAA,UACT;AACA,cAAI,oBAAoB,OAAO,kBAAkB;AAC/C,mBAAO,iBAAiB,UAAU,gBAAgB;AAClD,mBAAO;AAAA,UACT;AAAA,QACF,SAAS,OAAO;AACd,cAAI,iBAAkB,cAAO,mCAAP,gCAAwC;AAC9D,gBAAM;AAAA,QACR;AAAA,MACF;AACA,eAAS,KAAA;AACT,aAAO;AAAA,IACT,UAAA;AACE,iBAAW,UAAU;AAAA,IACvB;AAAA,EACF,GAAG,CAAC,GAAG,CAAC;AACV;"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
3
- const version = "0.22.0";
3
+ const version = "0.22.2";
4
4
  const packageJson = {
5
5
  version
6
6
  };
@@ -1,4 +1,4 @@
1
- const version = "0.22.0";
1
+ const version = "0.22.2";
2
2
  const packageJson = {
3
3
  version
4
4
  };