@alia.onl/sdk 15.8.0 → 19.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/CHANGELOG.md +242 -13
  2. package/README.md +223 -151
  3. package/package.json +15 -5
  4. package/src/animations.ts +0 -1
  5. package/src/components/AliaAskProvider.tsx +127 -0
  6. package/src/components/AliaChatContent.tsx +604 -392
  7. package/src/components/AliaChatHeader.tsx +14 -38
  8. package/src/components/AliaChatScreen.tsx +56 -31
  9. package/src/components/AliaChatSheet.tsx +110 -36
  10. package/src/components/AliaComposer.tsx +1 -1
  11. package/src/components/AliaParticipants.tsx +202 -0
  12. package/src/components/AliaParticipantsHeader.tsx +60 -0
  13. package/src/components/AliaSharedConversation.tsx +86 -0
  14. package/src/components/AliaWelcomeMessage.tsx +18 -5
  15. package/src/components/Image.tsx +12 -0
  16. package/src/components/Markdown.tsx +498 -14
  17. package/src/components/PlanPreviewCard.tsx +7 -49
  18. package/src/components/ReplyMarkdown.tsx +359 -0
  19. package/src/components/RichBlocks.tsx +195 -0
  20. package/src/components/canvas/CanvasComponent.tsx +59 -0
  21. package/src/components/canvas/ChartRenderer.tsx +73 -0
  22. package/src/components/canvas/CodeRenderer.tsx +24 -0
  23. package/src/components/canvas/FormRenderer.tsx +101 -0
  24. package/src/components/canvas/MarkdownRenderer.tsx +14 -0
  25. package/src/components/canvas/TableRenderer.tsx +47 -0
  26. package/src/components/thought/FilesAndSources.tsx +120 -0
  27. package/src/components/thought/ThoughtPanel.tsx +417 -0
  28. package/src/components/thought/ToolStep.tsx +141 -0
  29. package/src/components/thread/AgentResultCard.tsx +125 -0
  30. package/src/components/thread/AgentTaskCard.tsx +127 -0
  31. package/src/components/thread/AliaThread.tsx +401 -0
  32. package/src/components/thread/MessageBlockBoundary.tsx +78 -0
  33. package/src/components/thread/NewConversationOffer.tsx +54 -0
  34. package/src/components/thread/ThreadRow.tsx +458 -0
  35. package/src/components/thread/ThreadSearch.tsx +129 -0
  36. package/src/components/thread/ToolResultCard.tsx +254 -0
  37. package/src/components/thread/TurnMenu.tsx +54 -0
  38. package/src/components/thread/TurnStatusLine.tsx +34 -0
  39. package/src/components/voice/AliaVoiceCall.tsx +21 -0
  40. package/src/components/voice/VoiceControls.tsx +2 -28
  41. package/src/components/voice/VoiceSession.tsx +39 -11
  42. package/src/hooks/useAliaActivity.ts +2 -2
  43. package/src/hooks/useAtBottom.ts +51 -0
  44. package/src/hooks/useAttachMenu.ts +94 -0
  45. package/src/hooks/useChatComposer.ts +299 -0
  46. package/src/hooks/useChatVoice.ts +129 -0
  47. package/src/hooks/useDocumentPicker.ts +3 -29
  48. package/src/hooks/useFailedTurnToast.ts +43 -0
  49. package/src/hooks/useMessageVote.ts +50 -0
  50. package/src/hooks/useOpenThought.ts +79 -0
  51. package/src/hooks/usePendingQuestions.ts +2 -2
  52. package/src/hooks/useReadAloud.ts +92 -0
  53. package/src/hooks/useShareHandler.ts +24 -0
  54. package/src/hooks/useSoundEffects.ts +1 -1
  55. package/src/hooks/useThoughtScope.ts +59 -0
  56. package/src/hooks/useThreadRows.ts +145 -0
  57. package/src/hooks/useThreadView.ts +68 -0
  58. package/src/hooks/useTimelineWindow.ts +256 -0
  59. package/src/hooks/useVoiceLauncher.ts +180 -0
  60. package/src/hooks/useVoiceRoom.ts +0 -3
  61. package/src/index.ts +43 -29
  62. package/src/lib/activity-store.ts +1 -1
  63. package/src/lib/add-menu.ts +7 -0
  64. package/src/lib/call-thread.ts +33 -0
  65. package/src/lib/citations.ts +79 -2
  66. package/src/lib/clipboard.ts +23 -0
  67. package/src/lib/content-utils.ts +10 -0
  68. package/src/lib/copy-en.ts +284 -0
  69. package/src/lib/copy.ts +32 -0
  70. package/src/lib/document-picker.ts +31 -0
  71. package/src/lib/flights.ts +22 -13
  72. package/src/lib/focus-return.ts +66 -0
  73. package/src/lib/keyboard.native.tsx +19 -0
  74. package/src/lib/keyboard.tsx +9 -0
  75. package/src/lib/labels.tsx +69 -17
  76. package/src/lib/participants.ts +86 -0
  77. package/src/lib/power-level-options.ts +66 -0
  78. package/src/lib/questions.ts +2 -2
  79. package/src/lib/task-labels.ts +4 -4
  80. package/src/lib/tool-call-status.ts +1 -1
  81. package/src/lib/tool-icons.ts +36 -0
  82. package/src/lib/tool-renderers.tsx +48 -0
  83. package/src/lib/turn-state.ts +2 -2
  84. package/src/lib/voice-call-request.ts +49 -0
  85. package/src/lib/voice-call.ts +76 -34
  86. package/src/lib/voice-controls-labels.ts +31 -0
  87. package/src/lib/voice-protocol.ts +32 -14
  88. package/src/lib/work-log.ts +6 -6
  89. package/src/runtime/agents.ts +151 -0
  90. package/src/runtime/api.ts +123 -0
  91. package/src/runtime/attachment-utils.ts +96 -0
  92. package/src/runtime/composer-draft-store.ts +270 -0
  93. package/src/runtime/config.tsx +186 -0
  94. package/src/runtime/conversation-store.ts +49 -0
  95. package/src/runtime/conversations.ts +557 -0
  96. package/src/runtime/error-utils.ts +104 -0
  97. package/src/runtime/host-tools.ts +140 -0
  98. package/src/runtime/index.ts +113 -0
  99. package/src/runtime/message-history.ts +124 -0
  100. package/src/runtime/model.ts +39 -0
  101. package/src/runtime/oxy-files.ts +37 -0
  102. package/src/runtime/power-levels.ts +66 -0
  103. package/src/runtime/query-keys.ts +34 -0
  104. package/src/runtime/random-uuid.ts +25 -0
  105. package/src/runtime/routes.ts +51 -0
  106. package/src/runtime/sharing.ts +165 -0
  107. package/src/runtime/sse-frame-reader.ts +88 -0
  108. package/src/runtime/stream-events.ts +146 -0
  109. package/src/runtime/stream-outcome.ts +21 -0
  110. package/src/runtime/thought-store.ts +103 -0
  111. package/src/runtime/thread/agent-activity.ts +261 -0
  112. package/src/runtime/thread/automation-labels.ts +79 -0
  113. package/src/runtime/thread/conversation-export.ts +135 -0
  114. package/src/runtime/thread/message-days.ts +120 -0
  115. package/src/runtime/thread/message-processor.ts +93 -0
  116. package/src/runtime/thread/task-utils.ts +11 -0
  117. package/src/runtime/thread/thought-utils.ts +752 -0
  118. package/src/runtime/thread/thread-history.ts +139 -0
  119. package/src/runtime/thread/timeline.ts +202 -0
  120. package/src/runtime/thread/title-tags.ts +8 -0
  121. package/src/runtime/thread/tool-cards.ts +383 -0
  122. package/src/runtime/turn-failure.ts +108 -0
  123. package/src/runtime/types.ts +148 -0
  124. package/src/runtime/usage-limit-error.ts +44 -0
  125. package/src/runtime/use-agent-activity.ts +80 -0
  126. package/src/runtime/use-conversation-stream.ts +1427 -0
  127. package/src/runtime/use-conversation-thread.ts +556 -0
  128. package/src/runtime/use-start-conversation.ts +107 -0
  129. package/src/runtime/use-thread-history.ts +64 -0
  130. package/src/runtime/use-thread-search.ts +95 -0
  131. package/src/runtime/use-turn-edit.ts +99 -0
  132. package/src/tools.ts +2 -2
  133. package/src/types.ts +18 -47
  134. package/src/components/AliaChatMessageList.tsx +0 -532
  135. package/src/components/ThinkingBlock.tsx +0 -43
  136. package/src/hooks/useAliaChat.ts +0 -573
  137. package/src/lib/catalogue.ts +0 -236
  138. package/src/lib/chat-stream.ts +0 -655
  139. package/src/lib/chat-transport.ts +0 -98
  140. package/src/lib/delegation.ts +0 -34
  141. package/src/lib/work-log-copy.ts +0 -61
