@athenaintel/react 0.23.2 → 0.23.3

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.
Files changed (87) hide show
  1. package/README.md +40 -1
  2. package/dist/chat/AthenaChat.cjs +129 -29
  3. package/dist/chat/AthenaChat.cjs.map +1 -1
  4. package/dist/chat/AthenaChat.d.ts +32 -1
  5. package/dist/chat/AthenaChat.js +130 -30
  6. package/dist/chat/AthenaChat.js.map +1 -1
  7. package/dist/chat/athena-composer-controls.js +1 -1
  8. package/dist/chat/statewire-client-tools.cjs +20 -0
  9. package/dist/chat/statewire-client-tools.cjs.map +1 -1
  10. package/dist/chat/statewire-client-tools.d.ts +27 -0
  11. package/dist/chat/statewire-client-tools.js +20 -0
  12. package/dist/chat/statewire-client-tools.js.map +1 -1
  13. package/dist/chat/use-athena-run-live.cjs.map +1 -1
  14. package/dist/chat/use-athena-run-live.d.ts +7 -0
  15. package/dist/chat/use-athena-run-live.js.map +1 -1
  16. package/dist/chat-ui/composer/tiptap-composer.cjs +16 -1
  17. package/dist/chat-ui/composer/tiptap-composer.cjs.map +1 -1
  18. package/dist/chat-ui/composer/tiptap-composer.js +16 -1
  19. package/dist/chat-ui/composer/tiptap-composer.js.map +1 -1
  20. package/dist/composer/composer-handoff.cjs +121 -0
  21. package/dist/composer/composer-handoff.cjs.map +1 -0
  22. package/dist/composer/composer-handoff.d.ts +89 -0
  23. package/dist/composer/composer-handoff.js +121 -0
  24. package/dist/composer/composer-handoff.js.map +1 -0
  25. package/dist/composer/index.d.ts +2 -0
  26. package/dist/composer/useAppendToComposer.cjs +19 -7
  27. package/dist/composer/useAppendToComposer.cjs.map +1 -1
  28. package/dist/composer/useAppendToComposer.d.ts +22 -4
  29. package/dist/composer/useAppendToComposer.js +20 -8
  30. package/dist/composer/useAppendToComposer.js.map +1 -1
  31. package/dist/composer/useSendMessage.cjs +26 -8
  32. package/dist/composer/useSendMessage.cjs.map +1 -1
  33. package/dist/composer/useSendMessage.d.ts +8 -0
  34. package/dist/composer/useSendMessage.js +27 -9
  35. package/dist/composer/useSendMessage.js.map +1 -1
  36. package/dist/diagnostics/athena-diagnostics.cjs.map +1 -1
  37. package/dist/diagnostics/athena-diagnostics.d.ts +1 -1
  38. package/dist/diagnostics/athena-diagnostics.js.map +1 -1
  39. package/dist/diagnostics/errors.cjs +2 -0
  40. package/dist/diagnostics/errors.cjs.map +1 -1
  41. package/dist/diagnostics/errors.d.ts +2 -0
  42. package/dist/diagnostics/errors.js +2 -0
  43. package/dist/diagnostics/errors.js.map +1 -1
  44. package/dist/dictation/DictationStrip.cjs +60 -0
  45. package/dist/dictation/DictationStrip.cjs.map +1 -0
  46. package/dist/dictation/DictationStrip.d.ts +25 -0
  47. package/dist/dictation/DictationStrip.js +60 -0
  48. package/dist/dictation/DictationStrip.js.map +1 -0
  49. package/dist/dictation/audio-level-history.cjs +68 -0
  50. package/dist/dictation/audio-level-history.cjs.map +1 -0
  51. package/dist/dictation/audio-level-history.d.ts +27 -0
  52. package/dist/dictation/audio-level-history.js +68 -0
  53. package/dist/dictation/audio-level-history.js.map +1 -0
  54. package/dist/dictation/dictation-controls.cjs +60 -0
  55. package/dist/dictation/dictation-controls.cjs.map +1 -0
  56. package/dist/dictation/dictation-controls.d.ts +15 -0
  57. package/dist/dictation/dictation-controls.js +60 -0
  58. package/dist/dictation/dictation-controls.js.map +1 -0
  59. package/dist/dictation/dictation-engine.cjs +128 -0
  60. package/dist/dictation/dictation-engine.cjs.map +1 -0
  61. package/dist/dictation/dictation-engine.d.ts +87 -0
  62. package/dist/dictation/dictation-engine.js +128 -0
  63. package/dist/dictation/dictation-engine.js.map +1 -0
  64. package/dist/dictation/dictation-state.cjs +68 -0
  65. package/dist/dictation/dictation-state.cjs.map +1 -0
  66. package/dist/dictation/dictation-state.d.ts +58 -0
  67. package/dist/dictation/dictation-state.js +68 -0
  68. package/dist/dictation/dictation-state.js.map +1 -0
  69. package/dist/dictation/use-dictation.cjs +204 -0
  70. package/dist/dictation/use-dictation.cjs.map +1 -0
  71. package/dist/dictation/use-dictation.d.ts +47 -0
  72. package/dist/dictation/use-dictation.js +204 -0
  73. package/dist/dictation/use-dictation.js.map +1 -0
  74. package/dist/index.cjs +13 -0
  75. package/dist/index.cjs.map +1 -1
  76. package/dist/index.d.ts +11 -0
  77. package/dist/index.js +13 -0
  78. package/dist/index.js.map +1 -1
  79. package/dist/packages/react/package.json.cjs +1 -1
  80. package/dist/packages/react/package.json.js +1 -1
  81. package/dist/provider/AthenaProvider.cjs +42 -3
  82. package/dist/provider/AthenaProvider.cjs.map +1 -1
  83. package/dist/provider/AthenaProvider.d.ts +1 -1
  84. package/dist/provider/AthenaProvider.js +43 -4
  85. package/dist/provider/AthenaProvider.js.map +1 -1
  86. package/dist/styles.css +1 -1
  87. package/package.json +1 -1
