@alia.onl/sdk 21.3.2 → 21.5.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,42 @@
1
1
  # @alia.onl/sdk
2
2
 
3
+ ## 21.5.0
4
+
5
+ ### Fixed
6
+
7
+ - **The SDK no longer ships every Oxy account screen to the app.** The host's app assertion
8
+ (`useAppAssertion`) was read from `@oxy.so/services/ui`, the only entry that exported it, and
9
+ that entry registers every account screen at import (with Bloom's phone flags and the rest), so
10
+ every app using the SDK carried them in its first-paint JS: +0.58 MiB in CrowdSource's reviewer.
11
+ It now comes from the lean `@oxy.so/services/ui/client`, like `useOxy` and `useAuth`.
12
+ `check:entries` refuses `@oxy.so/services/ui` from every entry, as it already refused the root.
13
+
14
+ ### Changed
15
+
16
+ - The `@oxy.so/services` peer is `>=12.1.0`, the first release whose `ui/client` exports
17
+ `useAppAssertion`.
18
+
19
+ ## 21.4.0
20
+
21
+ ### Added
22
+
23
+ - **`@alia.onl/sdk/text` and `@alia.onl/sdk/ask/text`: the chat without bundling the voice
24
+ call.** `voice={false}` hid the call, but its code still shipped: since 21.3.2 the web build
25
+ inlines the call into the chat's chunk, so a text-only app paid for it. The new entries export
26
+ the same `AliaChatSheet`, `AliaChatScreen` and `AliaChatContent`, and `AliaAskProvider` /
27
+ `useAskAgent` with a lazily loaded sheet. None of them reaches the call's modules (its socket,
28
+ protocol and audio engine), and they never offer a call. Read-aloud and dictation stay.
29
+ CrowdSource's reviewer, which switched `AliaChatSheet` to `@alia.onl/sdk/text`: lazy sheet
30
+ chunk 1,455,831 → 1,357,287 bytes (413.8 → 374.2 KB gzip), `__common.js` 3,978,158 →
31
+ 3,962,370 bytes, no new chunk. The README has a table of which entry to import.
32
+
33
+ ### Changed
34
+
35
+ - `AliaChatContent` no longer imports the call. It draws whatever call `VoiceCallContext` hands it,
36
+ and the root entry and `@alia.onl/sdk/ask` export their chat through `withVoiceCall`, so those
37
+ two behave as before. `check:entries` checks that the text entries never reach the call, on any
38
+ platform, and draw no async boundary on web other than the ask sheet's.
39
+
3
40
  ## 21.3.2
4
41
 
5
42
  ### Fixed
package/README.md CHANGED
@@ -143,6 +143,26 @@ The sheet, the screen and the Alia app are three frames around ONE conversation
143
143
  stop, retry, edit and clear conversations through the same hooks, and a conversation started in
144
144
  one is the same conversation in the others.
145
145
 
146
+ ### Which entry to import
147
+
148
+ | You want | Import from | Bundles the voice call |
149
+ |---|---|---|
150
+ | The chat with the call (`AliaChatSheet`, `AliaChatScreen`, `AliaChatContent`) | `@alia.onl/sdk` | yes |
151
+ | The same chat, text only | `@alia.onl/sdk/text` | no |
152
+ | One "Ask" sheet at the app's root, loaded with the first `ask` | `@alia.onl/sdk/ask` | yes, in the sheet's chunk |
153
+ | The same, text only | `@alia.onl/sdk/ask/text` | no |
154
+ | Data only: conversations, the stream, the model | `@alia.onl/sdk/runtime`, `@alia.onl/sdk/model` | no |
155
+ | Her face on a button | `@alia.onl/sdk/avatar` | no |
156
+
157
+ `voice={false}` hides the call, but the code still ships: the root entry and `@alia.onl/sdk/ask`
158
+ bundle the call (its socket, protocol and audio engine) because their chat can open one. A
159
+ text-only app imports from `@alia.onl/sdk/text` or `@alia.onl/sdk/ask/text`. Their chat never
160
+ reaches those modules and never offers a call, whatever `voice` says. Read-aloud and dictation
161
+ are still there, because they belong to the thread and the composer. Types can still come from
162
+ `@alia.onl/sdk`, since type imports are erased. A value imported from the root brings the call
163
+ along with it. In CrowdSource's reviewer, `@alia.onl/sdk/text` made its lazy sheet chunk 98 KB
164
+ smaller (raw; 39 KB gzip).
165
+
146
166
  ## Any agent, from any app