@@ -0,0 +1,64 @@
1
+ import { useInfiniteQuery } from '@tanstack/react-query';
2
+ import { useMemo } from 'react';
3
+ import { useAliaApi } from './api';
4
+ import { errorStatus } from './error-utils';
5
+ import { aliaQueryKeys } from './query-keys';
6
+ import { AGENT_ROUTES } from './routes';
7
+ import { type ThreadPage, threadHistory } from './thread/thread-history';
8
+
9
+ /**
10
+ * Everything said in this thread before the conversation on screen, a page at a time, going back.
11
+ *
12
+ * The screen streams into the most recent conversation of the thread and knows nothing older;
13
+ * the thread's messages route is what crosses the seams into the stretches before it. Pages are
14
+ * pulled as the reader approaches the top, never eagerly — a thread has no end going backwards,
15
+ * and the first page is already everything a reader who does not scroll will see.
16
+ *
17
+ * `nextCursor` is what says whether anything older remains, and it is the server's answer rather
18
+ * than a count: a page that comes back empty after its live messages are dropped is ordinary, and
19
+ * treating that as the end would stop the history dead on any thread whose current conversation
20
+ * is long.
21
+ */
22
+
23
+ /** How many messages a page carries. The server clamps its own maximum. */
24
+ const PAGE_SIZE = 50;
25
+
26
+ /** A 404 is the answer — the agent does not exist or is not the person's, and asking again does not change it. */
27
+ export function retryThreadRead(failureCount: number, error: unknown): boolean {
28
+ return errorStatus(error) !== 404 && failureCount < 2;
29
+ }
30
+
31
+ export function useThreadHistory(
32
+ handle: string | undefined,
33
+ activeConversationId: string | undefined,
34
+ ) {
35
+ const api = useAliaApi();
36
+ const query = useInfiniteQuery({
37
+ queryKey: aliaQueryKeys.threads.messages(handle ?? ''),
38
+ queryFn: async ({ pageParam }): Promise<ThreadPage> => {
39
+ const { data } = await api.get<ThreadPage>(AGENT_ROUTES.threadMessages(handle ?? ''), {
40
+ params: { limit: PAGE_SIZE, before: pageParam },
41
+ });
42
+ return data;
43
+ },
44
+ initialPageParam: undefined as string | undefined,
45
+ getNextPageParam: (lastPage) => lastPage.nextCursor ?? undefined,
46
+ // Nothing to page through until the thread has answered which conversation is the live one:
47
+ // without it every message would be history, including the ones already on screen.
48
+ enabled: (handle?.length ?? 0) > 0 && activeConversationId !== undefined,
49
+ retry: retryThreadRead,
50
+ });
51
+
52
+ const messages = useMemo(
53
+ () => threadHistory(query.data?.pages ?? [], activeConversationId ?? ''),
54
+ [query.data?.pages, activeConversationId],
55
+ );
56
+
57
+ return {
58
+ messages,
59
+ /** Whether a page older than the ones held remains to be asked for. */
60
+ hasMore: query.hasNextPage,
61
+ isLoadingMore: query.isFetchingNextPage,
62
+ loadMore: query.fetchNextPage,
63
+ };
64
+ }
@@ -0,0 +1,95 @@
1
+ import { useInfiniteQuery, useQuery } from '@tanstack/react-query';
2
+ import { useMemo } from 'react';
3
+ import { useAliaApi } from './api';
4
+ import { aliaQueryKeys } from './query-keys';
5
+ import { AGENT_ROUTES } from './routes';
6
+ import {
7
+ type ThreadMessage,
8
+ type ThreadPage,
9
+ type ThreadSearchHit,
10
+ threadHistory,
11
+ } from './thread/thread-history';
12
+ import { retryThreadRead } from './use-thread-history';
13
+
14
+ /**
15
+ * Finding something said earlier in a thread, and getting back to it.
16
+ *
17
+ * Two halves, and the second is why the first is worth anything: a hit tells you the message
18
+ * exists, and its CURSOR is what opens the stretch of thread around it — however many
19
+ * conversations back it lives. Without that, a result list is a wall you can read and not reach.
20
+ */
21
+
22
+ /** How many hits to ask for. The server clamps its own maximum. */
23
+ const HIT_LIMIT = 30;
24
+
25
+ export function useThreadSearch(handle: string | undefined, query: string) {
26
+ const api = useAliaApi();
27
+ const trimmed = query.trim();
28
+
29
+ return useQuery({
30
+ queryKey: aliaQueryKeys.threads.search(handle ?? '', trimmed),
31
+ queryFn: async (): Promise<ThreadSearchHit[]> => {
32
+ const { data } = await api.get<{ hits: ThreadSearchHit[] }>(
33
+ AGENT_ROUTES.threadSearch(handle ?? ''),
34
+ { params: { q: trimmed, limit: HIT_LIMIT } },
35
+ );
36
+ return data.hits;
37
+ },
38
+ // An empty query is not a search, and asking for one would return the thread's most recent
39
+ // everything under the guise of a result.
40
+ enabled: (handle?.length ?? 0) > 0 && trimmed.length > 0,
41
+ // What was said cannot change, so a query already asked is answered from the cache.
42
+ staleTime: 1000 * 60 * 5,
43
+ retry: retryThreadRead,
44
+ });
45
+ }
46
+
47
+ /** How many messages a window carries, half of them before the hit. */
48
+ const WINDOW_SIZE = 40;
49
+
50
+ /**
51
+ * The stretch of thread around one message, opened from a search hit.
52
+ *
53
+ * `at=` rather than `before=`: `before` is exclusive, so the one message the window exists to
54
+ * reveal would be the one missing from it.
55
+ *
56
+ * Reading further back from here is the same paging as anywhere else — the window's own oldest
57
+ * cursor continues it — so this is an infinite query too, and only its first page is addressed
58
+ * differently. There is no paging FORWARD: the way back to the present is leaving the window, not
59
+ * walking to it.
60
+ */
61
+ export function useThreadWindow(handle: string | undefined, cursor: string | null) {
62
+ const api = useAliaApi();
63
+ const query = useInfiniteQuery({
64
+ queryKey: aliaQueryKeys.threads.window(handle ?? '', cursor ?? ''),
65
+ queryFn: async ({ pageParam }): Promise<ThreadPage> => {
66
+ const { data } = await api.get<ThreadPage>(AGENT_ROUTES.threadMessages(handle ?? ''), {
67
+ params:
68
+ pageParam === undefined
69
+ ? { limit: WINDOW_SIZE, at: cursor ?? undefined }
70
+ : { limit: WINDOW_SIZE, before: pageParam },
71
+ });
72
+ return data;
73
+ },
74
+ initialPageParam: undefined as string | undefined,
75
+ getNextPageParam: (lastPage) => lastPage.nextCursor ?? undefined,
76
+ enabled: (handle?.length ?? 0) > 0 && cursor !== null,
77
+ retry: retryThreadRead,
78
+ });
79
+
80
+ // Nothing is dropped here, unlike the tail: a window is a view of the PAST, and the live
81
+ // conversation is not on screen underneath it to be duplicated. The empty string is a
82
+ // conversation id no message has.
83
+ const messages: ThreadMessage[] = useMemo(
84
+ () => threadHistory(query.data?.pages ?? [], ''),
85
+ [query.data?.pages],
86
+ );
87
+
88
+ return {
89
+ messages,
90
+ hasMore: query.hasNextPage,
91
+ isLoadingMore: query.isFetchingNextPage,
92
+ loadMore: query.fetchNextPage,
93
+ isLoading: query.isPending && cursor !== null,
94
+ };
95
+ }
@@ -0,0 +1,99 @@
1
+ import { useCallback, useRef } from 'react';
2
+ import { getTextFromContent } from '../lib/content-utils';
3
+ import type { Message, SendOptions } from './types';
4
+ import type { AliaAttachment } from '../types';
5
+ import { useComposerDraft, useComposerDraftStore, type DraftTarget } from './composer-draft-store';
6
+
7
+ export interface TurnEditOptions {
8
+ /** The composer the edit happens in: this chat's draft. */
9
+ target: DraftTarget;
10
+ messages: readonly Message[];
11
+ /** The options a question was sent with, when this screen sent it. */
12
+ turnOptionsOf?: (userMessageId: string) => SendOptions | undefined;
13
+ /** Send the rewrite in place of the question (`useChatConversation.editMessage`). */
14
+ onEditMessage?: (
15
+ messageId: string,
16
+ text: string,
17
+ options?: SendOptions,
18
+ attachments?: AliaAttachment[],
19
+ ) => Promise<boolean>;
20
+ }
21
+
22
+ /**
23
+ * Rewriting a question already sent, in the composer (#608 §7).
24
+ *
25
+ * Before the template this was an "Editing message" strip over the input, with
26
+ * the id in the screen's own `useState` — so leaving the chat and coming back
27
+ * forgot the edit and the next send became a new turn. The edit lives in the
28
+ * chat's draft now (`composer-draft-store.ts`), like the text it is made of:
29
+ * per account and per conversation, and gone when the draft is.
30
+ *
31
+ * - `start` loads the question's words and the connector and skills it was sent
32
+ * with into the composer, keeping aside what the composer held. A question
33
+ * this screen did not send has no record of its options, so the composer's
34
+ * own choice stays.
35
+ * - `cancel` gives the composer back what it held.
36
+ * - `submit` sends the rewrite; a failure leaves the composer still editing,
37
+ * with the words back in it (the send path restores those).
38
+ *
39
+ * `editing` is the edit only while its question is still in the thread: a
40
+ * conversation cleared, or cut back by another edit, takes it away, and the
41
+ * composer goes back to writing a new turn.
42
+ */
43
+ export function useTurnEdit({ target, messages, turnOptionsOf, onEditMessage }: TurnEditOptions) {
44
+ const draftEdit = useComposerDraft(target).editing;
45
+ const editing =
46
+ draftEdit !== undefined && messages.some((m) => m.id === draftEdit.messageId)
47
+ ? draftEdit
48
+ : undefined;
49
+
50
+ // Read through a ref: `start` is a prop of every row in the thread, and an
51
+ // identity that changed per streamed token would re-render all of them.
52
+ const latestMessages = useRef(messages);
53
+ latestMessages.current = messages;
54
+
55
+ const start = useCallback(
56
+ (messageId: string) => {
57
+ const message = latestMessages.current.find((m) => m.id === messageId);
58
+ if (message === undefined) return;
59
+ const store = useComposerDraftStore.getState();
60
+ const current = store.drafts[target ?? ''];
61
+ const sentWith = turnOptionsOf?.(messageId);
62
+ store.startEdit(store.address(target), {
63
+ messageId,
64
+ text: getTextFromContent(message.content),
65
+ mcpServerId: sentWith?.mcpServerId ?? current?.mcpServerId ?? null,
66
+ skillNames: sentWith?.skillNames ?? current?.skillNames ?? [],
67
+ });
68
+ },
69
+ [target, turnOptionsOf],
70
+ );
71
+
72
+ const cancel = useCallback(() => {
73
+ const store = useComposerDraftStore.getState();
74
+ store.cancelEdit(store.address(target));
75
+ }, [target]);
76
+
77
+ /** Send `text` in place of the question being edited. The caller has cleared the draft. */
78
+ const submit = useCallback(
79
+ async (
80
+ text: string,
81
+ options?: SendOptions,
82
+ attachments?: AliaAttachment[],
83
+ ): Promise<boolean> => {
84
+ if (editing === undefined || onEditMessage === undefined) return false;
85
+ const address = useComposerDraftStore.getState().address(target);
86
+ const sent = await onEditMessage(editing.messageId, text, options, attachments);
87
+ if (!sent) useComposerDraftStore.getState().restore(address, { editing });
88
+ return sent;
89
+ },
90
+ [editing, onEditMessage, target],
91
+ );
92
+
93
+ return {
94
+ editing: onEditMessage === undefined ? undefined : editing,
95
+ start: onEditMessage === undefined ? undefined : start,
96
+ cancel,
97
+ submit,
98
+ };
99
+ }
package/src/tools.ts CHANGED
@@ -22,5 +22,5 @@ export type { AskedOption, AskedQuestion, QuestionAnswer } from './lib/questions
22
22
  export { classifyIntake, MAX_ATTACHMENT_BYTES } from './lib/attachment-intake';