package/README.md CHANGED
@@ -459,6 +459,13 @@ Both use the same bearer / API key as session creation. The full recipe
459
459
  template skill `voice-session-guide.md`. `createAthenaVoiceSession` is also
460
460
  exported if you hold the bridge state yourself.
461
461
 
462
+ ## What's new in 0.23.3
463
+
464
+ **Dictation in the composer.** `<AthenaChat>` now has a microphone left of Send. Click it and the editor row becomes a live waveform; Stop puts the spoken words into the draft for review, Send sends them (queued while a run is live). The default engine is the browser's Web Speech API (Chrome, Edge, Safari) — no backend, no extra dependency; the mic hides itself where the engine is missing (Firefox). Pass `dictation={false}` to remove it, or `dictation={{ lang, engine }}` to set the language or plug in another speech engine (`DictationEngine` interface, `useDictation()` hook, `DictationStrip` component are exported for custom composers).
465
+
466
+ **Composer slots.** New `composer` prop on `<AthenaChat>`: `toolbarStart` renders after the attach button, `toolbarEnd` before the mic and Send, and `onBeforeSend` runs host bookkeeping before every submit (dictated sends included). Slot components render inside the chat's thread scope, so `useAthenaRunLive()` works in them; the template's model-picker portal is no longer needed.
467
+
468
+ **Frontend tools bind reliably.** Two fixes to the `frontendTools` run config: (1) tools with an `execute()` no longer also ride `enabled_tools` — doing so switched on any backend toolkit owning a tool of the same name, and the backend won the collision, so the client tool was silently dropped (a frontend `notify_user` ran the backend notification cascade instead); (2) declaring executable frontend tools without a recognised `appId` now emits an `sdk.config.warning` error through diagnostics and `onError`, because the deep-agent runtime drops every client tool for an unknown `app_id` without telling the client.
462
469
  ## What's new in 0.23.2