147
167
 
148
168
  Every agent — Alia included — is the same agent (ADR 0020), and any Oxy app opens any agent the
@@ -181,7 +201,8 @@ stays out of the app's entry bundle. The root entry exports the same two, with t
181
201
  two async chunks share into `__common.js`, which loads at first paint. The SDK draws no async chunk
182
202
  boundary of its own on web (the call, sounds and pickers import statically there), except the one
183
203
  of `@alia.onl/sdk/ask`. So either mount the provider from `@alia.onl/sdk/ask`, or lazy-import your
184
- own module that imports the root entry — not both, or the chat they share lands in `__common.js`.
204
+ own module that imports the root entry (or `@alia.onl/sdk/text`). Don't do both, or the chat they
205
+ share lands in `__common.js`. The text-only entries draw no extra boundary either.
185
206
 
186
207
  The sheet offers the person's latest conversations with its lead under the greeting
187
208
  (`recentConversations={false}` turns it off). The code that configures the sheet sits above it, so
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alia.onl/sdk",
3
- "version": "21.3.2",
3
+ "version": "21.5.0",
4
4
  "description": "Alia AI chat SDK — the conversation runtime, chat UI, voice calls and dictation for Oxy apps",
5
5
  "main": "src/index.ts",
6
6
  "react-native": "src/index.ts",