23
23
  export { toolCallStatus } from './lib/tool-call-status';
24
24
  export type { ToolCallStatus } from './lib/tool-call-status';
25
- export { englishWorkLog } from './lib/work-log-copy';
26
- export type { Translate } from './lib/work-log-copy';
25
+ export { englishCopy } from './lib/copy';
26
+ export type { Translate } from './lib/copy';
package/src/types.ts CHANGED
@@ -1,4 +1,3 @@
1
- import type { ComponentType } from 'react';
2
1
  import type { SharedValue } from 'react-native-reanimated';
3
2
  import type { VoiceErrorCode } from './lib/speech-messages';
4
3
  import type { VoiceCloseReason } from './lib/voice-protocol';
@@ -9,6 +8,8 @@ import type { VoiceCloseReason } from './lib/voice-protocol';
9
8
  export interface AliaSuggestion {
10
9
  id: string;
11
10
  text: string;
11
+ /** It has blanks to fill: picking it puts it in the composer for the person to finish, instead of sending it. */
12
+ template?: boolean;
12
13
  }
13
14
 
14
15
  // ── Composer Types ──
@@ -51,43 +52,10 @@ export interface PlanStep {
51
52
  toolName?: string;
52
53
  }
53
54
 
54
- export interface PendingPlan {
55
+ /** A plan the agent shows before it works (`alia.plan_preview`). Shown only: nothing waits on it. */
56
+ export interface MessagePlan {
55
57
  planId: string;
56
58
  steps: PlanStep[];
57
- approved: boolean;
58
- rejected: boolean;
59
- }
60
-
61
- export interface ChatMessage {
62
- id: string;
63
- role: 'user' | 'assistant' | 'system';
64
- content: string | Array<{ type: string; [key: string]: unknown }>;
65
- toolInvocations?: ToolInvocation[];
66
- createdAt: number;
67
- /** Extended thinking / reasoning content */
68
- thinking?: string;
69
- /** Deep research progress metadata */
70
- researchProgress?: ResearchProgress;
71
- /** Plan preview data (pending approval) */
72
- pendingPlan?: PendingPlan;
73
- /** Message origin: text chat or voice */
74
- source?: 'text' | 'voice';
75
- /** Speaker identity in voice mode */
76
- speaker?: 'primary';
77
- /** Whether a voice transcript is still streaming */
78
- isStreaming?: boolean;
79
- /** Delegated agent identity */
80
- /** `character` is the agent's look as the server stores it (JSON), parsed where it is drawn. */
81
- agentInfo?: { id: string; name: string; character?: unknown };
82
- }
83
-
84
- export interface ToolInvocation {
85
- toolName: string;
86
- state: 'call' | 'partial-call' | 'result';
87
- args?: Record<string, unknown>;
88
- result?: unknown;
89
- /** OpenAI-format tool call ID for matching calls to results */
90
- toolCallId?: string;
91
59
  }
92
60
 
93
61
  // ── Voice Types ──
@@ -173,18 +141,21 @@ export interface VoiceSessionProps {
173
141
  locale?: string;
174
142
  /** Which of the product's two voices answers. */
175
143
  voice?: 'male' | 'female';
176
- /** Called on every room, agent, or transcript change. */
177
- onStateChange: (state: VoiceSessionState) => void;
144
+ /** Alia stored messages of the call in the conversation: refresh the thread. */
145
+ onMessagesSaved?: (conversationId: string, messageIds: readonly string[]) => void;
178
146
  /**
179
- * The call is over — the user ended it, it errored, or the room dropped. When it
180
- * failed, say why, so the shell can tell the person instead of silently going back.
147
+ * What the call tells the person, in the chat's language (`labels`): a failure, a call moved
148
+ * to another device, the time limit near. Omitted, Bloom's toast.
181
149
  */
182
- onEnd: (error?: string) => void;
150
+ onNotice?: (notice: VoiceNotice) => void;
151
+ /** Called on every room, agent, or transcript change. */
152
+ onStateChange: (state: VoiceSessionState) => void;
153
+ /** The call is over: hung up, failed, ended by the server or dropped. The person was told why. */
154
+ onEnd: () => void;
183
155
  }
184
156
 
185
- /**
186
- * The voice capability handed to `AliaChatScreen` / `AliaChatSheet`. Pass
187
- * `VoiceSession` from `@alia.onl/sdk/voice` to enable voice calls; leave it out
188
- * and the chat is text-only, with the audio engine absent from the module graph.
189
- */
190
- export type VoiceSessionComponent = ComponentType<VoiceSessionProps>;
157
+ /** Something the call tells the person: a failure, how it ended, or that it is about to end. */
158
+ export interface VoiceNotice {
159
+ kind: 'error' | 'info' | 'warning';
160
+ text: string;
161
+ }