463
470
 
464
471
  - Assistant turns no longer draw the grey vertical rule down their left edge. `AthenaChat` renders the lead column only when the host passes an `AvatarComponent`; without one the reply sits flush with the thread column and is told apart from the right-aligned user bubbles by alignment alone.
@@ -1205,8 +1212,40 @@ Dedicated tool UIs built on `ToolCard` follow the provider automatically; `useTo
1205
1212
  ## Composer Hooks
1206
1213
 
1207
1214
  Use `useAppendToComposer()` when you want to prefill a draft without sending it.
1215
+ Pass `{ focus: true }` to put the caret after the text once the editor shows it,
1216
+ so the user can finish the draft by typing or dictating (an agenda card that
1217
+ prefills "Here is what happened in this meeting:", for example):
1218
+
1219
+ ```tsx
1220
+ import { useAppendToComposer } from '@athenaintel/react';
1221
+
1222
+ function MeetingCard({ block }: { block: string }) {
1223
+ const append = useAppendToComposer();
1224
+ return (
1225
+ <button onClick={() => append(block, { replace: true, focus: true })}>
1226
+ Brief the agent
1227
+ </button>
1228
+ );
1229
+ }
1230
+ ```
1208
1231
 
1209
- Use `useSendMessage()` for workflow buttons, sidebar shortcuts, and other UI that lives outside `<AthenaChat>` but still needs to submit a prompt.
1232
+ Both hooks are safe to call at any time. A write made before the chat is
1233
+ ready — with `enableThreadList`, while the list is still resolving the active
1234
+ thread, or while the thread's history is still loading — waits in the provider
1235
+ and lands once the composer is ready, instead of being lost with the mount it
1236
+ was written into (the provider keys its statewire subtree by thread). Readiness
1237
+ is not the socket: a never-started chat sits in `standby` until its first send
1238
+ and still takes a prefill. Early writes keep their order (two early sends both
1239
+ go out, first then second; adjacent plain appends fold into one draft), and
1240
+ each is bound to the thread it was made for: if the user switches threads, the
1241
+ identity changes, or the provider unmounts before it lands, it is dropped —
1242
+ `useSendMessage` rejects with an `AthenaSdkError` (`composer_write_dropped`)
1243
+ and a `composer.handoff` diagnostic says why — never written into another
1244
+ thread. `useComposerReady()` exposes the readiness for UI that wants to show a
1245
+ "loading" state; `composer.handoff` records each deferred, applied and dropped
1246
+ write.
1247
+
1248
+ Use `useSendMessage()` for workflow buttons, sidebar shortcuts, and other UI that lives outside `<AthenaChat>` but still needs to submit a prompt. Its promise settles once the send has been dispatched, including a send that waited for the connection.
1210
1249
 
1211
1250
  ```tsx
1212
1251
  import { useSendMessage } from '@athenaintel/react';
@@ -39,6 +39,12 @@ const chatThreadShell = require("../chat-ui/thread/chat-thread-shell.cjs");
39
39
  const statewireHistoryEdge = require("../chat-ui/thread/statewire-history-edge.cjs");
40
40
  const threadLoadingOverlay = require("../chat-ui/thread/thread-loading-overlay.cjs");
41
41
  const composerSendOrStop = require("../chat-ui/thread/composer-send-or-stop.cjs");
42
+ const composerAction = require("../chat-ui/composer/composer-action.cjs");
43
+ const athenaDiagnostics = require("../diagnostics/athena-diagnostics.cjs");
44
+ const useDictation = require("../dictation/use-dictation.cjs");
45
+ const DictationStrip = require("../dictation/DictationStrip.cjs");
46
+ const dictationControls = require("../dictation/dictation-controls.cjs");
47
+ const dictationState = require("../dictation/dictation-state.cjs");
42
48
  const tiptapComposer = require("../chat-ui/composer/tiptap-composer.cjs");
