@athenaintel/react 0.41.2 → 0.42.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +108 -9
- package/dist/agent-runtime/src/asset-attachment.d.ts +98 -0
- package/dist/agent-runtime/src/athena-asset-drag.d.ts +32 -0
- package/dist/agent-runtime/src/auth-denial.d.ts +29 -0
- package/dist/agent-runtime/src/chat-outbox-attachments.cjs +41 -0
- package/dist/agent-runtime/src/chat-outbox-attachments.cjs.map +1 -0
- package/dist/agent-runtime/src/chat-outbox-attachments.d.ts +38 -0
- package/dist/agent-runtime/src/chat-outbox-attachments.js +41 -0
- package/dist/agent-runtime/src/chat-outbox-attachments.js.map +1 -0
- package/dist/agent-runtime/src/chat-outbox-signals.cjs +63 -0
- package/dist/agent-runtime/src/chat-outbox-signals.cjs.map +1 -0
- package/dist/agent-runtime/src/chat-outbox-signals.d.ts +12 -0
- package/dist/agent-runtime/src/chat-outbox-signals.js +63 -0
- package/dist/agent-runtime/src/chat-outbox-signals.js.map +1 -0
- package/dist/agent-runtime/src/chat-outbox-store.cjs +509 -0
- package/dist/agent-runtime/src/chat-outbox-store.cjs.map +1 -0
- package/dist/agent-runtime/src/chat-outbox-store.d.ts +203 -0
- package/dist/agent-runtime/src/chat-outbox-store.js +509 -0
- package/dist/agent-runtime/src/chat-outbox-store.js.map +1 -0
- package/dist/agent-runtime/src/chat-outbox-thread.cjs +136 -0
- package/dist/agent-runtime/src/chat-outbox-thread.cjs.map +1 -0
- package/dist/agent-runtime/src/chat-outbox-thread.d.ts +118 -0
- package/dist/agent-runtime/src/chat-outbox-thread.js +136 -0
- package/dist/agent-runtime/src/chat-outbox-thread.js.map +1 -0
- package/dist/agent-runtime/src/chat-outbox.cjs +696 -0
- package/dist/agent-runtime/src/chat-outbox.cjs.map +1 -0
- package/dist/agent-runtime/src/chat-outbox.d.ts +285 -0
- package/dist/agent-runtime/src/chat-outbox.js +696 -0
- package/dist/agent-runtime/src/chat-outbox.js.map +1 -0
- package/dist/agent-runtime/src/chat-socket-reachability.d.ts +45 -0
- package/dist/agent-runtime/src/client-tool-bridge.d.ts +152 -0
- package/dist/agent-runtime/src/client-tools.d.ts +133 -0
- package/dist/agent-runtime/src/composer-markdown.cjs +10 -0
- package/dist/agent-runtime/src/composer-markdown.cjs.map +1 -1
- package/dist/agent-runtime/src/composer-markdown.d.ts +108 -0
- package/dist/agent-runtime/src/composer-markdown.js +10 -0
- package/dist/agent-runtime/src/composer-markdown.js.map +1 -1
- package/dist/agent-runtime/src/config-mutations.d.ts +51 -0
- package/dist/agent-runtime/src/conversation-index.d.ts +110 -0
- package/dist/agent-runtime/src/conversation-list.d.ts +343 -0
- package/dist/agent-runtime/src/deep-agent-transport-recipe.cjs +3 -2
- package/dist/agent-runtime/src/deep-agent-transport-recipe.cjs.map +1 -1
- package/dist/agent-runtime/src/deep-agent-transport-recipe.d.ts +166 -0
- package/dist/agent-runtime/src/deep-agent-transport-recipe.js +3 -2
- package/dist/agent-runtime/src/deep-agent-transport-recipe.js.map +1 -1
- package/dist/agent-runtime/src/desktop-handoff.d.ts +139 -0
- package/dist/agent-runtime/src/durable-thread-snapshot-cache.cjs +158 -0
- package/dist/agent-runtime/src/durable-thread-snapshot-cache.cjs.map +1 -0
- package/dist/agent-runtime/src/durable-thread-snapshot-cache.d.ts +60 -0
- package/dist/agent-runtime/src/durable-thread-snapshot-cache.js +158 -0
- package/dist/agent-runtime/src/durable-thread-snapshot-cache.js.map +1 -0
- package/dist/agent-runtime/src/edit-command.d.ts +156 -0
- package/dist/agent-runtime/src/errors.d.ts +174 -0
- package/dist/agent-runtime/src/execute-output.d.ts +31 -0
- package/dist/agent-runtime/src/generated/runconfig-contract.cjs +16 -2
- package/dist/agent-runtime/src/generated/runconfig-contract.cjs.map +1 -1
- package/dist/agent-runtime/src/generated/runconfig-contract.d.ts +202 -0
- package/dist/agent-runtime/src/generated/runconfig-contract.js +16 -2
- package/dist/agent-runtime/src/generated/runconfig-contract.js.map +1 -1
- package/dist/agent-runtime/src/github-wake-event.d.ts +77 -0
- package/dist/agent-runtime/src/index.d.ts +74 -0
- package/dist/agent-runtime/src/interrupts.cjs +17 -0
- package/dist/agent-runtime/src/interrupts.cjs.map +1 -1
- package/dist/agent-runtime/src/interrupts.d.ts +348 -0
- package/dist/agent-runtime/src/interrupts.js +17 -0
- package/dist/agent-runtime/src/interrupts.js.map +1 -1
- package/dist/agent-runtime/src/lane-follow.d.ts +121 -0
- package/dist/agent-runtime/src/lane-history.cjs +209 -0
- package/dist/agent-runtime/src/lane-history.cjs.map +1 -0
- package/dist/agent-runtime/src/lane-history.d.ts +175 -0
- package/dist/agent-runtime/src/lane-history.js +209 -0
- package/dist/agent-runtime/src/lane-history.js.map +1 -0
- package/dist/agent-runtime/src/lane-images.d.ts +184 -0
- package/dist/agent-runtime/src/message-author.d.ts +43 -0
- package/dist/agent-runtime/src/message-text.cjs +15 -0
- package/dist/agent-runtime/src/message-text.cjs.map +1 -0
- package/dist/agent-runtime/src/message-text.d.ts +47 -0
- package/dist/agent-runtime/src/message-text.js +15 -0
- package/dist/agent-runtime/src/message-text.js.map +1 -0
- package/dist/agent-runtime/src/message-window.cjs +15 -1
- package/dist/agent-runtime/src/message-window.cjs.map +1 -1
- package/dist/agent-runtime/src/message-window.d.ts +159 -0
- package/dist/agent-runtime/src/message-window.js +16 -2
- package/dist/agent-runtime/src/message-window.js.map +1 -1
- package/dist/agent-runtime/src/messages.cjs +88 -4
- package/dist/agent-runtime/src/messages.cjs.map +1 -1
- package/dist/agent-runtime/src/messages.d.ts +603 -0
- package/dist/agent-runtime/src/messages.js +88 -4
- package/dist/agent-runtime/src/messages.js.map +1 -1
- package/dist/agent-runtime/src/model-context.d.ts +22 -0
- package/dist/agent-runtime/src/offline-status.d.ts +157 -0
- package/dist/agent-runtime/src/optimistic-content.d.ts +21 -0
- package/dist/agent-runtime/src/protocol.cjs +12 -4
- package/dist/agent-runtime/src/protocol.cjs.map +1 -1
- package/dist/agent-runtime/src/protocol.d.ts +119 -0
- package/dist/agent-runtime/src/protocol.js +13 -5
- package/dist/agent-runtime/src/protocol.js.map +1 -1
- package/dist/agent-runtime/src/ptc-subcalls.d.ts +51 -0
- package/dist/agent-runtime/src/queue.d.ts +138 -0
- package/dist/agent-runtime/src/quota-safe-local-storage-session.d.ts +154 -0
- package/dist/agent-runtime/src/running-tool-placeholder.cjs +11 -0
- package/dist/agent-runtime/src/running-tool-placeholder.cjs.map +1 -0
- package/dist/agent-runtime/src/running-tool-placeholder.d.ts +25 -0
- package/dist/agent-runtime/src/running-tool-placeholder.js +11 -0
- package/dist/agent-runtime/src/running-tool-placeholder.js.map +1 -0
- package/dist/agent-runtime/src/runtime-messages.d.ts +104 -0
- package/dist/agent-runtime/src/screen-point.d.ts +151 -0
- package/dist/agent-runtime/src/sealed-idb.cjs +112 -0
- package/dist/agent-runtime/src/sealed-idb.cjs.map +1 -0
- package/dist/agent-runtime/src/sealed-idb.d.ts +106 -0
- package/dist/agent-runtime/src/sealed-idb.js +112 -0
- package/dist/agent-runtime/src/sealed-idb.js.map +1 -0
- package/dist/agent-runtime/src/send-recovery.d.ts +40 -0
- package/dist/agent-runtime/src/statewire-attach-observer.d.ts +194 -0
- package/dist/agent-runtime/src/statewire-diagnostics.d.ts +74 -0
- package/dist/agent-runtime/src/statewire-state.d.ts +29 -0
- package/dist/agent-runtime/src/statewire-suspended.cjs.map +1 -1
- package/dist/agent-runtime/src/statewire-suspended.d.ts +27 -0
- package/dist/agent-runtime/src/statewire-suspended.js.map +1 -1
- package/dist/agent-runtime/src/statewire-transport.d.ts +40 -0
- package/dist/agent-runtime/src/streaming-markdown.d.ts +46 -0
- package/dist/agent-runtime/src/subagent-transcripts.cjs +209 -0
- package/dist/agent-runtime/src/subagent-transcripts.cjs.map +1 -0
- package/dist/agent-runtime/src/subagent-transcripts.d.ts +71 -0
- package/dist/agent-runtime/src/subagent-transcripts.js +209 -0
- package/dist/agent-runtime/src/subagent-transcripts.js.map +1 -0
- package/dist/agent-runtime/src/subagents.cjs +253 -0
- package/dist/agent-runtime/src/subagents.cjs.map +1 -0
- package/dist/agent-runtime/src/subagents.d.ts +311 -0
- package/dist/agent-runtime/src/subagents.js +253 -0
- package/dist/agent-runtime/src/subagents.js.map +1 -0
- package/dist/agent-runtime/src/subscription-wake-event.d.ts +111 -0
- package/dist/agent-runtime/src/thread-extras.d.ts +43 -0
- package/dist/agent-runtime/src/thread-handoff.d.ts +364 -0
- package/dist/agent-runtime/src/thread-prewarm-controller.d.ts +76 -0
- package/dist/agent-runtime/src/thread-prewarm.d.ts +118 -0
- package/dist/agent-runtime/src/thread-snapshot-cache.d.ts +99 -0
- package/dist/agent-runtime/src/thread-snapshot-mirror.cjs +329 -0
- package/dist/agent-runtime/src/thread-snapshot-mirror.cjs.map +1 -0
- package/dist/agent-runtime/src/thread-snapshot-mirror.d.ts +101 -0
- package/dist/agent-runtime/src/thread-snapshot-mirror.js +329 -0
- package/dist/agent-runtime/src/thread-snapshot-mirror.js.map +1 -0
- package/dist/agent-runtime/src/thread-status.d.ts +53 -0
- package/dist/agent-runtime/src/tool-metadata.d.ts +146 -0
- package/dist/agent-runtime/src/tool-names.cjs +67 -0
- package/dist/agent-runtime/src/tool-names.cjs.map +1 -1
- package/dist/agent-runtime/src/tool-names.d.ts +60 -0
- package/dist/agent-runtime/src/tool-names.js +68 -1
- package/dist/agent-runtime/src/tool-names.js.map +1 -1
- package/dist/agent-runtime/src/tool-outputs.d.ts +35 -0
- package/dist/agent-runtime/src/tool-result.cjs +1 -0
- package/dist/agent-runtime/src/tool-result.cjs.map +1 -1
- package/dist/agent-runtime/src/tool-result.d.ts +126 -0
- package/dist/agent-runtime/src/tool-result.js +1 -0
- package/dist/agent-runtime/src/tool-result.js.map +1 -1
- package/dist/agent-runtime/src/types.d.ts +159 -0
- package/dist/agent-runtime/src/use-conversation-list.d.ts +76 -0
- package/dist/agent-runtime/src/use-deep-agent-chat-outbox.cjs +136 -0
- package/dist/agent-runtime/src/use-deep-agent-chat-outbox.cjs.map +1 -0
- package/dist/agent-runtime/src/use-deep-agent-chat-outbox.d.ts +64 -0
- package/dist/agent-runtime/src/use-deep-agent-chat-outbox.js +136 -0
- package/dist/agent-runtime/src/use-deep-agent-chat-outbox.js.map +1 -0
- package/dist/agent-runtime/src/use-deep-agent-config.d.ts +169 -0
- package/dist/agent-runtime/src/use-deep-agent-edit-submit.cjs +13 -0
- package/dist/agent-runtime/src/use-deep-agent-edit-submit.cjs.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-edit-submit.d.ts +22 -0
- package/dist/agent-runtime/src/use-deep-agent-edit-submit.js +13 -0
- package/dist/agent-runtime/src/use-deep-agent-edit-submit.js.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-runtime.cjs +20 -2
- package/dist/agent-runtime/src/use-deep-agent-runtime.cjs.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-runtime.d.ts +168 -0
- package/dist/agent-runtime/src/use-deep-agent-runtime.js +20 -2
- package/dist/agent-runtime/src/use-deep-agent-runtime.js.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-thread.cjs +12 -4
- package/dist/agent-runtime/src/use-deep-agent-thread.cjs.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-thread.d.ts +320 -0
- package/dist/agent-runtime/src/use-deep-agent-thread.js +12 -4
- package/dist/agent-runtime/src/use-deep-agent-thread.js.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-voice.cjs +999 -0
- package/dist/agent-runtime/src/use-deep-agent-voice.cjs.map +1 -0
- package/dist/agent-runtime/src/use-deep-agent-voice.d.ts +409 -0
- package/dist/agent-runtime/src/use-deep-agent-voice.js +999 -0
- package/dist/agent-runtime/src/use-deep-agent-voice.js.map +1 -0
- package/dist/agent-runtime/src/use-deep-agent-window-sync.d.ts +102 -0
- package/dist/agent-runtime/src/use-historical-edit-attachments.d.ts +2 -0
- package/dist/agent-runtime/src/use-thread-handoff-flow.d.ts +84 -0
- package/dist/agent-runtime/src/voice-presentation.d.ts +80 -0
- package/dist/agent-runtime/src/voice-turn-grouping.d.ts +28 -0
- package/dist/agent-runtime/src/voice-wire-types.d.ts +15 -0
- package/dist/agent-runtime/src/withdrawn-turns.d.ts +97 -0
- package/dist/chat/AthenaChat.cjs +77 -6
- package/dist/chat/AthenaChat.cjs.map +1 -1
- package/dist/chat/AthenaChat.js +78 -7
- package/dist/chat/AthenaChat.js.map +1 -1
- package/dist/chat/StatewireApprovalCard.d.ts +1 -1
- package/dist/chat/StatewireClientToolBridge.d.ts +1 -1
- package/dist/chat/StatewireQueuedMessages.cjs +11 -4
- package/dist/chat/StatewireQueuedMessages.cjs.map +1 -1
- package/dist/chat/StatewireQueuedMessages.d.ts +1 -1
- package/dist/chat/StatewireQueuedMessages.js +11 -4
- package/dist/chat/StatewireQueuedMessages.js.map +1 -1
- package/dist/chat/chat-outbox-rows.cjs +223 -0
- package/dist/chat/chat-outbox-rows.cjs.map +1 -0
- package/dist/chat/chat-outbox-rows.d.ts +29 -0
- package/dist/chat/chat-outbox-rows.js +223 -0
- package/dist/chat/chat-outbox-rows.js.map +1 -0
- package/dist/chat/statewire-approval.d.ts +1 -1
- package/dist/chat/statewire-client-tools.d.ts +1 -1
- package/dist/chat/super-grouping/SuperGroupingCard.cjs +3 -1
- package/dist/chat/super-grouping/SuperGroupingCard.cjs.map +1 -1
- package/dist/chat/super-grouping/SuperGroupingCard.d.ts +7 -1
- package/dist/chat/super-grouping/SuperGroupingCard.js +3 -1
- package/dist/chat/super-grouping/SuperGroupingCard.js.map +1 -1
- package/dist/chat/super-grouping/SuperGroupingTranscript.cjs +7 -2
- package/dist/chat/super-grouping/SuperGroupingTranscript.cjs.map +1 -1
- package/dist/chat/super-grouping/SuperGroupingTranscript.d.ts +2 -2
- package/dist/chat/super-grouping/SuperGroupingTranscript.js +7 -2
- package/dist/chat/super-grouping/SuperGroupingTranscript.js.map +1 -1
- package/dist/chat/super-grouping/activity-timeline.cjs +19 -11
- package/dist/chat/super-grouping/activity-timeline.cjs.map +1 -1
- package/dist/chat/super-grouping/activity-timeline.d.ts +25 -9
- package/dist/chat/super-grouping/activity-timeline.js +19 -11
- package/dist/chat/super-grouping/activity-timeline.js.map +1 -1
- package/dist/chat/use-statewire-approval-request.cjs +36 -0
- package/dist/chat/use-statewire-approval-request.cjs.map +1 -1
- package/dist/chat/use-statewire-approval-request.d.ts +49 -1
- package/dist/chat/use-statewire-approval-request.js +39 -3
- package/dist/chat/use-statewire-approval-request.js.map +1 -1
- package/dist/chat-ui/composer/tiptap-composer.cjs +102 -3
- package/dist/chat-ui/composer/tiptap-composer.cjs.map +1 -1
- package/dist/chat-ui/composer/tiptap-composer.d.ts +32 -1
- package/dist/chat-ui/composer/tiptap-composer.js +103 -4
- package/dist/chat-ui/composer/tiptap-composer.js.map +1 -1
- package/dist/chat-ui/header/conversation-history-panel.d.ts +1 -1
- package/dist/chat-ui/index.d.ts +1 -1
- package/dist/chat-ui/messages/message-components.cjs +15 -0
- package/dist/chat-ui/messages/message-components.cjs.map +1 -1
- package/dist/chat-ui/messages/message-components.js +15 -0
- package/dist/chat-ui/messages/message-components.js.map +1 -1
- package/dist/chat-ui/messages/runtime-message.d.ts +1 -1
- package/dist/chat-ui/messages/statewire-continue-run.d.ts +1 -1
- package/dist/chat-ui/messages/user-edit-composer.cjs +15 -2
- package/dist/chat-ui/messages/user-edit-composer.cjs.map +1 -1
- package/dist/chat-ui/messages/user-edit-composer.d.ts +4 -2
- package/dist/chat-ui/messages/user-edit-composer.js +15 -2
- package/dist/chat-ui/messages/user-edit-composer.js.map +1 -1
- package/dist/chat-ui/messages/wake-marker.d.ts +1 -1
- package/dist/chat-ui/rich-text/chat-text-schema.cjs +1 -0
- package/dist/chat-ui/rich-text/chat-text-schema.cjs.map +1 -1
- package/dist/chat-ui/rich-text/chat-text-schema.d.ts +3 -0
- package/dist/chat-ui/rich-text/chat-text-schema.js +1 -0
- package/dist/chat-ui/rich-text/chat-text-schema.js.map +1 -1
- package/dist/chat-ui/statewire.cjs +1 -0
- package/dist/chat-ui/statewire.cjs.map +1 -1
- package/dist/chat-ui/statewire.d.ts +2 -2
- package/dist/chat-ui/statewire.js +2 -1
- package/dist/chat-ui/thread/statewire-history-edge.d.ts +1 -1
- package/dist/composer/AttachmentContext.cjs +8 -4
- package/dist/composer/AttachmentContext.cjs.map +1 -1
- package/dist/composer/AttachmentContext.d.ts +7 -0
- package/dist/composer/AttachmentContext.js +8 -4
- package/dist/composer/AttachmentContext.js.map +1 -1
- package/dist/composer/build-composed-message.cjs +34 -0
- package/dist/composer/build-composed-message.cjs.map +1 -1
- package/dist/composer/build-composed-message.d.ts +20 -0
- package/dist/composer/build-composed-message.js +35 -1
- package/dist/composer/build-composed-message.js.map +1 -1
- package/dist/composer/composer-handoff.cjs +39 -2
- package/dist/composer/composer-handoff.cjs.map +1 -1
- package/dist/composer/composer-handoff.d.ts +28 -3
- package/dist/composer/composer-handoff.js +39 -2
- package/dist/composer/composer-handoff.js.map +1 -1
- package/dist/composer/useAttachFiles.cjs +15 -2
- package/dist/composer/useAttachFiles.cjs.map +1 -1
- package/dist/composer/useAttachFiles.d.ts +11 -0
- package/dist/composer/useAttachFiles.js +15 -2
- package/dist/composer/useAttachFiles.js.map +1 -1
- package/dist/composer/useSendMessage.cjs.map +1 -1
- package/dist/composer/useSendMessage.d.ts +4 -3
- package/dist/composer/useSendMessage.js.map +1 -1
- package/dist/diagnostics/athena-diagnostics.cjs.map +1 -1
- package/dist/diagnostics/athena-diagnostics.d.ts +3 -1
- package/dist/diagnostics/athena-diagnostics.js.map +1 -1
- package/dist/index.cjs +18 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +15 -6
- package/dist/index.js +19 -1
- package/dist/index.js.map +1 -1
- package/dist/offline/chat-outbox.cjs +57 -0
- package/dist/offline/chat-outbox.cjs.map +1 -0
- package/dist/offline/chat-outbox.d.ts +80 -0
- package/dist/offline/chat-outbox.js +57 -0
- package/dist/offline/chat-outbox.js.map +1 -0
- package/dist/offline/offline-history.cjs +77 -0
- package/dist/offline/offline-history.cjs.map +1 -0
- package/dist/offline/offline-history.d.ts +60 -0
- package/dist/offline/offline-history.js +77 -0
- package/dist/offline/offline-history.js.map +1 -0
- package/dist/provider/AthenaProvider.cjs +206 -9
- package/dist/provider/AthenaProvider.cjs.map +1 -1
- package/dist/provider/AthenaProvider.d.ts +22 -1
- package/dist/provider/AthenaProvider.js +207 -10
- package/dist/provider/AthenaProvider.js.map +1 -1
- package/dist/provider/statewire-window-context.d.ts +1 -1
- package/dist/react/package.json.cjs +1 -1
- package/dist/react/package.json.js +1 -1
- package/dist/references/src/anchors/file-line-range.d.ts +58 -0
- package/dist/references/src/anchors/format.d.ts +21 -0
- package/dist/references/src/anchors/message-anchors.d.ts +71 -0
- package/dist/references/src/anchors/parse.d.ts +8 -0
- package/dist/references/src/anchors/registry-contract.d.ts +24 -0
- package/dist/references/src/anchors/registry.d.ts +19 -0
- package/dist/references/src/anchors/schemas.d.ts +194 -0
- package/dist/references/src/anchors/serialize.d.ts +8 -0
- package/dist/references/src/anchors/time.d.ts +78 -0
- package/dist/references/src/anchors/types.d.ts +474 -0
- package/dist/references/src/asset-bounds.d.ts +27 -0
- package/dist/references/src/asset-links.d.ts +23 -0
- package/dist/references/src/asset-reference.d.ts +16 -0
- package/dist/references/src/formats/format-catalog.d.ts +79 -0
- package/dist/references/src/formats/markdown/parse-reference-markdown.d.ts +28 -0
- package/dist/references/src/formats/markdown/reference-display-name.d.ts +6 -0
- package/dist/references/src/formats/object/asset-object.d.ts +9 -0
- package/dist/references/src/formats/object/callable-object.d.ts +6 -0
- package/dist/references/src/formats/object/decode-reference-object-payload.d.ts +3 -0
- package/dist/references/src/formats/object/external-url-object.d.ts +9 -0
- package/dist/references/src/formats/object/group-object.d.ts +9 -0
- package/dist/references/src/formats/object/navigation-object.d.ts +9 -0
- package/dist/references/src/formats/object/parse-reference-object.d.ts +11 -0
- package/dist/references/src/formats/object/person-object.d.ts +9 -0
- package/dist/references/src/formats/uri/asset-mention-uri.d.ts +9 -0
- package/dist/references/src/formats/uri/asset-spaces-url.d.ts +9 -0
- package/dist/references/src/formats/uri/callable-mention-uris.d.ts +9 -0
- package/dist/references/src/formats/uri/detect-uri-wire.d.ts +9 -0
- package/dist/references/src/formats/uri/group-mention-uri.d.ts +12 -0
- package/dist/references/src/formats/uri/navigation-spaces-url.d.ts +14 -0
- package/dist/references/src/formats/uri/parse-reference-uri.d.ts +20 -0
- package/dist/references/src/formats/uri/person-mention-uri.d.ts +9 -0
- package/dist/references/src/formats/uri/template-placeholder.d.ts +12 -0
- package/dist/references/src/index.d.ts +47 -0
- package/dist/references/src/legacy/index.d.ts +14 -0
- package/dist/references/src/legacy/pre-reference-asset-url.d.ts +57 -0
- package/dist/references/src/legacy/pre-reference-citation-bridge.d.ts +22 -0
- package/dist/references/src/legacy/pre-reference-citation.d.ts +138 -0
- package/dist/references/src/legacy/pre-reference-external-url.d.ts +31 -0
- package/dist/references/src/legacy/pre-reference-format.d.ts +8 -0
- package/dist/references/src/legacy/pre-reference-mention.d.ts +27 -0
- package/dist/references/src/legacy/pre-reference-object.d.ts +8 -0
- package/dist/references/src/legacy/pre-reference-uri.d.ts +12 -0
- package/dist/references/src/legacy.d.ts +2 -0
- package/dist/references/src/mention-href-patterns.d.ts +19 -0
- package/dist/references/src/mention-node/index.d.ts +5 -0
- package/dist/references/src/mention-node/mention-node.d.ts +31 -0
- package/dist/references/src/mention-node/resolve-node-attrs.d.ts +27 -0
- package/dist/references/src/parse-reference.d.ts +23 -0
- package/dist/references/src/schemas.d.ts +463 -0
- package/dist/references/src/serialize-reference.d.ts +50 -0
- package/dist/references/src/sheet-name.d.ts +10 -0
- package/dist/references/src/tool-option-schemas.d.ts +597 -0
- package/dist/references/src/types.d.ts +125 -0
- package/dist/references/src/version-ref.d.ts +30 -0
- package/dist/runtime/statewire-lifecycle.d.ts +1 -1
- package/dist/runtime/statewire-model-lane.d.ts +1 -1
- package/dist/runtime/statewire-server-lane-context.d.ts +1 -1
- package/dist/runtime/statewire-session-key.cjs +7 -0
- package/dist/runtime/statewire-session-key.cjs.map +1 -1
- package/dist/runtime/statewire-session-key.d.ts +16 -0
- package/dist/runtime/statewire-session-key.js +8 -1
- package/dist/runtime/statewire-session-key.js.map +1 -1
- package/dist/runtime/statewire-session-lane.cjs +48 -0
- package/dist/runtime/statewire-session-lane.cjs.map +1 -0
- package/dist/runtime/statewire-session-lane.d.ts +29 -0
- package/dist/runtime/statewire-session-lane.js +48 -0
- package/dist/runtime/statewire-session-lane.js.map +1 -0
- package/dist/runtime/statewire-thread-config-lane.d.ts +1 -1
- package/dist/runtime/useAthenaStatewireRuntime.cjs +16 -5
- package/dist/runtime/useAthenaStatewireRuntime.cjs.map +1 -1
- package/dist/runtime/useAthenaStatewireRuntime.d.ts +15 -1
- package/dist/runtime/useAthenaStatewireRuntime.js +17 -6
- package/dist/runtime/useAthenaStatewireRuntime.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/threads/api.d.ts +1 -1
- package/dist/threads/conversation-list-fetcher.d.ts +1 -1
- package/dist/threads/index-triggered-refresh.cjs +164 -0
- package/dist/threads/index-triggered-refresh.cjs.map +1 -0
- package/dist/threads/index-triggered-refresh.d.ts +70 -0
- package/dist/threads/index-triggered-refresh.js +164 -0
- package/dist/threads/index-triggered-refresh.js.map +1 -0
- package/dist/threads/offline-session-list.cjs +75 -0
- package/dist/threads/offline-session-list.cjs.map +1 -0
- package/dist/threads/offline-session-list.d.ts +27 -0
- package/dist/threads/offline-session-list.js +75 -0
- package/dist/threads/offline-session-list.js.map +1 -0
- package/dist/threads/pull-request-display.d.ts +1 -1
- package/dist/threads/session-list-model.d.ts +1 -1
- package/dist/threads/session-list-tab-share.cjs +150 -0
- package/dist/threads/session-list-tab-share.cjs.map +1 -0
- package/dist/threads/session-list-tab-share.d.ts +73 -0
- package/dist/threads/session-list-tab-share.js +150 -0
- package/dist/threads/session-list-tab-share.js.map +1 -0
- package/dist/threads/session-row-parts.d.ts +1 -1
- package/dist/threads/sessions-query.d.ts +1 -1
- package/dist/threads/statewire-thread-list.cjs +105 -20
- package/dist/threads/statewire-thread-list.cjs.map +1 -1
- package/dist/threads/statewire-thread-list.d.ts +31 -3
- package/dist/threads/statewire-thread-list.js +107 -22
- package/dist/threads/statewire-thread-list.js.map +1 -1
- package/dist/threads/thread-prefetcher.d.ts +1 -1
- package/dist/threads/use-athena-session-attention.d.ts +1 -1
- package/dist/threads/use-athena-sessions.cjs +32 -22
- package/dist/threads/use-athena-sessions.cjs.map +1 -1
- package/dist/threads/use-athena-sessions.js +33 -23
- package/dist/threads/use-athena-sessions.js.map +1 -1
- package/dist/threads/use-rest-conversation-history.d.ts +1 -1
- package/dist/threads/use-statewire-conversation-history.d.ts +5 -5
- package/dist/tiptap-markdown-table/src/markdown-table-serializer.d.ts +56 -0
- package/dist/tools/NestedPtcMessages.cjs +14 -9
- package/dist/tools/NestedPtcMessages.cjs.map +1 -1
- package/dist/tools/NestedPtcMessages.d.ts +9 -0
- package/dist/tools/NestedPtcMessages.js +6 -1
- package/dist/tools/NestedPtcMessages.js.map +1 -1
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/primitives.d.ts +1 -1
- package/dist/tools/subagent-task-tool-ui.cjs +494 -0
- package/dist/tools/subagent-task-tool-ui.cjs.map +1 -0
- package/dist/tools/subagent-task-tool-ui.d.ts +109 -0
- package/dist/tools/subagent-task-tool-ui.js +494 -0
- package/dist/tools/subagent-task-tool-ui.js.map +1 -0
- package/dist/tools/tool-fallback.d.ts +1 -1
- package/dist/tools/tool-icons.d.ts +1 -1
- package/dist/tools/tool-summary.d.ts +1 -1
- package/dist/tools/tool-uis.cjs +4 -0
- package/dist/tools/tool-uis.cjs.map +1 -1
- package/dist/tools/tool-uis.js +4 -0
- package/dist/tools/tool-uis.js.map +1 -1
- package/dist/voice/chat.cjs +6 -0
- package/dist/voice/chat.cjs.map +1 -0
- package/dist/voice/chat.d.ts +2 -0
- package/dist/voice/chat.js +6 -0
- package/dist/voice/chat.js.map +1 -0
- package/dist/voice/livekit-join.cjs +119 -0
- package/dist/voice/livekit-join.cjs.map +1 -0
- package/dist/voice/livekit-join.d.ts +8 -0
- package/dist/voice/livekit-join.js +119 -0
- package/dist/voice/livekit-join.js.map +1 -0
- package/dist/voice/livekit.cjs +32 -0
- package/dist/voice/livekit.cjs.map +1 -0
- package/dist/voice/livekit.d.ts +80 -0
- package/dist/voice/livekit.js +32 -0
- package/dist/voice/livekit.js.map +1 -0
- package/package.json +20 -10
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-deep-agent-voice.js","sources":["../../../../agent-runtime/src/use-deep-agent-voice.ts"],"sourcesContent":["import { useStatewireSendCommand, useStatewireState } from '@assistant-ui/react-statewire';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { statewireRejectionPayload } from './errors';\nimport { isDeepAgentStateHydrated, readDeepAgentStreamValues } from './statewire-state';\nimport type { DeepAgentVoiceWireError } from './voice-wire-types';\n\ndeclare module '@assistant-ui/core' {\n namespace Assistant {\n interface Commands {\n /**\n * Opens a voice session (`DeepAgentThread.voice_start`). `offer` is the\n * WebRTC SDP offer, {@link VOICE_WEBSOCKET_OFFER} to request the\n * WebSocket audio transport (the answer is then a relay ticket), or\n * {@link VOICE_LIVEKIT_OFFER} to request a LiveKit room (the answer is\n * then the room's join credentials). `model` picks the realtime model\n * (`gpt-live-1`, `gemini-3.8-live`, …); omitted → the host's default.\n */\n voiceStart: {\n type: 'voice/start';\n offer: string;\n model?: string;\n };\n /** Ends the live voice session (`DeepAgentThread.voice_end`). */\n voiceEnd: {\n type: 'voice/end';\n };\n /**\n * No-op whose INGRESS refreshes the caller's server-stored context\n * (context syncs ride command sends), so runConfig changes made while a\n * voice session is live reach the delegated runs.\n */\n voiceProfileSync: {\n type: 'voice/profile-sync';\n };\n /**\n * Mute/unmute the live session's microphone at the protocol level\n * (`DeepAgentThread.voice_set_mic` → `session.input_audio.mute`/`unmute`).\n */\n voiceSetMic: {\n type: 'voice/set-mic';\n paused: boolean;\n };\n /**\n * Route a typed message into the live conversation\n * (`DeepAgentThread.voice_user_text`): commits durably as a\n * `source: \"text\"` user turn and reaches the live model as speakable\n * context, so it responds by voice exactly as it would to speech.\n * `messageId` is client-minted so a durable-command redelivery after a\n * host crash commits idempotently instead of duplicating the turn.\n */\n voiceUserText: {\n type: 'voice/user-text';\n text: string;\n messageId: string;\n };\n /**\n * Tell the live session what the user is looking at\n * (`DeepAgentThread.voice_context`): one short description of the open\n * asset or page, appended as silent commentary so the live model can\n * resolve \"this document\" without delegating. Never spoken, never\n * persisted; identical consecutive descriptions are dropped server-side.\n */\n voiceContext: {\n type: 'voice/context';\n text: string;\n };\n }\n }\n}\n\n/** A voice-delegated frontend tool call projected by legacy hosts\n * (`arguments` is a JSON string). Current hosts run frontend tools through\n * the delegated run's client-tool interrupts, so this stays empty. */\nexport interface DeepAgentVoiceToolCall {\n callId: string;\n name: string;\n arguments: string;\n}\n\n/** The host's `state.voice` projection while a session exists. */\nexport interface DeepAgentVoiceWireState {\n clientId: string;\n status: 'connecting' | 'live';\n toolCalls: DeepAgentVoiceToolCall[];\n /** The delegated voice task currently running, if any. */\n activeTask?: { taskId: string; text: string } | null;\n}\n\nexport type DeepAgentVoicePhase = 'idle' | 'connecting' | 'live' | 'error';\n\n/**\n * Why a voice session failed, classified where the failure happened rather\n * than by parsing the message afterwards. `server_rejected` carries the\n * host's structured code (`voice-active`, `voice-not-enabled`,\n * `voice-unavailable`, `checkpointer_unavailable`, …); `media_lost` carries the\n * transport state that ended the session. Every surface reports and words the\n * same kinds, so telemetry across Olympus, Chrome, and mobile joins on them.\n */\nexport type DeepAgentVoiceFailureKind =\n | 'unsupported'\n | 'audio_session'\n | 'microphone_denied'\n | 'capture_failed'\n | 'offer_failed'\n | 'answer_timeout'\n | 'server_rejected'\n | 'command_failed'\n | 'no_answer'\n | 'answer_failed'\n | 'relay_failed'\n | 'room_failed'\n | 'playback_failed'\n | 'media_lost'\n | 'unknown';\n\n/** Where in the session's lifecycle a failure happened. */\nexport type DeepAgentVoiceFailureStage =\n | 'preflight'\n | 'audio-session'\n | 'capture'\n | 'offer'\n | 'negotiate'\n | 'answer'\n | 'relay'\n | 'room'\n | 'media'\n | 'playback';\n\nexport interface DeepAgentVoiceFailure {\n kind: DeepAgentVoiceFailureKind;\n stage: DeepAgentVoiceFailureStage;\n /** The message surfaces render (the same value as `error`). */\n message: string;\n /** The host's rejection reason / structured `detail.code`, when it sent one. */\n code?: string;\n /** The rejection's HTTP-style status, when the host sent a structured one. */\n status?: number;\n /** The statewire send fate when the command settled without a verdict. */\n fate?: string;\n /**\n * The lower-level reason: the media state that ended the session\n * (`failed` / `closed` / `disconnected` / `relay-closed` / `relay-error`), or\n * the channel death that settled the command (`unmount` / `stream-gone` /\n * `session-lost`).\n */\n reason?: string;\n}\n\n/**\n * Lifecycle milestones of a session attempt, for surfaces that record them\n * (crash breadcrumbs, latency telemetry). `attempt` groups one start's\n * events; `sinceStartMs` is measured from that attempt's `start-requested`.\n */\nexport type DeepAgentVoiceDiagnosticEvent =\n | 'start-requested'\n | 'audio-session-ready'\n | 'microphone-captured'\n | 'offer-ready'\n | 'ice-gathering-timeout'\n | 'answer-received'\n | 'room-joined'\n | 'live'\n | 'media-state'\n | 'media-loss'\n | 'auto-restart'\n | 'server-ended'\n | 'end'\n | 'error'\n | 'stale-attempt';\n\nexport interface DeepAgentVoiceDiagnostic {\n event: DeepAgentVoiceDiagnosticEvent;\n attempt: number;\n /** Epoch ms. */\n at: number;\n sinceStartMs: number;\n data?: Record<string, string | number | boolean | null>;\n}\n\n/** The statewire send failure a rejected/failed command carries (`error.failure`). */\ninterface VoiceCommandSendFailure {\n fate: string;\n cause?: string;\n code?: string;\n message?: string;\n payload?: unknown;\n}\n\nconst readSendFailure = (error: unknown): VoiceCommandSendFailure | null => {\n const failure = (error as { failure?: unknown } | null | undefined)?.failure;\n if (failure === null || typeof failure !== 'object') return null;\n return typeof (failure as { fate?: unknown }).fate === 'string'\n ? (failure as VoiceCommandSendFailure)\n : null;\n};\n\nconst errorMessage = (error: unknown): string =>\n error instanceof Error ? error.message : String(error);\n\n/** Marker for the local answer deadline, so the timeout classifies by identity. */\nclass VoiceAnswerTimeoutError extends Error {\n constructor() {\n super('The voice session did not answer in time');\n this.name = 'VoiceAnswerTimeoutError';\n }\n}\n\n/**\n * Classify a failed `voice/start` command. A host rejection keeps the host's\n * reason (`payload.reason` from the voice manager, `payload.detail.code` from\n * the command gate's structured envelope) and its own user-worded message; a\n * command the channel settled without a verdict reports its fate.\n */\nexport const classifyVoiceStartError = (\n error: unknown\n): Pick<DeepAgentVoiceFailure, 'kind' | 'message' | 'code' | 'status' | 'fate' | 'reason'> => {\n if (error instanceof VoiceAnswerTimeoutError) {\n return { kind: 'answer_timeout', message: error.message };\n }\n const failure = readSendFailure(error);\n if (failure === null) return { kind: 'unknown', message: errorMessage(error) };\n if (failure.fate === 'rejected') {\n const payload = failure.payload as\n | { reason?: unknown; status?: unknown; detail?: unknown }\n | null\n | undefined;\n const detail = payload?.detail as { code?: unknown; message?: unknown } | null | undefined;\n const reason = typeof payload?.reason === 'string' ? payload.reason : undefined;\n const detailCode = typeof detail?.code === 'string' ? detail.code : undefined;\n const detailMessage = typeof detail?.message === 'string' ? detail.message : undefined;\n const code = reason ?? detailCode ?? failure.code;\n const status = typeof payload?.status === 'number' ? payload.status : undefined;\n return {\n kind: 'server_rejected',\n message: failure.message ?? detailMessage ?? errorMessage(error),\n ...(code !== undefined && { code }),\n ...(status !== undefined && { status }),\n };\n }\n return {\n kind: 'command_failed',\n message: failure.message ?? errorMessage(error),\n fate: failure.fate,\n ...(failure.cause !== undefined && failure.cause !== 'server' && { reason: failure.cause }),\n };\n};\n\n/** Marker for a verdict that carried no SDP answer / relay ticket / room. */\nclass VoiceNoAnswerError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'VoiceNoAnswerError';\n }\n}\n\n/**\n * Decode a `voice/start` answer on the `livekit` transport. Throws (as a\n * `no_answer` failure when raised in the `answer` stage) on a missing prefix,\n * malformed JSON, or a missing/non-string field — a host that answered with\n * something else did not open a LiveKit room for this client.\n */\nexport const parseVoiceLiveKitAnswer = (answer: string): DeepAgentVoiceLiveKitAnswer => {\n if (!answer.startsWith(VOICE_LIVEKIT_ANSWER_PREFIX)) {\n throw new VoiceNoAnswerError('voice/start returned no voice room');\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(answer.slice(VOICE_LIVEKIT_ANSWER_PREFIX.length));\n } catch {\n throw new VoiceNoAnswerError('voice/start returned a malformed voice room');\n }\n const record = (parsed ?? null) as {\n serverUrl?: unknown;\n token?: unknown;\n roomName?: unknown;\n } | null;\n const serverUrl = record?.serverUrl;\n const token = record?.token;\n const roomName = record?.roomName;\n if (\n typeof serverUrl !== 'string' ||\n serverUrl === '' ||\n typeof token !== 'string' ||\n token === '' ||\n typeof roomName !== 'string' ||\n roomName === ''\n ) {\n throw new VoiceNoAnswerError('voice/start returned an incomplete voice room');\n }\n return { serverUrl, token, roomName };\n};\n\n/** The failure for an error thrown while `stage` was under way. */\nconst classifyStartFailure = (\n error: unknown,\n stage: DeepAgentVoiceFailureStage\n): DeepAgentVoiceFailure => {\n const message = errorMessage(error);\n switch (stage) {\n case 'preflight':\n return { kind: 'unsupported', stage, message };\n case 'audio-session':\n return { kind: 'audio_session', stage, message };\n case 'capture':\n return {\n kind: isMicrophoneDenied(error) ? 'microphone_denied' : 'capture_failed',\n stage,\n message,\n };\n case 'offer':\n return { kind: 'offer_failed', stage, message };\n case 'negotiate':\n return { ...classifyVoiceStartError(error), stage };\n case 'answer':\n return {\n kind: error instanceof VoiceNoAnswerError ? 'no_answer' : 'answer_failed',\n stage,\n message,\n };\n case 'relay':\n return { kind: 'relay_failed', stage, message };\n case 'room':\n // LiveKit captures the microphone while joining, so a permission\n // refusal surfaces here rather than in a `capture` stage.\n return {\n kind: isMicrophoneDenied(error) ? 'microphone_denied' : 'room_failed',\n stage,\n message,\n };\n default:\n return { kind: 'unknown', stage, message };\n }\n};\n\nconst isMicrophoneDenied = (error: unknown): boolean => {\n const name = (error as { name?: unknown } | null | undefined)?.name;\n if (\n name === 'SecurityError' ||\n name === 'NotAllowedError' ||\n name === 'PermissionDeniedError'\n ) {\n return true;\n }\n return /permission|denied|not allowed/i.test(errorMessage(error));\n};\n\n/**\n * User-facing copy for a failure, shared so every surface words the same\n * cause alike. Host rejections keep the host's own wording (it already names\n * the actionable cause); the local kinds get a sentence the user can act on.\n */\nexport const describeVoiceFailure = (failure: DeepAgentVoiceFailure): string => {\n switch (failure.kind) {\n case 'microphone_denied':\n return 'Microphone access is blocked. Allow the microphone for Athena and try again.';\n case 'unsupported':\n return 'Voice mode is not available in this environment.';\n case 'audio_session':\n return `The audio session could not start: ${failure.message}`;\n case 'answer_timeout':\n return 'The voice session did not answer in time. Check your connection and try again.';\n case 'room_failed':\n return 'The voice room could not be joined. Check your connection and try again.';\n case 'media_lost':\n return 'The voice connection was lost. Start voice again to continue.';\n case 'server_rejected':\n if (failure.code === 'voice-active') {\n return 'A voice session is already live on another device or tab. End it there first.';\n }\n if (failure.code === 'voice-not-enabled') {\n return 'Voice mode is not enabled for this workspace.';\n }\n return failure.message;\n default:\n return failure.message;\n }\n};\n\n/**\n * How the session's audio travels. `webrtc` (the default) negotiates a peer\n * connection straight to OpenAI; `websocket` captures PCM locally and streams\n * it to Agora's relay endpoint, for surfaces where WebRTC is unavailable\n * (Office add-in webviews, UDP-blocked networks) — web only: it needs\n * `AudioContext` and `WebSocket` globals. `livekit` joins the LiveKit room the\n * host mints for the session through a caller-supplied\n * {@link DeepAgentVoiceLiveKitAdapter}; Agora joins the same room as the agent\n * participant, so this is the only transport on which the realtime model is a\n * per-session choice (`gpt-live-1`, `gemini-3.8-live`, …) — the other two are\n * OpenAI-direct media legs and stay GPT Live only.\n */\nexport type DeepAgentVoiceTransport = 'webrtc' | 'websocket' | 'livekit';\n\n/**\n * The `voice/start` offer that requests the WebSocket transport. Mirrors\n * `agora.services.voice_relay.VOICE_WEBSOCKET_OFFER`.\n */\nexport const VOICE_WEBSOCKET_OFFER = 'athena-voice-transport:websocket/v1';\n/** Prefix of a `voice/start` answer that carries a relay ticket, not an SDP answer. */\nexport const VOICE_RELAY_ANSWER_PREFIX = 'athena-voice-relay:';\n/** The relay's wire format: raw PCM16, mono, 24 kHz (the Live API's PCM format). */\nexport const VOICE_RELAY_SAMPLE_RATE = 24_000;\n/**\n * The `voice/start` offer that requests the LiveKit transport. Mirrors\n * `agora.graphs.athena_deep_agent.voice_media.VOICE_LIVEKIT_OFFER`.\n */\nexport const VOICE_LIVEKIT_OFFER = 'athena-voice-transport:livekit/v1';\n/**\n * Prefix of a `voice/start` answer that carries LiveKit room credentials: the\n * rest is `JSON.stringify({ serverUrl, token, roomName })`. Mirrors\n * `agora.graphs.athena_deep_agent.voice_media.VOICE_LIVEKIT_ANSWER_PREFIX`.\n */\nexport const VOICE_LIVEKIT_ANSWER_PREFIX = 'athena-voice-livekit:';\n\n/** The room the host minted for the session, decoded from the `voice/start` answer. */\nexport interface DeepAgentVoiceLiveKitAnswer {\n /** LiveKit signalling URL, already `wss://`. */\n serverUrl: string;\n /** The browser participant's join token (`room_join`, publish, subscribe). */\n token: string;\n roomName: string;\n}\n\n/** A joined LiveKit room, as far as the session state machine drives it. */\nexport interface DeepAgentVoiceLiveKitSession {\n /** Publish/unpublish (or unmute/mute) the local microphone track. */\n setMicrophoneEnabled(enabled: boolean): Promise<void>;\n /** Leave the room and release the microphone. Idempotent. */\n disconnect(): Promise<void>;\n}\n\n/**\n * The LiveKit leg of the `livekit` transport, supplied by the surface so this\n * package never depends on `livekit-client` (web surfaces wrap `Room`; native\n * ones wrap the React Native SDK). Structural on purpose: tests inject fakes.\n */\nexport interface DeepAgentVoiceLiveKitAdapter {\n /**\n * Join the room, publish the microphone (respecting `micMuted`), play remote\n * audio; rejects on failure (a microphone-permission error keeps its DOM\n * `name`, so it classifies as `microphone_denied`). `onDisconnected` fires\n * once when the room drops AFTER a successful join — never for the session's\n * own `disconnect()`. An aborted `signal` must stop pending/joined media and\n * reject a pending join; the runtime also releases late results from older\n * adapters that do not implement cancellation.\n */\n connect(\n answer: DeepAgentVoiceLiveKitAnswer,\n handlers: {\n micMuted: boolean;\n /** Abort a pending join and release media when this attempt ends. */\n signal?: AbortSignal;\n onDisconnected: (reason: string) => void;\n }\n ): Promise<DeepAgentVoiceLiveKitSession>;\n}\n\n/** One realtime model the host offers for voice sessions (`state.voiceModelOptions`). */\nexport interface DeepAgentVoiceModelOption {\n id: string;\n label: string;\n provider: 'openai_live' | 'google';\n /** `false` when the deployment lacks the provider's credentials. */\n enabled: boolean;\n description: string;\n}\n\nconst VOICE_MODEL_PROVIDERS: ReadonlySet<string> = new Set(['openai_live', 'google']);\n\n/**\n * Validate the host's `state.voiceModelOptions`: `null` when absent or not a\n * list, otherwise the well-formed entries (a malformed entry is dropped, not\n * fatal — an older or newer host must never blank the whole picker).\n */\nexport const parseVoiceModelOptions = (value: unknown): DeepAgentVoiceModelOption[] | null => {\n if (!Array.isArray(value)) return null;\n const options: DeepAgentVoiceModelOption[] = [];\n for (const entry of value) {\n if (entry === null || typeof entry !== 'object') continue;\n const { id, label, provider, enabled, description } = entry as Record<string, unknown>;\n if (\n typeof id !== 'string' ||\n id === '' ||\n typeof label !== 'string' ||\n typeof provider !== 'string' ||\n !VOICE_MODEL_PROVIDERS.has(provider) ||\n typeof enabled !== 'boolean' ||\n typeof description !== 'string'\n ) {\n continue;\n }\n options.push({\n id,\n label,\n provider: provider as DeepAgentVoiceModelOption['provider'],\n enabled,\n description,\n });\n }\n return options;\n};\n\nconst sameVoiceModelOptions = (\n a: readonly DeepAgentVoiceModelOption[],\n b: readonly DeepAgentVoiceModelOption[]\n): boolean =>\n a.length === b.length &&\n a.every((option, index) => {\n const other = b[index];\n return (\n other !== undefined &&\n option.id === other.id &&\n option.label === other.label &&\n option.provider === other.provider &&\n option.enabled === other.enabled &&\n option.description === other.description\n );\n });\n\n// This package compiles without the DOM lib; the WebRTC / Web Audio /\n// WebSocket surfaces the hook touches are declared structurally and resolved\n// from `globalThis` (browser, extension page, or react-native-webrtc's\n// registerGlobals).\ninterface VoiceMediaStreamTrack {\n /** Standard WebRTC mute lever: a disabled track transmits silence. */\n enabled: boolean;\n stop(): void;\n}\n\nexport interface DeepAgentVoiceRemoteStream {\n getTracks(): VoiceMediaStreamTrack[];\n /** React Native WebRTC owns native stream/track entries beyond track.stop(). */\n release?(): void;\n}\n\nfunction releaseCaptureStream(stream: DeepAgentVoiceRemoteStream | null): void {\n if (!stream) return;\n releaseSafely(() => {\n for (const track of stream.getTracks()) releaseSafely(() => track.stop());\n });\n releaseSafely(() => stream.release?.());\n}\n\n/** One failed native/browser cleanup must not strand the remaining media. */\nfunction releaseSafely(release: () => unknown): void {\n try {\n void Promise.resolve(release()).catch(() => undefined);\n } catch {\n // Teardown is best-effort, including synchronous native bridge failures.\n }\n}\n\ninterface VoiceTrackEvent {\n track: VoiceMediaStreamTrack;\n streams: DeepAgentVoiceRemoteStream[];\n}\n\ntype VoiceConnectionStateEvent = 'connectionstatechange' | 'iceconnectionstatechange';\n\ninterface VoicePeerConnection {\n readonly iceGatheringState: string;\n /** Aggregate transport state (`connected`, `disconnected`, `failed`, `closed`). */\n readonly connectionState?: string;\n /** ICE-only state; the fallback where an implementation lacks `connectionState`. */\n readonly iceConnectionState?: string;\n readonly localDescription: { readonly sdp?: string } | null;\n addTrack(track: VoiceMediaStreamTrack, stream: DeepAgentVoiceRemoteStream): unknown;\n addEventListener(type: 'track', listener: (event: VoiceTrackEvent) => void): void;\n addEventListener(type: 'icegatheringstatechange', listener: () => void): void;\n addEventListener(type: VoiceConnectionStateEvent, listener: () => void): void;\n removeEventListener(type: 'icegatheringstatechange', listener: () => void): void;\n removeEventListener(type: 'track', listener: (event: VoiceTrackEvent) => void): void;\n removeEventListener(type: VoiceConnectionStateEvent, listener: () => void): void;\n createOffer(): Promise<{ sdp?: string }>;\n setLocalDescription(description: { sdp?: string }): Promise<void>;\n setRemoteDescription(description: { type: 'answer'; sdp: string }): Promise<void>;\n close(): void;\n}\n\ninterface VoiceWebSocket {\n binaryType: string;\n /** 0 connecting, 1 open, 2 closing, 3 closed. */\n readonly readyState: number;\n send(data: ArrayBufferLike): void;\n close(code?: number, reason?: string): void;\n addEventListener(type: 'open' | 'close' | 'error', listener: () => void): void;\n addEventListener(type: 'message', listener: (event: { data: unknown }) => void): void;\n}\n\ninterface VoiceAudioNode {\n connect(destination: VoiceAudioNode): unknown;\n disconnect(): void;\n}\n\ninterface VoiceAudioBuffer {\n readonly duration: number;\n copyToChannel(source: Float32Array, channel: number): void;\n}\n\ninterface VoiceBufferSource extends VoiceAudioNode {\n buffer: VoiceAudioBuffer | null;\n start(when?: number): void;\n}\n\ninterface VoiceScriptProcessor extends VoiceAudioNode {\n onaudioprocess:\n | ((event: { inputBuffer: { getChannelData(channel: number): Float32Array } }) => void)\n | null;\n}\n\ninterface VoiceGainNode extends VoiceAudioNode {\n gain: { value: number };\n}\n\ninterface VoiceAudioContext {\n readonly currentTime: number;\n readonly destination: VoiceAudioNode;\n createMediaStreamSource(stream: DeepAgentVoiceRemoteStream): VoiceAudioNode;\n createScriptProcessor(\n bufferSize: number,\n inputChannels: number,\n outputChannels: number\n ): VoiceScriptProcessor;\n createGain(): VoiceGainNode;\n createBuffer(channels: number, length: number, sampleRate: number): VoiceAudioBuffer;\n createBufferSource(): VoiceBufferSource;\n resume?(): Promise<void>;\n close(): Promise<void>;\n}\n\nconst voiceGlobals = globalThis as {\n RTCPeerConnection?: new () => VoicePeerConnection;\n MediaStream?: new (tracks: VoiceMediaStreamTrack[]) => DeepAgentVoiceRemoteStream;\n WebSocket?: new (url: string) => VoiceWebSocket;\n AudioContext?: new (options?: { sampleRate?: number }) => VoiceAudioContext;\n navigator?: {\n mediaDevices?: {\n getUserMedia(constraints: { audio: boolean }): Promise<DeepAgentVoiceRemoteStream>;\n };\n };\n};\n\n// Covers the host's own worst case: up to 30 s for the realtime provider's\n// session.started (`_START_TIMEOUT_SECONDS`, agora gpt_live_provider.py), then\n// the LiveKit room create and join. A tenant egress proxy in front of the\n// provider pushes a healthy start past 15 s; a shorter deadline abandons a\n// session the host is about to answer. scripts/voice-answer-timeout.test.ts\n// keeps the two in step.\nconst ANSWER_TIMEOUT_MS = 45_000;\nconst ICE_TIMEOUT_MS = 10_000;\nconst RELAY_OPEN_TIMEOUT_MS = 10_000;\n/** Capture frame for the WebSocket transport: ~43 ms of audio at 24 kHz. */\nconst RELAY_CAPTURE_FRAME_SAMPLES = 1024;\n/** Playback lead so a chunk scheduled \"now\" never lands in the past. */\nconst RELAY_PLAYBACK_LEAD_SECONDS = 0.02;\n/**\n * How long a `disconnected` transport may try to recover on its own before it\n * counts as lost. ICE routinely reports `disconnected` for a few seconds on a\n * network change and then reconnects; `failed` and `closed` are final.\n */\nconst MEDIA_DISCONNECT_GRACE_MS = 5_000;\n/**\n * Upper bound on waiting for the server to acknowledge `voice/end` before a\n * media-loss restart sends its `voice/start`: a start that races the old\n * session's teardown is rejected `voice-active`.\n */\nconst END_BEFORE_RESTART_TIMEOUT_MS = 3_000;\n\n/** Idempotency id for a typed-in-voice turn (uniqueness, not secrecy) —\n * platform-free on purpose: React Native lacks full webcrypto. */\nconst mintVoiceTextId = (): string =>\n `voicetext_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 12)}`;\n\n/** `m:ss` / `h:mm:ss` for the live-session timer strips (shared so the three\n * surfaces cannot drift on formatting). */\nexport const formatVoiceElapsed = (sinceMs: number, nowMs: number = Date.now()): string => {\n const total = Math.max(0, Math.floor((nowMs - sinceMs) / 1000));\n const hours = Math.floor(total / 3600);\n const minutes = Math.floor((total % 3600) / 60);\n const seconds = total % 60;\n const mmss = `${minutes}:${String(seconds).padStart(2, '0')}`;\n return hours > 0 ? `${hours}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}` : mmss;\n};\n\n/** Ticking elapsed-call string for a live session — the one timer\n * implementation all surfaces share (interval lifecycle + formatting).\n * Returns `null` when there is no live session. Callers isolate it in a\n * leaf component so only that leaf re-renders per second. */\nexport const useVoiceElapsed = (since: number | null): string | null => {\n const [now, setNow] = useState(() => Date.now());\n useEffect(() => {\n if (since === null) return;\n setNow(Date.now());\n const timer = setInterval(() => setNow(Date.now()), 1000);\n return () => clearInterval(timer);\n }, [since]);\n return since === null ? null : formatVoiceElapsed(since, now);\n};\n\nconst REQUEST_LABELS: Record<string, string> = {\n 'session.commentary.append': 'a result for the voice conversation',\n 'session.thinking.append': 'a context update',\n 'session.input_audio.mute': 'the mute request',\n 'session.input_audio.unmute': 'the unmute request',\n 'response.item.create': 'a tool result',\n};\n\n/**\n * One-line, user-facing rendering of a Live API rejection the host projected\n * (`state.voiceLastError`). Shared so every surface words the notice alike.\n */\nexport const describeVoiceWireError = (error: DeepAgentVoiceWireError): string => {\n const subject = error.requestType\n ? (REQUEST_LABELS[error.requestType] ?? `a ${error.requestType} event`)\n : 'a session event';\n return `The voice service rejected ${subject}: ${error.message}`;\n};\n\n/** The relay endpoint for a ticket: `wss://<agora>/api/voice/relay?ticket=…`. */\nexport const voiceRelayUrl = (relayUrl: string, ticket: string): string =>\n `${relayUrl.replace(/^http/, 'ws').replace(/\\/+$/, '')}/api/voice/relay?ticket=${encodeURIComponent(ticket)}`;\n\n/** Float32 samples in [-1, 1] → little-endian PCM16 (the relay wire format). */\nexport const pcm16FromFloat32 = (samples: Float32Array): Int16Array => {\n const pcm = new Int16Array(samples.length);\n for (let index = 0; index < samples.length; index += 1) {\n const clamped = Math.max(-1, Math.min(1, samples[index] ?? 0));\n pcm[index] = clamped < 0 ? Math.round(clamped * 0x8000) : Math.round(clamped * 0x7fff);\n }\n return pcm;\n};\n\n/** PCM16 → Float32 samples in [-1, 1] for Web Audio playback. */\nexport const float32FromPcm16 = (pcm: Int16Array): Float32Array => {\n const samples = new Float32Array(pcm.length);\n for (let index = 0; index < pcm.length; index += 1) {\n samples[index] = (pcm[index] ?? 0) / 0x8000;\n }\n return samples;\n};\n\nconst isRecoveredCommandAuthRejection = (error: unknown): boolean => {\n const payload = statewireRejectionPayload(error);\n if (payload?.status !== 503) return false;\n const detail = payload.detail as { code?: unknown; retryable?: unknown } | undefined;\n return detail?.code === 'command_auth_context_expired' && detail.retryable === true;\n};\n\n/**\n * Wait for ICE gathering, bounded. Gathering that has not completed by the\n * deadline is NOT a failure: the offer carries the candidates gathered so far\n * and the Live API's answer supplies the server side, so the connectivity\n * checks the client initiates still establish the media path (the API's own\n * reference client sends the offer without waiting at all). Mobile radios with\n * several interfaces routinely take long to declare gathering complete, and\n * failing the whole session on that was a needless hard stop.\n */\nconst waitForIce = (\n pc: VoicePeerConnection,\n timeoutMs: number\n): Promise<'complete' | 'timeout'> =>\n new Promise((resolve) => {\n if (pc.iceGatheringState === 'complete') {\n resolve('complete');\n return;\n }\n const timeout = setTimeout(() => {\n pc.removeEventListener('icegatheringstatechange', check);\n resolve('timeout');\n }, timeoutMs);\n const check = (): void => {\n if (pc.iceGatheringState !== 'complete') return;\n clearTimeout(timeout);\n pc.removeEventListener('icegatheringstatechange', check);\n resolve('complete');\n };\n pc.addEventListener('icegatheringstatechange', check);\n });\n\nconst waitForRelayOpen = (ws: VoiceWebSocket, timeoutMs: number): Promise<void> =>\n new Promise((resolve, reject) => {\n const timeout = setTimeout(\n () => reject(new Error('The voice relay did not open in time')),\n timeoutMs\n );\n ws.addEventListener('open', () => {\n clearTimeout(timeout);\n resolve();\n });\n ws.addEventListener('error', () => {\n clearTimeout(timeout);\n reject(new Error('The voice relay connection failed'));\n });\n ws.addEventListener('close', () => {\n clearTimeout(timeout);\n reject(new Error('The voice relay closed before it opened'));\n });\n });\n\nexport interface UseDeepAgentVoiceOptions {\n /**\n * Remote-audio sink: called with the remote stream when the session answers\n * and with `null` on teardown. Web surfaces attach it to an `<audio>`\n * element; react-native-webrtc plays remote audio natively, so native\n * surfaces omit it. A rejected promise fails the session. WebRTC only: the\n * WebSocket transport plays through its own `AudioContext`.\n */\n onRemoteStream?: (stream: DeepAgentVoiceRemoteStream | null) => void | Promise<void>;\n /**\n * Platform audio-session ownership for the whole call: `start` runs before\n * mic capture (a rejection fails the session), `stop` runs on every\n * teardown path. Native surfaces pass their call-audio module (on iOS the\n * non-mixable playAndRecord/voiceChat session that entitles the app to\n * keep a live call running); web surfaces omit it.\n */\n audioSession?: {\n start: () => Promise<void>;\n stop: () => Promise<void>;\n /** Synchronously mark an in-flight start for cleanup before a new owner starts. */\n cancelStart?: () => void;\n };\n /** Media transport; see {@link DeepAgentVoiceTransport}. Default `webrtc`. */\n transport?: DeepAgentVoiceTransport;\n /**\n * Agora's base URL (e.g. `https://api.athenaintel.com`) whose\n * `/api/voice/relay` endpoint carries the audio on the `websocket`\n * transport. Required for that transport, ignored otherwise.\n */\n relayUrl?: string;\n /**\n * The room leg for the `livekit` transport (required for it, ignored\n * otherwise); see {@link DeepAgentVoiceLiveKitAdapter}. Read through a\n * per-render ref, so a fresh adapter never restarts anything.\n */\n livekit?: DeepAgentVoiceLiveKitAdapter;\n /**\n * The realtime model to ask for, sent as `voice/start.model` — a value or a\n * getter resolved when each attempt (user start or automatic restart)\n * negotiates. `undefined`/empty omits the key and the host picks its\n * default. Only the `livekit` transport can honour a non-GPT-Live model.\n */\n model?: string | (() => string | undefined);\n /**\n * Lifecycle observer for the session attempts (negotiation milestones with\n * timings, media-state changes, losses, restarts, ends). Surfaces feed it to\n * their crash breadcrumbs / telemetry; read through a per-render ref, so a\n * fresh closure never restarts anything. Must not throw.\n */\n onDiagnostic?: (entry: DeepAgentVoiceDiagnostic) => void;\n /** Per-surface overrides of the negotiation deadlines (ms). */\n timeouts?: {\n /** How long to wait for ICE gathering to complete before sending the offer with the candidates so far. */\n iceGatheringMs?: number;\n /** How long `voice/start` may take to answer. */\n answerMs?: number;\n /** How long the relay WebSocket may take to open (`websocket` transport). */\n relayOpenMs?: number;\n };\n}\n\nexport interface UseDeepAgentVoiceReturn {\n phase: DeepAgentVoicePhase;\n /** The failure message while `phase === 'error'` (same as `failure.message`). */\n error: string | null;\n /**\n * The classified failure while `phase === 'error'`; `null` otherwise. Where\n * `error` is only words, this says WHICH step failed and, for a host\n * rejection, the host's reason code — the key telemetry and support join on.\n */\n failure: DeepAgentVoiceFailure | null;\n start: () => Promise<void>;\n end: () => void;\n /** The delegated voice task currently running server-side, if any. */\n activeTask: { taskId: string; text: string } | null;\n /** Epoch ms when the session went live; `null` outside a live session.\n * Surfaces render an elapsed-call timer from it. */\n liveSince: number | null;\n /**\n * Whether the local microphone is muted. Mute is distinct from `end`: the\n * session (and any delegated work) stays live while the local track is\n * disabled AND the server mutes protocol input (`session.input_audio.mute`\n * — muted speech is discarded server-side, the model keeps speaking).\n */\n micMuted: boolean;\n /** Mute/unmute the live session's microphone; a no-op unless live. */\n setMicMuted: (muted: boolean) => void;\n /**\n * Bimodal input: send typed text into the live conversation as if spoken.\n * Resolves `true` once the server accepted it; `false` when there is no\n * live session or the command was rejected — the caller falls back to a\n * normal run send so the message is never lost.\n */\n sendUserText: (text: string) => Promise<boolean>;\n /**\n * The delegated task that was still running when the last session ended.\n * The run itself continues on the thread — surfaces render a \"voice ended,\n * still working\" note from this until the run settles or a new session\n * starts, at which point it clears itself.\n */\n handoffTask: { taskId: string; text: string } | null;\n /**\n * Push the client's current synced context (runConfig included) to the\n * server so the NEXT delegated run uses it. Call after a runConfig change\n * while the session is live; a no-op otherwise.\n */\n syncProfile: () => void;\n /**\n * Non-fatal notice for the live session: the most recent Live API\n * rejection the host projected (a refused context update, a failed mute),\n * worded for the user. The session stays live; `null` when there is none\n * or the session is not live.\n */\n notice: string | null;\n /**\n * Tell the live session what the user is looking at (one short line, e.g.\n * `spreadsheet \"Q3 Budget\"`). Silent context for reference resolution;\n * fire-and-forget, a no-op unless live.\n */\n sendContext: (text: string) => void;\n /**\n * The realtime model the host resolved for the current/last session\n * (`state.voiceModel`, stamped when a session starts), or `null` before\n * any session or while the snapshot is not hydrated. Stale after an end is\n * inert — surfaces gate on `phase`, like `micMuted`.\n */\n model: string | null;\n /**\n * The realtime models this host offers (`state.voiceModelOptions`,\n * published at host boot), validated; `null` on an older host that does\n * not publish them — surfaces fall back to their static list.\n */\n modelOptions: DeepAgentVoiceModelOption[] | null;\n}\n\n/**\n * The experimental voice-mode session against the thread's `voice/start` /\n * `voice/end` commands: mic capture and media negotiation (WebRTC by default,\n * or PCM over Agora's relay WebSocket); the live turns stream into the chat\n * log as `ephemeral: true` entries in `state.messages`.\n */\nexport const useDeepAgentVoice = (\n options: UseDeepAgentVoiceOptions = {}\n): UseDeepAgentVoiceReturn => {\n const wireSendCommand = useStatewireSendCommand();\n const sendCommandRef = useRef(wireSendCommand);\n sendCommandRef.current = wireSendCommand;\n // Normalize synchronous dispatch failures and async verdicts in one place.\n // Callers can always catch a rejection, even while the thread unmounts.\n const sendCommand = useCallback(\n async (command: Parameters<typeof wireSendCommand>[0]) => sendCommandRef.current(command),\n []\n );\n const voice = useStatewireState(\n // The raw wire state is non-object until the attach snapshot hydrates it.\n (s) => (readDeepAgentStreamValues(s).voice as DeepAgentVoiceWireState | null | undefined) ?? null\n );\n const [phase, setPhase] = useState<DeepAgentVoicePhase>('idle');\n const [failure, setFailure] = useState<DeepAgentVoiceFailure | null>(null);\n const error = failure?.message ?? null;\n const [micMuted, setMicMutedState] = useState(false);\n // Mute is user intent, not media state. A replacement stream must inherit it\n // before any track is attached, including during automatic recovery.\n const micMutedRef = useRef(false);\n const micChangeRef = useRef(0);\n const [liveSince, setLiveSince] = useState<number | null>(null);\n // The task that was running when the last session ended: the run continues\n // on the thread, so surfaces keep a \"still working\" note until it settles.\n const [handoffTask, setHandoffTask] = useState<{\n taskId: string;\n text: string;\n } | null>(null);\n const lastActiveTaskRef = useRef<{ taskId: string; text: string } | null>(null);\n const pcRef = useRef<VoicePeerConnection | null>(null);\n const releasePeerListenersRef = useRef<(() => void) | null>(null);\n const streamRef = useRef<DeepAgentVoiceRemoteStream | null>(null);\n // WebSocket transport state: the relay socket and the local audio graph.\n const wsRef = useRef<VoiceWebSocket | null>(null);\n const audioContextRef = useRef<VoiceAudioContext | null>(null);\n const audioNodesRef = useRef<VoiceAudioNode[]>([]);\n const playheadRef = useRef(0);\n // LiveKit transport state: the joined room.\n const livekitRef = useRef<DeepAgentVoiceLiveKitSession | null>(null);\n const livekitAbortRef = useRef<AbortController | null>(null);\n // Bumped by teardown and by every start(): a startup attempt that awakes\n // from an await into a different generation is stale — it must release\n // whatever it acquired and exit without touching session state.\n const attemptRef = useRef(0);\n const startingRef = useRef(false);\n // The in-flight startup, so a media-loss restart can wait for a startup\n // that died mid-negotiation to release its guard before it retries.\n const startPromiseRef = useRef<Promise<void> | null>(null);\n // One-shot resurrection budget: a session the SERVER dropped (connection\n // blip, host rotation) or whose MEDIA path died restarts once per\n // user-initiated session; a second drop — or a failed restart — settles to\n // idle/error so a flapping host or network can never loop the mic grant.\n const autoRestartedRef = useRef(false);\n const onRemoteStreamRef = useRef(options.onRemoteStream);\n onRemoteStreamRef.current = options.onRemoteStream;\n const audioSessionRef = useRef(options.audioSession);\n audioSessionRef.current = options.audioSession;\n const transportRef = useRef(options.transport ?? 'webrtc');\n transportRef.current = options.transport ?? 'webrtc';\n const relayUrlRef = useRef(options.relayUrl);\n relayUrlRef.current = options.relayUrl;\n const livekitAdapterRef = useRef(options.livekit);\n livekitAdapterRef.current = options.livekit;\n const modelRef = useRef(options.model);\n modelRef.current = options.model;\n const onDiagnosticRef = useRef(options.onDiagnostic);\n onDiagnosticRef.current = options.onDiagnostic;\n const timeoutsRef = useRef(options.timeouts);\n timeoutsRef.current = options.timeouts;\n // The phase as of the last render, for the unmount cleanup (which runs\n // outside React's render cycle and cannot read state).\n const phaseRef = useRef<DeepAgentVoicePhase>('idle');\n phaseRef.current = phase;\n // When the current attempt's start was requested, for diagnostic timings.\n const attemptStartedAtRef = useRef(0);\n // The session object whose start() succeeded — teardown must stop that\n // same object even if `options.audioSession` changes mid-session.\n const activeAudioSessionRef = useRef<UseDeepAgentVoiceOptions['audioSession'] | null>(null);\n const startingAudioSessionRef = useRef<UseDeepAgentVoiceOptions['audioSession'] | null>(null);\n // Serializes stop -> start: the next start() waits for the previous\n // session's shutdown to settle before claiming the platform audio session.\n const audioSessionIdleRef = useRef<Promise<void>>(Promise.resolve());\n\n const report = useCallback(\n (\n event: DeepAgentVoiceDiagnosticEvent,\n attempt: number,\n data?: DeepAgentVoiceDiagnostic['data']\n ): void => {\n const observer = onDiagnosticRef.current;\n if (!observer) return;\n const at = Date.now();\n try {\n observer({\n event,\n attempt,\n at,\n sinceStartMs: Math.max(0, at - attemptStartedAtRef.current),\n ...(data !== undefined && { data }),\n });\n } catch {\n // An observer is telemetry: it must never take the session down.\n }\n },\n []\n );\n\n const releaseAudioSession = useCallback((): void => {\n const session = activeAudioSessionRef.current;\n if (!session) return;\n activeAudioSessionRef.current = null;\n audioSessionIdleRef.current = audioSessionIdleRef.current\n .then(() => session.stop())\n .catch(() => undefined);\n }, []);\n\n const teardown = useCallback((): void => {\n attemptRef.current += 1;\n const livekitAbort = livekitAbortRef.current;\n livekitAbortRef.current = null;\n releaseSafely(() => livekitAbort?.abort());\n releaseSafely(() => startingAudioSessionRef.current?.cancelStart?.());\n releaseAudioSession();\n const releasePeerListeners = releasePeerListenersRef.current;\n releasePeerListenersRef.current = null;\n releaseSafely(() => releasePeerListeners?.());\n const pc = pcRef.current;\n pcRef.current = null;\n releaseSafely(() => pc?.close());\n const ws = wsRef.current;\n wsRef.current = null;\n releaseSafely(() => {\n if (ws && ws.readyState < 2) ws.close(1000, 'client end');\n });\n const nodes = audioNodesRef.current;\n audioNodesRef.current = [];\n for (const node of nodes) releaseSafely(() => node.disconnect());\n const audio = audioContextRef.current;\n audioContextRef.current = null;\n releaseSafely(() => audio?.close());\n playheadRef.current = 0;\n const livekit = livekitRef.current;\n livekitRef.current = null;\n releaseSafely(() => livekit?.disconnect());\n const stream = streamRef.current;\n streamRef.current = null;\n releaseCaptureStream(stream);\n setLiveSince(null);\n releaseSafely(() => onRemoteStreamRef.current?.(null));\n }, [releaseAudioSession]);\n\n const fail = useCallback(\n (nextFailure: DeepAgentVoiceFailure): void => {\n report('error', attemptRef.current, {\n kind: nextFailure.kind,\n stage: nextFailure.stage,\n message: nextFailure.message,\n code: nextFailure.code ?? null,\n status: nextFailure.status ?? null,\n fate: nextFailure.fate ?? null,\n reason: nextFailure.reason ?? null,\n });\n teardown();\n setFailure(nextFailure);\n setPhase('error');\n },\n [report, teardown]\n );\n\n // Forward reference: the media-loss handler restarts through the same\n // startSession the user start uses, and startSession installs the watcher\n // that calls the handler.\n const handleMediaLossRef = useRef<(attempt: number, reason: string) => Promise<void>>(\n async () => undefined\n );\n\n /**\n * Watch the peer connection's transport. The sideband can outlive a dead\n * media path (the server keeps a \"live\" session nobody can hear), so the\n * client is the only side that can notice: `failed`/`closed` are final,\n * `disconnected` gets a short grace to recover on its own.\n */\n const watchConnection = useCallback((pc: VoicePeerConnection, attempt: number): (() => void) => {\n let disconnectTimer: ReturnType<typeof setTimeout> | null = null;\n const clearDisconnectTimer = (): void => {\n if (disconnectTimer === null) return;\n clearTimeout(disconnectTimer);\n disconnectTimer = null;\n };\n const onChange = (): void => {\n if (attemptRef.current !== attempt) {\n clearDisconnectTimer();\n return;\n }\n const state = pc.connectionState ?? pc.iceConnectionState;\n report('media-state', attempt, {\n connectionState: pc.connectionState ?? null,\n iceConnectionState: pc.iceConnectionState ?? null,\n });\n if (state === 'failed' || state === 'closed') {\n clearDisconnectTimer();\n void handleMediaLossRef.current(attempt, state);\n return;\n }\n if (state === 'disconnected') {\n if (disconnectTimer !== null) return;\n disconnectTimer = setTimeout(() => {\n disconnectTimer = null;\n if (attemptRef.current !== attempt) return;\n void handleMediaLossRef.current(attempt, 'disconnected');\n }, MEDIA_DISCONNECT_GRACE_MS);\n return;\n }\n // connected / connecting / new: a transient disconnect recovered.\n clearDisconnectTimer();\n };\n pc.addEventListener('connectionstatechange', onChange);\n pc.addEventListener('iceconnectionstatechange', onChange);\n return () => {\n clearDisconnectTimer();\n releaseSafely(() => pc.removeEventListener('connectionstatechange', onChange));\n releaseSafely(() => pc.removeEventListener('iceconnectionstatechange', onChange));\n };\n }, [report]);\n\n /** Schedule one relayed PCM chunk gaplessly after the previous one. */\n const playRelayChunk = useCallback((audio: VoiceAudioContext, data: ArrayBuffer): void => {\n const pcm = new Int16Array(data);\n if (pcm.length === 0) return;\n const samples = float32FromPcm16(pcm);\n const buffer = audio.createBuffer(1, samples.length, VOICE_RELAY_SAMPLE_RATE);\n buffer.copyToChannel(samples, 0);\n const node = audio.createBufferSource();\n node.buffer = buffer;\n node.connect(audio.destination);\n const startAt = Math.max(audio.currentTime + RELAY_PLAYBACK_LEAD_SECONDS, playheadRef.current);\n node.start(startAt);\n playheadRef.current = startAt + buffer.duration;\n }, []);\n\n const runStartSession = useCallback(async (): Promise<void> => {\n const attempt = (attemptRef.current += 1);\n startingRef.current = true;\n attemptStartedAtRef.current = Date.now();\n setFailure(null);\n setPhase('connecting');\n // The step under way, so a thrown error is classified by WHERE it\n // happened rather than by its wording.\n let stage: DeepAgentVoiceFailureStage = 'preflight';\n const timeouts = timeoutsRef.current;\n const iceGatheringMs = timeouts?.iceGatheringMs ?? ICE_TIMEOUT_MS;\n const answerMs = timeouts?.answerMs ?? ANSWER_TIMEOUT_MS;\n const relayOpenMs = timeouts?.relayOpenMs ?? RELAY_OPEN_TIMEOUT_MS;\n const stale = (): void => {\n report('stale-attempt', attempt, { stage });\n releaseAudioSession();\n };\n report('start-requested', attempt, { transport: transportRef.current });\n try {\n const transport = transportRef.current;\n const mediaDevices = voiceGlobals.navigator?.mediaDevices;\n const PeerConnection = voiceGlobals.RTCPeerConnection;\n const WebSocketCtor = voiceGlobals.WebSocket;\n const AudioContextCtor = voiceGlobals.AudioContext;\n const livekitAdapter = livekitAdapterRef.current;\n // LiveKit captures the microphone itself, inside the adapter.\n if (transport === 'livekit' && !livekitAdapter)\n throw new Error('Voice is unavailable in this environment');\n if (transport !== 'livekit' && !mediaDevices)\n throw new Error('Microphone capture is unavailable in this environment');\n if (transport === 'webrtc' && !PeerConnection)\n throw new Error('WebRTC is unavailable in this environment');\n if (transport === 'websocket' && (!WebSocketCtor || !AudioContextCtor))\n throw new Error('WebSocket audio is unavailable in this environment');\n const relayUrl = relayUrlRef.current;\n if (transport === 'websocket' && !relayUrl)\n throw new Error('The websocket voice transport needs a relay URL');\n const audioSession = audioSessionRef.current;\n if (audioSession) {\n stage = 'audio-session';\n await audioSessionIdleRef.current;\n if (attemptRef.current !== attempt) {\n report('stale-attempt', attempt, { stage });\n return;\n }\n startingAudioSessionRef.current = audioSession;\n try {\n await audioSession.start();\n } finally {\n startingAudioSessionRef.current = null;\n }\n activeAudioSessionRef.current = audioSession;\n if (attemptRef.current !== attempt) {\n stale();\n return;\n }\n report('audio-session-ready', attempt);\n }\n // Resolved once per attempt, so an automatic restart re-asks for the\n // model the user had picked when this session started.\n const modelOption = modelRef.current;\n const requestedModel = typeof modelOption === 'function' ? modelOption() : modelOption;\n const model =\n typeof requestedModel === 'string' && requestedModel !== '' ? requestedModel : undefined;\n const sendVoiceStart = (offer: string) =>\n sendCommand({\n type: 'voice/start',\n offer,\n ...(model !== undefined && { model }),\n }) as Promise<{\n answer?: unknown;\n } | null>;\n // A durable command accepted immediately before host rotation can be\n // replayed on the replacement host after its socket-scoped auth result\n // has expired. The server rejects that replay fail-closed with an exact\n // retriable code. By the time the client receives the verdict it is\n // attached to the replacement host, so repeat the SAME offer once. Do\n // not retry generic 5xx/channel failures: boundedness avoids duplicate\n // live sessions and unbounded GPT Live spend.\n const negotiateStart = async (offer: string): Promise<string> => {\n stage = 'negotiate';\n const negotiationStartedAt = Date.now();\n const commandPromise = sendVoiceStart(offer).catch((commandError: unknown) => {\n if (!isRecoveredCommandAuthRejection(commandError)) throw commandError;\n return sendVoiceStart(offer);\n });\n // A session that opens server-side after this attempt was torn down\n // (local end, unmount, or the answer timeout below) must not linger.\n void commandPromise\n .then(() => {\n if (attemptRef.current !== attempt)\n void sendCommand({ type: 'voice/end' }).catch(() => undefined);\n })\n .catch(() => undefined);\n let answerTimer: ReturnType<typeof setTimeout> | null = null;\n let result: { answer?: unknown } | null;\n try {\n result = await Promise.race([\n commandPromise,\n new Promise<never>((_, reject) => {\n answerTimer = setTimeout(() => reject(new VoiceAnswerTimeoutError()), answerMs);\n }),\n ]);\n } finally {\n if (answerTimer !== null) clearTimeout(answerTimer);\n }\n report('answer-received', attempt, {\n negotiationMs: Date.now() - negotiationStartedAt,\n });\n stage = 'answer';\n const answer = result?.answer;\n if (typeof answer !== 'string' || answer === '')\n throw new VoiceNoAnswerError('voice/start returned no answer');\n return answer;\n };\n\n if (transport === 'livekit') {\n if (!livekitAdapter) return;\n const answer = await negotiateStart(VOICE_LIVEKIT_OFFER);\n if (attemptRef.current !== attempt) {\n stale();\n return;\n }\n // From here the host holds an open session (it answered): a client\n // that cannot use the room must end it, or the model session keeps\n // running with no media leg until the attach departure reaps it.\n try {\n const room = parseVoiceLiveKitAnswer(answer);\n stage = 'room';\n // A drop is only a media loss once the join completed; the adapter\n // owns the failure of a join that never completed.\n let joined = false;\n let disconnectedWhileJoining = false;\n const controller = new AbortController();\n livekitAbortRef.current = controller;\n const session = await livekitAdapter.connect(room, {\n micMuted: micMutedRef.current,\n signal: controller.signal,\n onDisconnected: (reason) => {\n if (attemptRef.current !== attempt) return;\n if (!joined) {\n disconnectedWhileJoining = true;\n return;\n }\n void handleMediaLossRef.current(attempt, reason);\n },\n });\n if (attemptRef.current !== attempt) {\n releaseSafely(() => session.disconnect());\n stale();\n return;\n }\n if (disconnectedWhileJoining) {\n releaseSafely(() => session.disconnect());\n throw new Error('The voice room disconnected while joining');\n }\n joined = true;\n livekitRef.current = session;\n report('room-joined', attempt, { roomName: room.roomName });\n } catch (roomError) {\n if (attemptRef.current === attempt)\n void sendCommand({ type: 'voice/end' }).catch(() => undefined);\n throw roomError;\n }\n report('live', attempt, { transport, ...(model !== undefined && { model }) });\n if (micMutedRef.current) {\n void sendCommand({ type: 'voice/set-mic', paused: true }).catch(() => undefined);\n }\n setPhase('live');\n setLiveSince(Date.now());\n return;\n }\n\n if (!mediaDevices) return;\n stage = 'capture';\n const stream = await mediaDevices.getUserMedia({ audio: true });\n if (attemptRef.current !== attempt) {\n releaseCaptureStream(stream);\n stale();\n return;\n }\n streamRef.current = stream;\n for (const track of stream.getTracks()) track.enabled = !micMutedRef.current;\n report('microphone-captured', attempt, {\n tracks: stream.getTracks().length,\n });\n\n if (transport === 'websocket') {\n if (!WebSocketCtor || !AudioContextCtor || !relayUrl) return;\n const answer = await negotiateStart(VOICE_WEBSOCKET_OFFER);\n if (attemptRef.current !== attempt) {\n stale();\n return;\n }\n if (!answer.startsWith(VOICE_RELAY_ANSWER_PREFIX))\n throw new VoiceNoAnswerError('voice/start returned no relay ticket');\n stage = 'relay';\n const ticket = answer.slice(VOICE_RELAY_ANSWER_PREFIX.length);\n const ws = new WebSocketCtor(voiceRelayUrl(relayUrl, ticket));\n ws.binaryType = 'arraybuffer';\n wsRef.current = ws;\n await waitForRelayOpen(ws, relayOpenMs);\n if (attemptRef.current !== attempt) {\n stale();\n return;\n }\n // Capture at the relay's rate so no client-side resampling is needed:\n // the browser resamples the microphone into the context's rate.\n const audio = new AudioContextCtor({\n sampleRate: VOICE_RELAY_SAMPLE_RATE,\n });\n audioContextRef.current = audio;\n await audio.resume?.();\n if (attemptRef.current !== attempt) {\n stale();\n return;\n }\n const source = audio.createMediaStreamSource(stream);\n const processor = audio.createScriptProcessor(RELAY_CAPTURE_FRAME_SAMPLES, 1, 1);\n // A processor only runs while connected to the destination; the\n // silent gain keeps the microphone from playing back locally.\n const sink = audio.createGain();\n sink.gain.value = 0;\n processor.onaudioprocess = (event) => {\n if (attemptRef.current !== attempt || ws.readyState !== 1) return;\n // A muted track yields silence, which keeps the frame cadence the\n // Live API expects while input is paused.\n ws.send(pcm16FromFloat32(event.inputBuffer.getChannelData(0)).buffer);\n };\n source.connect(processor);\n processor.connect(sink);\n sink.connect(audio.destination);\n audioNodesRef.current = [source, processor, sink];\n playheadRef.current = 0;\n ws.addEventListener('message', (event) => {\n if (attemptRef.current !== attempt) return;\n // Text frames are relay control (`ready`, `pong`); audio is binary.\n if (event.data instanceof ArrayBuffer) playRelayChunk(audio, event.data);\n });\n ws.addEventListener('close', () => {\n if (attemptRef.current !== attempt) return;\n void handleMediaLossRef.current(attempt, 'relay-closed');\n });\n ws.addEventListener('error', () => {\n if (attemptRef.current !== attempt) return;\n void handleMediaLossRef.current(attempt, 'relay-error');\n });\n report('live', attempt, { transport });\n if (micMutedRef.current) {\n void sendCommand({ type: 'voice/set-mic', paused: true }).catch(() => undefined);\n }\n setPhase('live');\n setLiveSince(Date.now());\n return;\n }\n\n if (!PeerConnection) return;\n stage = 'offer';\n const pc = new PeerConnection();\n pcRef.current = pc;\n for (const track of stream.getTracks()) pc.addTrack(track, stream);\n const onTrack = async (trackEvent: VoiceTrackEvent): Promise<void> => {\n if (attemptRef.current !== attempt) return;\n try {\n const remote =\n trackEvent.streams[0] ??\n (voiceGlobals.MediaStream ? new voiceGlobals.MediaStream([trackEvent.track]) : null);\n if (!remote) return;\n await onRemoteStreamRef.current?.(remote);\n } catch (playError) {\n // audio.play() can reject after hangup has already opened another call.\n if (attemptRef.current !== attempt) return;\n void sendCommand({ type: 'voice/end' }).catch(() => undefined);\n fail({\n kind: 'playback_failed',\n stage: 'playback',\n message: `Audio playback: ${errorMessage(playError)}`,\n });\n }\n };\n pc.addEventListener('track', onTrack);\n const unwatchConnection = watchConnection(pc, attempt);\n releasePeerListenersRef.current = () => {\n unwatchConnection();\n releaseSafely(() => pc.removeEventListener('track', onTrack));\n };\n const offer = await pc.createOffer();\n await pc.setLocalDescription(offer);\n const iceStartedAt = Date.now();\n const iceOutcome = await waitForIce(pc, iceGatheringMs);\n if (attemptRef.current !== attempt) {\n stale();\n return;\n }\n if (iceOutcome === 'timeout') {\n report('ice-gathering-timeout', attempt, {\n waitedMs: Date.now() - iceStartedAt,\n iceGatheringState: pc.iceGatheringState,\n });\n }\n const sdp = pc.localDescription?.sdp;\n if (!sdp) throw new Error('failed to create a local SDP offer');\n report('offer-ready', attempt, {\n iceGatheringMs: Date.now() - iceStartedAt,\n iceGathering: iceOutcome,\n sdpBytes: sdp.length,\n });\n const answer = await negotiateStart(sdp);\n if (attemptRef.current !== attempt) {\n stale();\n return;\n }\n await pc.setRemoteDescription({ type: 'answer', sdp: answer });\n if (attemptRef.current !== attempt) {\n stale();\n return;\n }\n report('live', attempt, { transport });\n if (micMutedRef.current) {\n void sendCommand({ type: 'voice/set-mic', paused: true }).catch(() => undefined);\n }\n setPhase('live');\n setLiveSince(Date.now());\n } catch (startError) {\n if (attemptRef.current !== attempt) {\n stale();\n return;\n }\n fail(classifyStartFailure(startError, stage));\n } finally {\n startingRef.current = false;\n }\n }, [fail, playRelayChunk, releaseAudioSession, report, sendCommand, watchConnection]);\n\n const startSession = useCallback(async (): Promise<void> => {\n if (pcRef.current || wsRef.current || livekitRef.current || startingRef.current) return;\n const run = runStartSession();\n startPromiseRef.current = run;\n try {\n await run;\n } finally {\n if (startPromiseRef.current === run) startPromiseRef.current = null;\n }\n }, [runStartSession]);\n const startRef = useRef(startSession);\n startRef.current = startSession;\n\n /**\n * The media path died while the server still holds a live session. End it\n * server-side first (a `voice/start` racing the old session's teardown is\n * rejected `voice-active`), then spend the one-shot restart budget — or,\n * if it is already spent, settle to an error the surface can show.\n *\n * The restart is cancelled by anything that supersedes this recovery while\n * it waits: an explicit `end()`, an unmount, or a user start all bump the\n * attempt generation, and a stale recovery must never reopen the mic.\n */\n const handleMediaLoss = useCallback(\n async (attempt: number, reason: string): Promise<void> => {\n if (attemptRef.current !== attempt) return;\n report('media-loss', attempt, {\n reason,\n restartBudgetLeft: !autoRestartedRef.current,\n });\n if (lastActiveTaskRef.current) setHandoffTask(lastActiveTaskRef.current);\n const ending: Promise<unknown> = sendCommand({ type: 'voice/end' }).catch(() => undefined);\n teardown();\n const recovery = attemptRef.current;\n setPhase('idle');\n let endTimer: ReturnType<typeof setTimeout> | undefined;\n try {\n await Promise.race([\n ending,\n new Promise<void>((resolve) => {\n endTimer = setTimeout(resolve, END_BEFORE_RESTART_TIMEOUT_MS);\n }),\n ]);\n } finally {\n clearTimeout(endTimer);\n }\n // A startup that was still negotiating when the transport died exits\n // at its next generation check; wait for it to release its guard so\n // the replacement start is not refused as \"already starting\".\n const inflight = startPromiseRef.current;\n if (inflight) await inflight.catch(() => undefined);\n if (attemptRef.current !== recovery) return;\n if (!autoRestartedRef.current) {\n autoRestartedRef.current = true;\n report('auto-restart', recovery, { trigger: 'media-loss', reason });\n await startRef.current();\n return;\n }\n // The server-drop effect may already be restarting — do not override it.\n if (startingRef.current || pcRef.current || wsRef.current || livekitRef.current) return;\n const lost: DeepAgentVoiceFailure = {\n kind: 'media_lost',\n stage: 'media',\n message: `The voice connection was lost (${reason})`,\n reason,\n };\n report('error', recovery, {\n kind: lost.kind,\n stage: lost.stage,\n message: lost.message,\n reason,\n });\n setFailure(lost);\n setPhase('error');\n },\n [report, sendCommand, teardown]\n );\n handleMediaLossRef.current = handleMediaLoss;\n\n // The public start is a USER start: it re-arms the one-shot auto-restart\n // budget the server-drop effect consumes (the automatic hop itself calls\n // `startSession` directly, so it can never re-arm its own budget).\n const start = useCallback(async (): Promise<void> => {\n if (pcRef.current || wsRef.current || livekitRef.current || startingRef.current) return;\n micMutedRef.current = false;\n setMicMutedState(false);\n autoRestartedRef.current = false;\n setHandoffTask(null);\n await startSession();\n }, [startSession]);\n\n const end = useCallback((): void => {\n if (phase === 'connecting' || phase === 'live') {\n report('end', attemptRef.current, { source: 'user', phase });\n void sendCommand({ type: 'voice/end' }).catch(() => undefined);\n if (lastActiveTaskRef.current) setHandoffTask(lastActiveTaskRef.current);\n }\n teardown();\n micMutedRef.current = false;\n setMicMutedState(false);\n setPhase('idle');\n }, [phase, report, sendCommand, teardown]);\n\n const setMicMuted = useCallback(\n (muted: boolean): void => {\n if (phase !== 'live') return;\n const attempt = attemptRef.current;\n const change = ++micChangeRef.current;\n const applyLocal = (value: boolean): void => {\n micMutedRef.current = value;\n setMicMutedState(value);\n for (const track of streamRef.current?.getTracks() ?? []) track.enabled = !value;\n // LiveKit owns the microphone track on its transport: best effort,\n // the protocol pause below is what actually silences the model.\n const room = livekitRef.current;\n if (room) releaseSafely(() => room.setMicrophoneEnabled(!value));\n };\n // Local track disable is the instant lever; the protocol pause is the\n // authoritative one (server discards paused speech).\n applyLocal(muted);\n void sendCommand({ type: 'voice/set-mic', paused: muted }).catch(() => {\n // A failed MUTE is safe — the disabled track transmits nothing either\n // way. A failed UNMUTE would claim live audio while the server still\n // discards it, so fall back to the muted truth (unless this session\n // was already torn down, whose reset wins).\n if (!muted && attemptRef.current === attempt && micChangeRef.current === change) {\n applyLocal(true);\n }\n });\n },\n [phase, sendCommand]\n );\n\n // A server-side end (connection drop, another surface, host shutdown)\n // clears `state.voice`; follow it down. Gate on a HYDRATED snapshot: the\n // raw wire state republishes `undefined` on replay boundaries (reconnects,\n // optimistic windows around sends), and tearing down on that transient\n // would kill a perfectly live session every time the user typed a message.\n const sessionGone = useStatewireState(\n (s) => isDeepAgentStateHydrated(s) && (readDeepAgentStreamValues(s).voice ?? null) === null\n );\n useEffect(() => {\n if (phase !== 'live' || !sessionGone) return;\n report('server-ended', attemptRef.current, {\n restartBudgetLeft: !autoRestartedRef.current,\n });\n if (lastActiveTaskRef.current) setHandoffTask(lastActiveTaskRef.current);\n teardown();\n setPhase('idle');\n if (!autoRestartedRef.current) {\n autoRestartedRef.current = true;\n report('auto-restart', attemptRef.current, { trigger: 'server-ended' });\n void startRef.current?.();\n }\n }, [phase, report, sessionGone, teardown]);\n\n // Unmount: release the media, and — when a session is still underway — end\n // it server-side too. The host would reap an orphaned session once this\n // client's attach departs (its departure grace), but a surface that\n // unmounts its voice controls while the thread connection stays up (a\n // remount of the composer, a navigation that keeps the runtime) would\n // otherwise leave the Live API session running and billing with no media\n // leg until the grace expired. Best-effort: the transport may already be\n // closing, in which case the departure path finishes the job.\n useEffect(\n () => () => {\n const phaseAtUnmount = phaseRef.current;\n if (phaseAtUnmount === 'connecting' || phaseAtUnmount === 'live') {\n report('end', attemptRef.current, {\n source: 'unmount',\n phase: phaseAtUnmount,\n });\n void sendCommand({ type: 'voice/end' }).catch(() => undefined);\n }\n teardown();\n },\n [report, sendCommand, teardown]\n );\n\n // Mirror the live session's delegated task so an end (local or server-side)\n // can hand it off: the run outlives the session, the projection does not.\n const activeTask = voice?.activeTask ?? null;\n useEffect(() => {\n if (phase === 'live') lastActiveTaskRef.current = activeTask;\n else if (phase === 'idle' || phase === 'error') lastActiveTaskRef.current = null;\n }, [phase, activeTask]);\n\n // The handoff note self-clears when the thread's run settles (the work it\n // pointed at is finished — or was never running to begin with). Unhydrated\n // snapshots keep the note: the raw wire state republishes `undefined` on\n // replay boundaries and must not read as \"settled\".\n const handoffRunActive = useStatewireState((s) => {\n if (!isDeepAgentStateHydrated(s)) return true;\n const status = readDeepAgentStreamValues(s).status;\n return status === 'running' || status === 'stopping' || status === 'input-required';\n });\n useEffect(() => {\n if (handoffTask !== null && !handoffRunActive) setHandoffTask(null);\n }, [handoffTask, handoffRunActive]);\n\n // The host projects the latest Live API rejection; every `voice/start`\n // resets it, so the wire value is always this session's. Hidden outside a\n // live session (a stale value after an end is inert).\n const wireError = useStatewireState((s) =>\n isDeepAgentStateHydrated(s) ? (readDeepAgentStreamValues(s).voiceLastError ?? null) : null\n );\n const notice = phase === 'live' && wireError ? describeVoiceWireError(wireError) : null;\n\n // The host stamps the resolved model when a session starts and publishes\n // its model catalogue at boot. Both are read raw off the hydrated snapshot\n // (the selectors must return referentially stable values — `Object.is`);\n // the catalogue is validated once per wire change below.\n const model = useStatewireState((s) => {\n if (!isDeepAgentStateHydrated(s)) return null;\n const value = readDeepAgentStreamValues(s).voiceModel;\n return typeof value === 'string' && value !== '' ? value : null;\n });\n const wireModelOptions = useStatewireState((s) =>\n isDeepAgentStateHydrated(s) ? (readDeepAgentStreamValues(s).voiceModelOptions ?? null) : null\n );\n // Replication may hand back a fresh array for unchanged content (a snapshot\n // replay, a reconnect); keep the previous validated list so memoized\n // consumers (the composer's picker) do not re-render on every wire update.\n const modelOptionsRef = useRef<DeepAgentVoiceModelOption[] | null>(null);\n const modelOptions = useMemo(() => {\n const next = parseVoiceModelOptions(wireModelOptions);\n const previous = modelOptionsRef.current;\n if (previous !== null && next !== null && sameVoiceModelOptions(previous, next)) return previous;\n modelOptionsRef.current = next;\n return next;\n }, [wireModelOptions]);\n\n const syncProfile = useCallback((): void => {\n if (phase !== 'connecting' && phase !== 'live') return;\n void sendCommand({ type: 'voice/profile-sync' }).catch(() => undefined);\n }, [phase, sendCommand]);\n\n const sendContext = useCallback(\n (text: string): void => {\n const trimmed = text.trim();\n if (phase !== 'live' || !trimmed) return;\n void sendCommand({ type: 'voice/context', text: trimmed }).catch(() => undefined);\n },\n [phase, sendCommand]\n );\n\n const sendUserText = useCallback(\n async (text: string): Promise<boolean> => {\n if (phase !== 'live' || !text.trim()) return false;\n // Client-minted id: a durable command replayed on a replacement host\n // reuses it, and the checkpoint append is idempotent by message id —\n // a crash between the server's commit and its ack cannot duplicate.\n const messageId = mintVoiceTextId();\n try {\n await sendCommand({ type: 'voice/user-text', text, messageId });\n return true;\n } catch {\n return false;\n }\n },\n [phase, sendCommand]\n );\n\n // One object per change of what it reports: surfaces pass the session down whole (the composer's\n // voice controls, its mic and end buttons), so a fresh literal per render re-rendered them on\n // every render of their host, e.g. every keystroke in the composer.\n return useMemo(\n () => ({\n phase,\n error,\n failure,\n start,\n end,\n activeTask,\n liveSince,\n micMuted,\n setMicMuted,\n sendUserText,\n handoffTask,\n syncProfile,\n notice,\n sendContext,\n model,\n modelOptions,\n }),\n [\n phase,\n error,\n failure,\n start,\n end,\n activeTask,\n liveSince,\n micMuted,\n setMicMuted,\n sendUserText,\n handoffTask,\n syncProfile,\n notice,\n sendContext,\n model,\n modelOptions,\n ]\n );\n};\n"],"names":["model","offer","answer","_a"],"mappings":"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"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { type DeepAgentInterestRequest, type DeepAgentMessageWindow } from './message-window';
|
|
2
|
+
import type { DeepAgentToolOutputStore } from './tool-outputs';
|
|
3
|
+
/**
|
|
4
|
+
* Pages of older history chained per approach to the top. A renderer lowers
|
|
5
|
+
* its near-top level once prepended content has pushed the first message out
|
|
6
|
+
* of its band; this bounds the case where it cannot (no viewport to scroll,
|
|
7
|
+
* a list too short to overflow) so a 2,000-message thread never streams whole.
|
|
8
|
+
*/
|
|
9
|
+
export declare const DEEP_AGENT_MAX_CHAINED_HISTORY_LOADS = 3;
|
|
10
|
+
/**
|
|
11
|
+
* Pages of older history that add no converted message — every message on
|
|
12
|
+
* them folds into the first loaded turn, such as one agent run's hundreds of
|
|
13
|
+
* tool steps — loaded on top of a request (or an approach's chain) before it
|
|
14
|
+
* stops. Such a page changes nothing the user can see, so it neither answers
|
|
15
|
+
* an ask nor counts against the per-approach chain.
|
|
16
|
+
*/
|
|
17
|
+
export declare const DEEP_AGENT_MAX_UNSEEN_HISTORY_LOADS = 25;
|
|
18
|
+
/** The converted thread messages the tool-output half scans (web and native `ThreadMessage` both satisfy it). */
|
|
19
|
+
export interface DeepAgentWindowSyncMessage {
|
|
20
|
+
readonly id: string;
|
|
21
|
+
/** The converted role; lets the opening fill tell a turn's prompt from its reply. */
|
|
22
|
+
readonly role?: string;
|
|
23
|
+
readonly content: readonly {
|
|
24
|
+
readonly type: string;
|
|
25
|
+
readonly artifact?: unknown;
|
|
26
|
+
}[];
|
|
27
|
+
}
|
|
28
|
+
/** One page interest round trip, for the host's telemetry. */
|
|
29
|
+
export interface DeepAgentHistoryLoadEvent {
|
|
30
|
+
/** The page's ordinal (`1` extends the head). */
|
|
31
|
+
page: number;
|
|
32
|
+
request: DeepAgentInterestRequest;
|
|
33
|
+
durationMs: number;
|
|
34
|
+
}
|
|
35
|
+
export interface UseDeepAgentWindowSyncOptions {
|
|
36
|
+
/**
|
|
37
|
+
* The converted thread messages — read by the host through its platform
|
|
38
|
+
* `useAuiState((s) => s.thread.messages)`, so this hook imports neither
|
|
39
|
+
* `@assistant-ui/react` nor `@assistant-ui/react-native`.
|
|
40
|
+
*/
|
|
41
|
+
messages: readonly DeepAgentWindowSyncMessage[];
|
|
42
|
+
/** Messages per page; the thread's window size by default. */
|
|
43
|
+
pageSize?: number;
|
|
44
|
+
/**
|
|
45
|
+
* Full tool outputs fetched back for cut tool messages; the same store the
|
|
46
|
+
* thread converts with (`useDeepAgentThread({ toolOutputs })`). Omitted, the
|
|
47
|
+
* tool-output half is inert (a surface that never shows a cut output whole).
|
|
48
|
+
*/
|
|
49
|
+
toolOutputs?: DeepAgentToolOutputStore | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* The renderer's report that the top of the LOADED history is near — a
|
|
52
|
+
* level, not an edge: while it holds and older messages exist, pages load
|
|
53
|
+
* one after another (bounded by `maxChainedLoads` per approach). Omitted for
|
|
54
|
+
* hosts with only a manual affordance (`loadOlder`).
|
|
55
|
+
*/
|
|
56
|
+
nearTop?: boolean;
|
|
57
|
+
/** Bumped on every rise of `nearTop` (and every explicit request), so the chain bound is per approach. */
|
|
58
|
+
nearTopEpoch?: number;
|
|
59
|
+
/**
|
|
60
|
+
* Whether a message's content is mounted right now, so lazy fetches (cut
|
|
61
|
+
* tool outputs) target what the user can see rather than the whole loaded
|
|
62
|
+
* history. Omitted: every message counts as mounted (the flat renderers).
|
|
63
|
+
*/
|
|
64
|
+
isMessageMounted?: ((messageId: string) => boolean) | undefined;
|
|
65
|
+
/**
|
|
66
|
+
* Cut tool outputs this mount reads at once; the rest wait for one to
|
|
67
|
+
* settle. One mounted message can hold a whole agent run — a hundred tool
|
|
68
|
+
* steps — and the host rate-limits a client's frames, so a reader that sends
|
|
69
|
+
* them all together gets refusals back for most of them. Omitted: every
|
|
70
|
+
* mounted output is asked for together (renderers whose mounted set is
|
|
71
|
+
* already small).
|
|
72
|
+
*/
|
|
73
|
+
maxConcurrentToolOutputs?: number;
|
|
74
|
+
maxChainedLoads?: number;
|
|
75
|
+
/** Pages adding no converted message loaded per request before it stops; see {@link DEEP_AGENT_MAX_UNSEEN_HISTORY_LOADS}. */
|
|
76
|
+
maxUnseenLoads?: number;
|
|
77
|
+
/** A page interest resolved (telemetry). */
|
|
78
|
+
onHistoryLoad?: ((event: DeepAgentHistoryLoadEvent) => void) | undefined;
|
|
79
|
+
/** A read failed; the sync itself recovers (the pending marks are released). */
|
|
80
|
+
onError?: ((error: unknown, command: 'athena/interest' | 'history/tool_output') => void) | undefined;
|
|
81
|
+
now?: () => number;
|
|
82
|
+
}
|
|
83
|
+
export interface DeepAgentWindowSyncState {
|
|
84
|
+
/** The joined window, or `null` while the head interest has not resolved (both halves are inert). */
|
|
85
|
+
window: DeepAgentMessageWindow | null;
|
|
86
|
+
/** Whether the host holds messages older than the loaded window. */
|
|
87
|
+
hasOlder: boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Whether the next page can be asked for: older messages exist and the
|
|
90
|
+
* host's interest cap has room for them. `hasOlder && !canLoadOlder` is the
|
|
91
|
+
* depth limit (`MAX_INTERESTS × MAX_MESSAGE_WINDOW` messages loaded in one
|
|
92
|
+
* connection) — an edge row says so instead of offering a dead button.
|
|
93
|
+
*/
|
|
94
|
+
canLoadOlder: boolean;
|
|
95
|
+
/** A page of older history is in flight. */
|
|
96
|
+
loading: boolean;
|
|
97
|
+
/** Ask for the next page of older history now (an edge row's button, a keypress). A no-op before the head resolved or at the beginning. */
|
|
98
|
+
loadOlder: () => void;
|
|
99
|
+
}
|
|
100
|
+
/** The joined interest window of the nearest statewire thread. */
|
|
101
|
+
export declare function useDeepAgentMessageWindow(): DeepAgentMessageWindow | null;
|
|
102
|
+
export declare function useDeepAgentWindowSync({ messages, pageSize, toolOutputs, nearTop, nearTopEpoch, isMessageMounted, maxConcurrentToolOutputs, maxChainedLoads, maxUnseenLoads, onHistoryLoad, onError, now, }: UseDeepAgentWindowSyncOptions): DeepAgentWindowSyncState;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { type HandoffLaunch, type HandoffOutcome, type HandoffSource, type ServerHandoffDestination, type ThreadHandoffTransport, type ThreadHandoffView } from './thread-handoff';
|
|
2
|
+
/** Mobile: a push lands in seconds; the QR path needs time to unlock a phone. */
|
|
3
|
+
export declare const DEFAULT_MOBILE_HANDOFF_TIMEOUT_MS = 120000;
|
|
4
|
+
/** Office: launching a desktop host from cold takes tens of seconds. */
|
|
5
|
+
export declare const DEFAULT_OFFICE_HANDOFF_TIMEOUT_MS = 180000;
|
|
6
|
+
export type ThreadHandoffFlowPhase = {
|
|
7
|
+
status: 'idle';
|
|
8
|
+
} | {
|
|
9
|
+
status: 'creating';
|
|
10
|
+
destination: ServerHandoffDestination;
|
|
11
|
+
threadId: string;
|
|
12
|
+
} | {
|
|
13
|
+
status: 'waiting';
|
|
14
|
+
destination: ServerHandoffDestination;
|
|
15
|
+
threadId: string;
|
|
16
|
+
handoff: ThreadHandoffView;
|
|
17
|
+
launch: HandoffLaunch;
|
|
18
|
+
startedAt: number;
|
|
19
|
+
} | {
|
|
20
|
+
status: 'acknowledged';
|
|
21
|
+
destination: ServerHandoffDestination;
|
|
22
|
+
threadId: string;
|
|
23
|
+
handoff: ThreadHandoffView;
|
|
24
|
+
launch: HandoffLaunch;
|
|
25
|
+
} | {
|
|
26
|
+
status: 'failed';
|
|
27
|
+
destination: ServerHandoffDestination;
|
|
28
|
+
threadId: string;
|
|
29
|
+
handoff: ThreadHandoffView | null;
|
|
30
|
+
launch: HandoffLaunch | null;
|
|
31
|
+
reason: string;
|
|
32
|
+
} | {
|
|
33
|
+
status: 'timeout';
|
|
34
|
+
destination: ServerHandoffDestination;
|
|
35
|
+
threadId: string;
|
|
36
|
+
handoff: ThreadHandoffView | null;
|
|
37
|
+
launch: HandoffLaunch;
|
|
38
|
+
};
|
|
39
|
+
export interface StartThreadHandoffInput {
|
|
40
|
+
threadId: string;
|
|
41
|
+
destination: ServerHandoffDestination;
|
|
42
|
+
}
|
|
43
|
+
export interface ThreadHandoffFlowCallbacks {
|
|
44
|
+
/** The server accepted the handoff; open the launch URL / show the QR. */
|
|
45
|
+
onLaunched?: (context: {
|
|
46
|
+
destination: ServerHandoffDestination;
|
|
47
|
+
threadId: string;
|
|
48
|
+
handoff: ThreadHandoffView;
|
|
49
|
+
launch: HandoffLaunch;
|
|
50
|
+
}) => void;
|
|
51
|
+
/** Terminal result (acknowledged/failed/timeout); `cancelled` is not reported. */
|
|
52
|
+
onSettled?: (context: {
|
|
53
|
+
destination: ServerHandoffDestination;
|
|
54
|
+
threadId: string;
|
|
55
|
+
outcome: Exclude<HandoffOutcome, {
|
|
56
|
+
status: 'cancelled';
|
|
57
|
+
}>;
|
|
58
|
+
}) => void;
|
|
59
|
+
}
|
|
60
|
+
export interface UseThreadHandoffFlowOptions extends ThreadHandoffFlowCallbacks {
|
|
61
|
+
transport: ThreadHandoffTransport;
|
|
62
|
+
source: HandoffSource;
|
|
63
|
+
timeoutMs?: Partial<Record<ServerHandoffDestination, number>>;
|
|
64
|
+
pollIntervalMs?: number;
|
|
65
|
+
}
|
|
66
|
+
export interface ThreadHandoffFlowController {
|
|
67
|
+
phase: ThreadHandoffFlowPhase;
|
|
68
|
+
/** True while a handoff is being created or awaiting the destination. */
|
|
69
|
+
isActive: boolean;
|
|
70
|
+
start: (input: StartThreadHandoffInput) => Promise<void>;
|
|
71
|
+
/** Stop waiting; the source keeps its copilot. The server record simply expires. */
|
|
72
|
+
cancel: () => void;
|
|
73
|
+
/** Back to idle after a terminal phase (dismisses the result UI). */
|
|
74
|
+
reset: () => void;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Drives one server-brokered handoff (mobile / Word / PowerPoint / Excel):
|
|
78
|
+
* create → launch → poll until the destination acknowledges. Exactly one
|
|
79
|
+
* handoff runs at a time; starting another cancels the previous wait.
|
|
80
|
+
*
|
|
81
|
+
* Source-side effects (closing the copilot only after `acknowledged`, toasts,
|
|
82
|
+
* opening `ms-word:` URLs) belong to the caller via `onLaunched`/`onSettled`.
|
|
83
|
+
*/
|
|
84
|
+
export declare function useThreadHandoffFlow({ transport, source, timeoutMs, pollIntervalMs, onLaunched, onSettled, }: UseThreadHandoffFlowOptions): ThreadHandoffFlowController;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/** `metadata.custom.source` of a spoken turn. */
|
|
2
|
+
export declare const VOICE_SOURCE_SPOKEN = "voice";
|
|
3
|
+
/** `metadata.custom.source` of a message typed into the live conversation. */
|
|
4
|
+
export declare const VOICE_SOURCE_TYPED = "text";
|
|
5
|
+
/** `metadata.custom.source` of a voice delegation's anchor message. */
|
|
6
|
+
export declare const VOICE_SOURCE_DELEGATION = "voice_delegation";
|
|
7
|
+
/** Id prefix of a voice delegation's anchor message (the working signal on
|
|
8
|
+
* hosts that drop the command's metadata). */
|
|
9
|
+
export declare const VOICE_DELEGATION_ID_PREFIX = "voicetask_";
|
|
10
|
+
/** `metadata.custom` flag on a spoken turn still in flight. */
|
|
11
|
+
export declare const VOICE_LIVE_KEY = "voice_live";
|
|
12
|
+
/** `metadata.custom` list of the wire ids a grouped spoken bubble stands for
|
|
13
|
+
* (see `groupSpokenUserTurns`), so id-keyed lookups such as checkpoint markers
|
|
14
|
+
* can resolve any member to the bubble. */
|
|
15
|
+
export declare const VOICE_TURN_IDS_KEY = "voice_turn_ids";
|
|
16
|
+
/** The delegated run's interim-update tool (agora `send_voice_update`). */
|
|
17
|
+
export declare const VOICE_UPDATE_TOOL_NAME = "send_voice_update";
|
|
18
|
+
/** Trailing hint on the live-session strip, worded once for every surface. */
|
|
19
|
+
export declare const VOICE_LIVE_TYPED_HINT = "typed messages go to the voice conversation";
|
|
20
|
+
export interface VoiceMessageMarker {
|
|
21
|
+
/** The thread message id; a host may not have assigned one yet. */
|
|
22
|
+
messageId: string | undefined;
|
|
23
|
+
/** The thread message's `metadata.custom`, when any. */
|
|
24
|
+
custom: Record<string, unknown> | undefined;
|
|
25
|
+
}
|
|
26
|
+
/** Whether a thread message is a voice delegation's anchor (the ask the live
|
|
27
|
+
* voice model handed to the thread), by source or by id prefix. */
|
|
28
|
+
export declare function isVoiceDelegationMessage({ messageId, custom }: VoiceMessageMarker): boolean;
|
|
29
|
+
export interface VoiceProvenance {
|
|
30
|
+
/** Chip copy: "Listening", "Speaking", "Spoken", or "Delegated from voice". */
|
|
31
|
+
readonly label: string;
|
|
32
|
+
/** True while the turn is still being heard or spoken. */
|
|
33
|
+
readonly live: boolean;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The provenance chip for a message, or `null` for ordinary chat messages so
|
|
37
|
+
* the chip is free to mount on every row. A spoken turn still in flight reads
|
|
38
|
+
* as what is happening ("Listening" for the user, "Speaking" for the
|
|
39
|
+
* assistant) rather than as a finished "Spoken" turn; a typed-in-voice turn
|
|
40
|
+
* is a normal message and gets no chip. Results are immutable and stable for
|
|
41
|
+
* equal presentation, so all surfaces can use this function as a selector.
|
|
42
|
+
*/
|
|
43
|
+
export declare function voiceProvenanceLabel({ messageId, custom, role, }: VoiceMessageMarker & {
|
|
44
|
+
role: 'user' | 'assistant' | string;
|
|
45
|
+
}): VoiceProvenance | null;
|
|
46
|
+
/**
|
|
47
|
+
* Whether a part of an assistant message holds the words of a spoken
|
|
48
|
+
* assistant turn.
|
|
49
|
+
*
|
|
50
|
+
* The converter joins consecutive assistant entries into one message. The
|
|
51
|
+
* host appends a live turn while it is heard, and a delegated run keeps
|
|
52
|
+
* appending its own steps after it, so a turn spoken during a run becomes a
|
|
53
|
+
* text part inside the run's message, before the run's later tool calls.
|
|
54
|
+
* Grouped views put the text before the last tool call in the details panel
|
|
55
|
+
* as the run's narration; they use this to keep the spoken words visible.
|
|
56
|
+
*
|
|
57
|
+
* The joined message's `metadata.custom` merges every entry in it, so this
|
|
58
|
+
* reads the part's own entry, which the converter binds to the part.
|
|
59
|
+
*/
|
|
60
|
+
export declare function isSpokenTurnTextPart(part: {
|
|
61
|
+
readonly type: string;
|
|
62
|
+
}): boolean;
|
|
63
|
+
export interface VoiceUpdateToolCall {
|
|
64
|
+
/** `mode: "speak"` — read aloud to the user; otherwise a silent progress note. */
|
|
65
|
+
spoken: boolean;
|
|
66
|
+
/** The update's words. */
|
|
67
|
+
text: string;
|
|
68
|
+
/** Whether the live session took it; `null` while the call is still running. */
|
|
69
|
+
delivered: boolean | null;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* What a `send_voice_update` tool call did, from its arguments and the tool's
|
|
73
|
+
* teaching-text result (agora returns prose, not JSON, so the run can read it).
|
|
74
|
+
*/
|
|
75
|
+
export declare function describeVoiceUpdateToolCall({ args, result, }: {
|
|
76
|
+
args: Record<string, unknown> | undefined;
|
|
77
|
+
result: unknown;
|
|
78
|
+
}): VoiceUpdateToolCall;
|
|
79
|
+
/** The card's status line for a voice update in a given tool-call state. */
|
|
80
|
+
export declare function voiceUpdateStatusLabel(call: VoiceUpdateToolCall, status: 'running' | 'complete' | 'incomplete' | 'requires-action' | undefined): string;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { LangChainMessage } from '@assistant-ui/react-langgraph';
|
|
2
|
+
/**
|
|
3
|
+
* A live voice session commits a spoken turn per utterance, so one breath of
|
|
4
|
+
* speech reaches the wire as several short human rows. Every chat surface
|
|
5
|
+
* renders consecutive spoken user turns as ONE user message: the run stays
|
|
6
|
+
* grouped until something other than a spoken user turn (the assistant's
|
|
7
|
+
* reply, a typed message, a delegation anchor) closes it. There is no pause
|
|
8
|
+
* threshold on purpose — the transcript is easier to read and reason about as
|
|
9
|
+
* one bubble per exchange.
|
|
10
|
+
*
|
|
11
|
+
* The merged message keeps the FIRST turn's id and metadata, so message
|
|
12
|
+
* actions (edit, retry) anchor where the user started speaking; lists every
|
|
13
|
+
* member's id under `voice_turn_ids`, so a checkpoint written after a later
|
|
14
|
+
* member still finds the bubble; and reads live while any member is still
|
|
15
|
+
* being heard. A turn the history forks at (its branch fan-out lists siblings)
|
|
16
|
+
* always starts a group: the branch picker reads `metadata.custom.branch` off
|
|
17
|
+
* the presented message, so a fork buried inside a group would be unreachable.
|
|
18
|
+
*/
|
|
19
|
+
type SpokenUserTurn = Extract<LangChainMessage, {
|
|
20
|
+
type: 'human';
|
|
21
|
+
}> & {
|
|
22
|
+
content: string;
|
|
23
|
+
};
|
|
24
|
+
/** A spoken (not typed, not delegated) user turn with plain text content. */
|
|
25
|
+
export declare function isSpokenUserTurn(message: LangChainMessage): message is SpokenUserTurn;
|
|
26
|
+
/** Collapse each run of consecutive spoken user turns into one human message. */
|
|
27
|
+
export declare function groupSpokenUserTurns(messages: readonly LangChainMessage[]): LangChainMessage[];
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A Live API rejection the voice host projected (`state.voiceLastError`):
|
|
3
|
+
* the API's error code and message plus, when the host could correlate the
|
|
4
|
+
* error to the command it rejected, that command's event type and target.
|
|
5
|
+
* `null` while the session has had no rejection; reset by every `voice/start`.
|
|
6
|
+
*/
|
|
7
|
+
export interface DeepAgentVoiceWireError {
|
|
8
|
+
code: string;
|
|
9
|
+
message: string;
|
|
10
|
+
requestType: string | null;
|
|
11
|
+
delegationItemId: string | null;
|
|
12
|
+
channel: string | null;
|
|
13
|
+
/** UTC ISO timestamp of the rejection. */
|
|
14
|
+
at: string;
|
|
15
|
+
}
|