@@ -9,11 +9,13 @@
9
9
  "exports": {
10
10
  ".": "./src/index.ts",
11
11
  "./ask": "./src/ask.ts",
12
+ "./ask/text": "./src/ask-text.ts",
12
13
  "./avatar": "./src/avatar.ts",
13
14
  "./content": "./src/lib/content-utils.ts",
14
15
  "./csp": "./src/csp.ts",
15
16
  "./model": "./src/runtime/model.ts",
16
17
  "./runtime": "./src/runtime/index.ts",
18
+ "./text": "./src/text.ts",
17
19
  "./tools": "./src/tools.ts",
18
20
  "./voice": "./src/voice.ts",
19
21
  "./package.json": "./package.json"
@@ -54,7 +56,7 @@
54
56
  "@expo/vector-icons": "^15.1.1",
55
57
  "@oxy.so/bloom": ">=10.21.0",
56
58
  "@oxy.so/core": ">=3.0.0",
57
- "@oxy.so/services": ">=11.12.0",
59
+ "@oxy.so/services": ">=12.1.0",
58
60
  "@tanstack/react-query": "^5.101",
59
61
  "@react-native-async-storage/async-storage": "^2.2.0",
60
62
  "expo-blur": "^57.0.0 || ^58.0.0",
@@ -0,0 +1,10 @@
1
+ // @alia.onl/sdk/ask/text — `@alia.onl/sdk/ask` without the voice call, and without bundling it
2
+ // The provider and its hook; the sheet loads with the first `ask`, and neither reaches the call's
3
+ // modules. `useAskAgent` is the same hook as `@alia.onl/sdk/ask`'s.
4
+
5
+ export { AliaAskProvider, useAskAgent } from './components/AliaAskProviderText';
6
+ export type {
7
+ AliaAsk,
8
+ AliaAskAgents,
9
+ AliaAskProviderProps,
10
+ } from './components/AliaAskProviderText';
@@ -10,7 +10,7 @@
10
10
  */
11
11
 
12
12
  import { createAskProvider } from './AliaAskCore';
13
- import { AliaAskSheet } from './AliaAskSheet';
13
+ import { AliaAskSheet } from './AliaAskSheetWithVoice';
14
14
 
15
15
  export type { AliaAsk, AliaAskAgents, AliaAskProviderProps } from './AliaAskCore';
16
16
  export { useAskAgent } from './AliaAskCore';
@@ -4,15 +4,15 @@
4
4
  * Signed in, the sheet's module is fetched in the background so the first `ask` does not wait on
5
5
  * the network.
6
6
  *
7
- * This `import()` is the ONE async boundary the SDK draws, and only for this entry: everything
8
- * behind it (`AliaAskSheet` and the chat) imports statically on web, so Metro emits one chunk for
7
+ * This `import()` is the ONE async boundary the SDK draws, and only for this entry (and
8
+ * `@alia.onl/sdk/ask/text`): everything behind it (`AliaAskSheetWithVoice` and the chat) imports statically on web, so Metro emits one chunk for
9
9
  * the sheet and hoists nothing of it into `__common.js` (`scripts/check-entry-isolation.mjs`).
10
10
  */
11
11
 
12
12
  import { lazy, Suspense } from 'react';
13
13
  import { type AskSheetComponent, createAskProvider } from './AliaAskCore';
14
14
 
15
- const loadSheet = () => import('./AliaAskSheet');
15
+ const loadSheet = () => import('./AliaAskSheetWithVoice');
16
16
  const LazyAskSheet = lazy(() => loadSheet().then((module) => ({ default: module.AliaAskSheet })));
17
17
 
18
18
  const SuspendedAskSheet: AskSheetComponent = (props) => (
@@ -0,0 +1,25 @@
1
+ /**
2
+ * The ask provider of `@alia.onl/sdk/ask/text`: `AliaAskProviderLazy` without the voice call. Its
3
+ * sheet is loaded lazily, with the first `ask`, and neither the provider nor the sheet reaches the
4
+ * call's modules, so a text-only app bundles none of them.
5
+ *
6
+ * Its `import()` is the one async boundary of this entry, as in `AliaAskProviderLazy`: behind it
7
+ * everything imports statically on web (`scripts/check-entry-isolation.mjs`).
8
+ */
9
+
10
+ import { lazy, Suspense } from 'react';
11
+ import { type AskSheetComponent, createAskProvider } from './AliaAskCore';
12
+
13
+ const loadSheet = () => import('./AliaAskSheet');
14
+ const LazyAskSheet = lazy(() => loadSheet().then((module) => ({ default: module.AliaAskSheet })));
15
+
16
+ const SuspendedAskSheet: AskSheetComponent = (props) => (
17
+ <Suspense fallback={null}>
18
+ <LazyAskSheet {...props} />
19
+ </Suspense>
20
+ );
21
+
22
+ export type { AliaAsk, AliaAskAgents, AliaAskProviderProps } from './AliaAskCore';
23
+ export { useAskAgent } from './AliaAskCore';
24
+
25
+ export const AliaAskProvider = createAskProvider(SuspendedAskSheet, loadSheet);
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The ask sheet with the voice call: what the root entry's `AliaAskProvider` imports and
3
+ * `@alia.onl/sdk/ask` loads with the first `ask`. `@alia.onl/sdk/ask/text` loads `AliaAskSheet`
4
+ * itself, which never reaches the call.
5
+ */
6
+
7
+ import { AliaAskSheet as TextAskSheet } from './AliaAskSheet';
8
+ import { withVoiceCall } from './voice/withVoiceCall';
9
+
10
+ export const AliaAskSheet = withVoiceCall(TextAskSheet, 'AliaAskSheet');
@@ -7,8 +7,9 @@
7
7
  * (`useChatComposer`, `useChatVoice`, `useThreadView`), and this, which draws it — the thread
8
8
  * (`AliaThread`) and the agent's history behind it, the composer with its levels, suggestions and
9
9
  * questions, and the voice call: the button that opens it, the bar that replaces the composer, the
10
- * notices and the transcript in the thread. The call loads on the first call, so nothing here
11
- * reaches its audio engine at module-evaluation time.
10
+ * notices and the transcript in the thread. The call itself is not imported here: the entry that
11
+ * bundles it hands it down (`VoiceCallContext`, `withVoiceCall`), so the text-only entries
12
+ * (`@alia.onl/sdk/text`, `@alia.onl/sdk/ask/text`) never reach its modules.
12
13
  *
13
14
  * What a host brings is data and places: the conversation to show, the chrome around it
14
15
  * (`chrome`), its rows in the add menu, its words (`labels`), where the thought panel and a
@@ -30,7 +31,15 @@ import { RiEditLine } from '@oxy.so/bloom/icons/RiEditLine';
30
31
  import { toast } from '@oxy.so/bloom/toast';
31
32
  import { Text } from '@oxy.so/bloom/typography';
32
33
  import { useQueryClient } from '@tanstack/react-query';
33
- import { type ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react';
34
+ import {
35
+ type ReactNode,
36
+ useCallback,
37
+ useContext,
38
+ useEffect,
39
+ useMemo,
40
+ useRef,
41
+ useState,
42
+ } from 'react';
34
43
  import { View } from 'react-native';
35
44
  import type { SharedValue } from 'react-native-reanimated';
36
45
  import { useAmbientWave } from '../hooks/useAmbientWave';
@@ -66,7 +75,7 @@ import { AmbientField } from './AmbientField';
66
75
  import { AliaThread } from './thread/AliaThread';
67
76
  import { ThreadSearch } from './thread/ThreadSearch';
68
77
  import { VoiceModeIcon } from './VoiceModeIcon';
69
- import { AliaVoiceCall } from './voice/AliaVoiceCall';
78
+ import { VoiceCallContext } from './voice/VoiceCallContext';
70
79
 
71
80
  /** The agent a chat is opened on: its lead instead of Alia. */
72
81
  export interface AliaChatAgentRef {
@@ -227,7 +236,8 @@ export interface AliaChatContentProps {
227
236
  */
228
237
  renderUsageLimit?: (limit: { error: UsageLimitError; dismiss: () => void }) => ReactNode;
229
238
  /**
230
- * The chat's voice call. On by default; `false` makes the chat text-only. An object brings
239
+ * The chat's voice call. On by default where the call is bundled (the root entry and
240
+ * `@alia.onl/sdk/ask`); the text-only entries have none. `false` makes the chat text-only. An object brings
231
241
  * what only the host knows: the gate before a call (`canStartVoice`, `onVoiceBlocked`), what
232
242
  * to refresh when one ends (`onCallEnded`), where its notices go (`onNotice`), the sounds it
233
243
  * plays. The call always lives in the chat's conversation, made when it starts if the chat has
@@ -405,8 +415,10 @@ function AliaConversationView({
405
415
  onSuggestionPicked,
406
416
  presentedProgress,
407
417
  });
418
+ // Without a call handed down (a text-only entry), the chat is text-only whatever `voice` says.
419
+ const VoiceCall = useContext(VoiceCallContext);
408
420
  const voice = useChatVoice({
409
- options: voiceOptions,
421
+ options: VoiceCall === null ? false : voiceOptions,
410
422
  conversationId,
411
423
  agentId,
412
424
  isFocused,
@@ -600,8 +612,8 @@ function AliaConversationView({
600
612
 
601
613
  const composerNode = (
602
614
  <>
603
- {voice.enabled && voice.isActive ? (
604
- <AliaVoiceCall {...voice.launcher.sessionProps} />
615
+ {VoiceCall !== null && voice.enabled && voice.isActive ? (
616
+ <VoiceCall {...voice.launcher.sessionProps} />
605
617
  ) : (
606
618
  <AliaComposer
607
619
  // A different account is a different composer: reads in flight for the last one are
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The chat components of the root entry: each draws the voice call (`withVoiceCall`). The same
3
+ * components without it — and without bundling it — are `@alia.onl/sdk/text`.
4
+ */
5
+
6
+ import { AliaChatContent as TextChatContent } from './AliaChatContent';
7
+ import { AliaChatScreen as TextChatScreen } from './AliaChatScreen';
8
+ import { AliaChatSheet as TextChatSheet } from './AliaChatSheet';
9
+ import { withVoiceCall } from './voice/withVoiceCall';
10
+
11
+ export const AliaChatContent = withVoiceCall(TextChatContent, 'AliaChatContent');
12
+ export const AliaChatSheet = withVoiceCall(TextChatSheet, 'AliaChatSheet');
13
+ export const AliaChatScreen = withVoiceCall(TextChatScreen, 'AliaChatScreen');
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The voice call a conversation can open, handed down by the entry that bundles it.
3
+ *
4
+ * `AliaChatContent` draws the call through this rather than importing it, so a text-only entry
5
+ * (`@alia.onl/sdk/text`, `@alia.onl/sdk/ask/text`) never reaches the call's modules — its socket,
6
+ * protocol and audio engine. The root and `@alia.onl/sdk/ask` provide `AliaVoiceCall` around their
7
+ * chat (`withVoiceCall`); without a provider the chat is text-only, whatever its `voice` prop says.
8
+ */
9
+
10
+ import { type ComponentType, createContext } from 'react';
11
+ import type { VoiceSessionProps } from '../../types';
12
+
13
+ export const VoiceCallContext = createContext<ComponentType<VoiceSessionProps> | null>(null);
@@ -0,0 +1,25 @@
1
+ /**
2
+ * A chat component with the voice call: `Component` drawn under `VoiceCallContext`, which hands
3
+ * it `AliaVoiceCall`. The root entry and `@alia.onl/sdk/ask` export their chat through this; the
4
+ * text-only entries export the components as they are.
5
+ */
6
+
7
+ import type { ComponentType } from 'react';
8
+ import { AliaVoiceCall } from './AliaVoiceCall';
9
+ import { VoiceCallContext } from './VoiceCallContext';
10
+
11
+ export function withVoiceCall<P extends object>(
12
+ Component: ComponentType<P>,
13
+ displayName: string,
14
+ ): ComponentType<P> {
15
+ // A ref is a prop of a function component (React 19), so it reaches a `forwardRef` `Component`.
16
+ function WithVoiceCall(props: P) {
17
+ return (
18
+ <VoiceCallContext.Provider value={AliaVoiceCall}>
19
+ <Component {...props} />
20
+ </VoiceCallContext.Provider>
21
+ );
22
+ }
23
+ WithVoiceCall.displayName = displayName;
24
+ return WithVoiceCall;
25
+ }
package/src/index.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  // Reusable chat UI, voice, and streaming components for Oxy apps
4
4
 
5
5
  // ── Main components ──
6
- export { AliaChatContent } from './components/AliaChatContent';
6
+ export { AliaChatContent } from './components/AliaChatWithVoice';
7
7
  export type {
8
8
  AliaChatAgentRef,
9
9
  AliaChatChrome,
@@ -25,11 +25,11 @@ export { AliaSharedConversation } from './components/AliaSharedConversation';
25
25
  export type { AliaSharedConversationProps } from './components/AliaSharedConversation';
26
26
  export type { AliaAddMenu } from './lib/add-menu';
27
27
  export type { AliaVoiceOptions } from './hooks/useChatVoice';
28
- export { AliaChatSheet } from './components/AliaChatSheet';
28
+ export { AliaChatSheet } from './components/AliaChatWithVoice';
29
29
  export type { AliaChatSheetProps, AliaChatSheetRef } from './components/AliaChatSheet';
30
30
  export { AliaRecentConversations } from './components/AliaRecentConversations';
31
31
  export type { AliaTurn } from './hooks/useChatComposer';
32
- export { AliaChatScreen } from './components/AliaChatScreen';
32
+ export { AliaChatScreen } from './components/AliaChatWithVoice';
33
33
  export type { AliaChatScreenProps } from './components/AliaChatScreen';
34
34
 
35
35
  // ── Chat UI components ──
@@ -1,6 +1,4 @@
1
- // `@oxy.so/services/ui`, not the root barrel (which ships the sign-in panels, `check:entries`):
2
- // `ui/client` does not export `useAppAssertion`.
3
- import { useAppAssertion } from '@oxy.so/services/ui';
1
+ import { useAppAssertion } from '@oxy.so/services/ui/client';
4
2
  import { useMemo } from 'react';
5
3
 
6
4
  /**
package/src/text.ts ADDED
@@ -0,0 +1,18 @@
1
+ // @alia.onl/sdk/text — the chat without the voice call, and without bundling it
2
+ // The same `AliaChatSheet`, `AliaChatScreen` and `AliaChatContent` as the root entry, but none of
3
+ // them reaches the call's modules (its socket, protocol and audio engine): a text-only app takes
4
+ // its chat from here. Read-aloud and dictation stay — they are the composer's and the thread's.
5
+ // Types can be imported from the root entry (they are erased); values from the root would bring
6
+ // the call along.
7
+
8
+ export { AliaChatContent } from './components/AliaChatContent';
9
+ export type {
10
+ AliaChatAgentRef,
11
+ AliaChatChrome,
12
+ AliaChatContentProps,
13
+ AliaChatState,
14
+ } from './components/AliaChatContent';
15
+ export { AliaChatSheet } from './components/AliaChatSheet';
16
+ export type { AliaChatSheetProps, AliaChatSheetRef } from './components/AliaChatSheet';
17
+ export { AliaChatScreen } from './components/AliaChatScreen';
18
+ export type { AliaChatScreenProps } from './components/AliaChatScreen';