43
49
  const apiClient = require("../chat-ui/mentions/suggestions/data/api-client.cjs");
44
50
  const messageComponents = require("../chat-ui/messages/message-components.cjs");
@@ -144,7 +150,9 @@ const AthenaChat = ({
144
150
  mentionTools,
145
151
  welcomeSuggestions = athenaChatWelcome.DEFAULT_WELCOME_SUGGESTIONS,
146
152
  components,
147
- groupToolCalls = true
153
+ groupToolCalls = true,
154
+ composer: composerSlots,
155
+ dictation = true
148
156
  }) => {
149
157
  var _a, _b, _c;
150
158
  const athenaConfig = AthenaContext.useAthenaConfig();
@@ -252,6 +260,8 @@ const AthenaChat = ({
252
260
  AthenaChatComposer,
253
261
  {
254
262
  tools,
263
+ slots: composerSlots,
264
+ dictation,
255
265
  welcomeSuggestionCount: welcomeSuggestions.length
256
266
  }
257
267
  ) })
@@ -310,7 +320,7 @@ const ATHENA_CHAT_MENTION_CATEGORIES = [
310
320
  "projects",
311
321
  "folders"
312
322
  ];
313
- const AthenaChatComposer = ({ tools, welcomeSuggestionCount }) => {
323
+ const AthenaChatComposer = ({ tools, slots, dictation, welcomeSuggestionCount }) => {
314
324
  const aui = react.useAui();
315
325
  const threadId = threadIdContext.useAthenaStatewireThreadId();
316
326
  const { appUrl } = AthenaContext.useAthenaConfig();
@@ -319,6 +329,10 @@ const AthenaChatComposer = ({ tools, welcomeSuggestionCount }) => {
319
329
  const { attachFiles } = useAttachFiles.useAttachFiles();
320
330
  const composerRef = react$1.useRef(null);
321
331
  const [hasText, setHasText] = react$1.useState(false);
332
+ const isRunning = react.useAuiState((state) => {
333
+ var _a;
334
+ return ((_a = state.optional.thread) == null ? void 0 : _a.isRunning) ?? false;
335
+ });
322
336
  const [dragActive, setDragActive] = react$1.useState(false);
323
337
  const keyboardHint = composerKeyboardHint.useComposerKeyboardHint();
324
338
  const reducedMotion = usePrefersReducedMotion.usePrefersReducedMotion();
@@ -328,6 +342,28 @@ const AthenaChatComposer = ({ tools, welcomeSuggestionCount }) => {
328
342
  athenaChatWelcome.welcomeComposerDelayMs(welcomeSuggestionCount),
329
343
  reducedMotion || !mountedEmpty
330
344
  );
345
+ const dictationEnabled = dictation !== false;
346
+ const dictationOptions = typeof dictation === "object" ? dictation : void 0;
347
+ const appendSpokenText = react$1.useCallback(
348
+ (spoken) => {
349
+ if (!spoken) return;
350
+ aui.composer.setText(dictationState.composeDictationDraft(aui.composer.getState().text ?? "", spoken));
351
+ },
352
+ [aui]
353
+ );
354
+ const dictationCtl = useDictation.useDictation({
355
+ engine: dictationOptions == null ? void 0 : dictationOptions.engine,
356
+ lang: dictationOptions == null ? void 0 : dictationOptions.lang,
357
+ // A recognizer that gives up on its own (silence window) still hands
358
+ // over what it heard instead of dropping the words with the session.
359
+ onAutoEnd: appendSpokenText
360
+ });
361
+ const recording = dictationEnabled && dictationCtl.isActive;
362
+ const showMic = dictationEnabled && dictationCtl.isSupported;
363
+ const { isActive: dictationActive, cancel: cancelDictation } = dictationCtl;
364
+ react$1.useEffect(() => {
365
+ if (!dictationEnabled && dictationActive) cancelDictation();
366
+ }, [cancelDictation, dictationActive, dictationEnabled]);
331
367
  const rootCategories = react$1.useMemo(() => {
332
368
  const categories = [...ATHENA_CHAT_MENTION_CATEGORIES];
333
369
  if (tools.some((tool) => tool.type === "toolkit")) categories.push("toolkits");
@@ -383,6 +419,36 @@ const AthenaChatComposer = ({ tools, welcomeSuggestionCount }) => {
383
419
  }),
384
420
  [attachFiles]
385
421
  );
422
+ const stopDictation = react$1.useCallback(async () => {
423
+ appendSpokenText(await dictationCtl.stop());
424
+ }, [appendSpokenText, dictationCtl]);
425
+ const sendDictation = react$1.useCallback(async () => {
426
+ var _a;
427
+ const spoken = await dictationCtl.stop();
428
+ const text = dictationState.composeDictationDraft(aui.composer.getState().text ?? "", spoken);
429
+ const extras = attachmentsRef.current.length > 0 || quoteRef.current !== null;
430
+ if (!text && !extras) return;
431
+ aui.composer.setText(text);
432
+ const action = composerAction.resolveComposerAction({ hasDraft: true, isRunning });
433
+ if (action === "stop") return;
434
+ try {
435
+ await ((_a = slots == null ? void 0 : slots.onBeforeSend) == null ? void 0 : _a.call(slots, { action }));
436
+ } catch (error) {
437
+ athenaDiagnostics.athenaDiagnostics.emit("composer.dictation", {
438
+ level: "warn",
439
+ data: {
440
+ phase: "before_send_rejected",
441
+ error: error instanceof Error ? error.message : String(error)
442
+ }
443
+ });
444
+ return;
445
+ }
446
+ if (extras) {
447
+ sendComposed(text);
448
+ return;
449
+ }
450
+ aui.composer.send({ steer: false });
451
+ }, [aui, dictationCtl, isRunning, sendComposed, slots]);
386
452
  return /* @__PURE__ */ jsxRuntime.jsx(ComposerDropZone.ComposerDropZone, { onDragActiveChange: setDragActive, children: /* @__PURE__ */ jsxRuntime.jsx(
387
453
  "div",
388
454
  {
@@ -399,23 +465,34 @@ const AthenaChatComposer = ({ tools, welcomeSuggestionCount }) => {
399
465
  /* @__PURE__ */ jsxRuntime.jsx(ComposerQuotePreview.ComposerQuotePreview, {}),
400
466
  /* @__PURE__ */ jsxRuntime.jsx(ComposerAttachmentPreview.ComposerAttachmentPreview, {})
401
467
  ] }),
402
- editor: /* @__PURE__ */ jsxRuntime.jsx(
403
- tiptapComposer.TiptapComposer,
404
- {
405
- ref: composerRef,
406
- threadId,
407
- appearance: "paper",
408
- placeholder: "Type @ to tag an asset",
409
- tools,
410
- rootCategories,
411
- onDraftStateChange: setHasText,
412
- waitForHostUploads,
413
- interceptSubmit,
414
- fileIntake,
415
- onSubmitted: keyboardHint.onSubmitted
416
- }
417
- ),
418
- toolbarStart: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
468
+ editor: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
469
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: cnfast.cn(recording && "hidden"), children: /* @__PURE__ */ jsxRuntime.jsx(
470
+ tiptapComposer.TiptapComposer,
471
+ {
472
+ ref: composerRef,
473
+ threadId,
474
+ appearance: "paper",
475
+ placeholder: "Type @ to tag an asset",
476
+ tools,
477
+ rootCategories,
478
+ onDraftStateChange: setHasText,
479
+ waitForHostUploads,
480
+ interceptSubmit,
481
+ fileIntake,
482
+ onSubmitted: keyboardHint.onSubmitted
483
+ }
484
+ ) }),
485
+ recording && /* @__PURE__ */ jsxRuntime.jsx(
486
+ DictationStrip.DictationStrip,
487
+ {
488
+ status: dictationCtl.status,
489
+ transcript: dictationCtl.transcript,
490
+ stream: dictationCtl.stream
491
+ }
492
+ ),
493
+ dictationEnabled && dictationCtl.status === dictationState.DICTATION_STATUSES.error && dictationCtl.error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "aui-dictation-error px-1.5 pb-1 text-destructive text-xs", role: "alert", children: dictationCtl.error.message })
494
+ ] }),
495
+ toolbarStart: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1", children: recording ? /* @__PURE__ */ jsxRuntime.jsx(dictationControls.DictationCancelButton, { onClick: dictationCtl.cancel }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
419
496
  /* @__PURE__ */ jsxRuntime.jsx(FileUploadButton.FileUploadButton, { className: "aui-composer-attach size-8 rounded-full p-0 disabled:opacity-30" }),
420
497
  /* @__PURE__ */ jsxRuntime.jsx(
421
498
  athenaComposerControls.AthenaComposerMentionButton,
@@ -425,17 +502,40 @@ const AthenaChatComposer = ({ tools, welcomeSuggestionCount }) => {
425
502
  return (_b = (_a = composerRef.current) == null ? void 0 : _a.openMentionMenu) == null ? void 0 : _b.call(_a);
426
503
  }
427
504
  }
505
+ ),
506
+ slots == null ? void 0 : slots.toolbarStart
507
+ ] }) }),
508
+ toolbarEnd: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1", children: recording ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
509
+ /* @__PURE__ */ jsxRuntime.jsx(
510
+ dictationControls.DictationStopButton,
511
+ {
512
+ onClick: () => void stopDictation(),
513
+ disabled: dictationCtl.status === dictationState.DICTATION_STATUSES.stopping
514
+ }
515
+ ),
516
+ /* @__PURE__ */ jsxRuntime.jsx(
517
+ athenaComposerControls.AthenaComposerSendButton,
518
+ {
519
+ action: isRunning ? "queue" : "send",
520
+ onSend: () => void sendDictation(),
521
+ disabled: dictationCtl.status !== dictationState.DICTATION_STATUSES.listening || isUploading,
522
+ title: isUploading ? "Upload in progress..." : void 0
523
+ }
428
524
  )
429
- ] }),
430
- toolbarEnd: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1", children: /* @__PURE__ */ jsxRuntime.jsx(
431
- composerSendOrStop.ComposerSendOrStop,
432
- {
433
- composerRef,
434
- hasDraft: hasText || hasExtras,
435
- sendBlockedReason: isUploading ? "Upload in progress..." : null,
436
- components: athenaComposerControls.ATHENA_COMPOSER_COMPONENTS
437
- }
438
- ) }),
525
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
526
+ slots == null ? void 0 : slots.toolbarEnd,
527
+ showMic && /* @__PURE__ */ jsxRuntime.jsx(dictationControls.DictationMicButton, { onClick: dictationCtl.start, disabled: isUploading }),
528
+ /* @__PURE__ */ jsxRuntime.jsx(
529
+ composerSendOrStop.ComposerSendOrStop,
530
+ {
531
+ composerRef,
532
+ hasDraft: hasText || hasExtras,
533
+ sendBlockedReason: isUploading ? "Upload in progress..." : null,
534
+ onBeforeSend: slots == null ? void 0 : slots.onBeforeSend,
535
+ components: athenaComposerControls.ATHENA_COMPOSER_COMPONENTS
536
+ }
537
+ )
538
+ ] }) }),
439
539
  belowSurface: /* @__PURE__ */ jsxRuntime.jsx(composerKeyboardHint.ComposerKeyboardHint, { visible: keyboardHint.visible })
440
540
  }
441
541
  )