@alia.onl/sdk 15.8.0 → 19.1.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 +262 -13
- package/README.md +223 -151
- package/package.json +16 -6
- package/src/animations.ts +0 -1
- package/src/components/AliaAskProvider.tsx +127 -0
- package/src/components/AliaChatContent.tsx +604 -392
- package/src/components/AliaChatHeader.tsx +14 -38
- package/src/components/AliaChatScreen.tsx +56 -31
- package/src/components/AliaChatSheet.tsx +110 -36
- package/src/components/AliaComposer.tsx +1 -1
- package/src/components/AliaParticipants.tsx +202 -0
- package/src/components/AliaParticipantsHeader.tsx +60 -0
- package/src/components/AliaSharedConversation.tsx +86 -0
- package/src/components/AliaWelcomeMessage.tsx +18 -5
- package/src/components/Image.tsx +12 -0
- package/src/components/Markdown.tsx +498 -14
- package/src/components/PlanPreviewCard.tsx +7 -49
- package/src/components/ReplyMarkdown.tsx +359 -0
- package/src/components/RichBlocks.tsx +195 -0
- package/src/components/canvas/CanvasComponent.tsx +59 -0
- package/src/components/canvas/ChartRenderer.tsx +73 -0
- package/src/components/canvas/CodeRenderer.tsx +24 -0
- package/src/components/canvas/FormRenderer.tsx +101 -0
- package/src/components/canvas/MarkdownRenderer.tsx +14 -0
- package/src/components/canvas/TableRenderer.tsx +47 -0
- package/src/components/thought/FilesAndSources.tsx +120 -0
- package/src/components/thought/ThoughtPanel.tsx +417 -0
- package/src/components/thought/ToolStep.tsx +141 -0
- package/src/components/thread/AgentResultCard.tsx +125 -0
- package/src/components/thread/AgentTaskCard.tsx +127 -0
- package/src/components/thread/AliaThread.tsx +401 -0
- package/src/components/thread/MessageBlockBoundary.tsx +78 -0
- package/src/components/thread/NewConversationOffer.tsx +54 -0
- package/src/components/thread/ThreadRow.tsx +458 -0
- package/src/components/thread/ThreadSearch.tsx +129 -0
- package/src/components/thread/ToolResultCard.tsx +254 -0
- package/src/components/thread/TurnMenu.tsx +54 -0
- package/src/components/thread/TurnStatusLine.tsx +34 -0
- package/src/components/voice/AliaVoiceCall.tsx +21 -0
- package/src/components/voice/VoiceControls.tsx +2 -28
- package/src/components/voice/VoiceSession.tsx +39 -11
- package/src/hooks/useAliaActivity.ts +2 -2
- package/src/hooks/useAtBottom.ts +51 -0
- package/src/hooks/useAttachMenu.ts +94 -0
- package/src/hooks/useChatComposer.ts +299 -0
- package/src/hooks/useChatVoice.ts +129 -0
- package/src/hooks/useDocumentPicker.ts +3 -29
- package/src/hooks/useFailedTurnToast.ts +43 -0
- package/src/hooks/useMessageVote.ts +50 -0
- package/src/hooks/useOpenThought.ts +79 -0
- package/src/hooks/usePendingQuestions.ts +2 -2
- package/src/hooks/useReadAloud.ts +92 -0
- package/src/hooks/useShareHandler.ts +24 -0
- package/src/hooks/useSoundEffects.ts +1 -1
- package/src/hooks/useThoughtScope.ts +59 -0
- package/src/hooks/useThreadRows.ts +145 -0
- package/src/hooks/useThreadView.ts +68 -0
- package/src/hooks/useTimelineWindow.ts +256 -0
- package/src/hooks/useVoiceLauncher.ts +180 -0
- package/src/hooks/useVoiceRoom.ts +4 -4
- package/src/index.ts +43 -29
- package/src/lib/activity-store.ts +1 -1
- package/src/lib/add-menu.ts +7 -0
- package/src/lib/call-thread.ts +33 -0
- package/src/lib/citations.ts +79 -2
- package/src/lib/clipboard.ts +23 -0
- package/src/lib/content-utils.ts +10 -0
- package/src/lib/copy-en.ts +284 -0
- package/src/lib/copy.ts +32 -0
- package/src/lib/document-picker.ts +31 -0
- package/src/lib/flights.ts +22 -13
- package/src/lib/focus-return.ts +66 -0
- package/src/lib/keyboard.native.tsx +19 -0
- package/src/lib/keyboard.tsx +9 -0
- package/src/lib/labels.tsx +69 -17
- package/src/lib/participants.ts +86 -0
- package/src/lib/power-level-options.ts +66 -0
- package/src/lib/questions.ts +2 -2
- package/src/lib/task-labels.ts +4 -4
- package/src/lib/tool-call-status.ts +1 -1
- package/src/lib/tool-icons.ts +36 -0
- package/src/lib/tool-renderers.tsx +48 -0
- package/src/lib/turn-state.ts +2 -2
- package/src/lib/voice-call-request.ts +49 -0
- package/src/lib/voice-call.ts +98 -35
- package/src/lib/voice-controls-labels.ts +31 -0
- package/src/lib/voice-protocol.ts +34 -15
- package/src/lib/work-log.ts +6 -6
- package/src/runtime/agents.ts +151 -0
- package/src/runtime/api.ts +141 -0
- package/src/runtime/app-assertion.ts +53 -0
- package/src/runtime/attachment-utils.ts +96 -0
- package/src/runtime/composer-draft-store.ts +270 -0
- package/src/runtime/config.tsx +186 -0
- package/src/runtime/conversation-store.ts +49 -0
- package/src/runtime/conversations.ts +557 -0
- package/src/runtime/error-utils.ts +104 -0
- package/src/runtime/host-tools.ts +140 -0
- package/src/runtime/index.ts +113 -0
- package/src/runtime/message-history.ts +124 -0
- package/src/runtime/model.ts +39 -0
- package/src/runtime/oxy-files.ts +37 -0
- package/src/runtime/power-levels.ts +66 -0
- package/src/runtime/query-keys.ts +34 -0
- package/src/runtime/random-uuid.ts +25 -0
- package/src/runtime/routes.ts +51 -0
- package/src/runtime/sharing.ts +165 -0
- package/src/runtime/sse-frame-reader.ts +88 -0
- package/src/runtime/stream-events.ts +146 -0
- package/src/runtime/stream-outcome.ts +21 -0
- package/src/runtime/thought-store.ts +103 -0
- package/src/runtime/thread/agent-activity.ts +261 -0
- package/src/runtime/thread/automation-labels.ts +79 -0
- package/src/runtime/thread/conversation-export.ts +135 -0
- package/src/runtime/thread/message-days.ts +120 -0
- package/src/runtime/thread/message-processor.ts +93 -0
- package/src/runtime/thread/task-utils.ts +11 -0
- package/src/runtime/thread/thought-utils.ts +752 -0
- package/src/runtime/thread/thread-history.ts +139 -0
- package/src/runtime/thread/timeline.ts +202 -0
- package/src/runtime/thread/title-tags.ts +8 -0
- package/src/runtime/thread/tool-cards.ts +383 -0
- package/src/runtime/turn-failure.ts +108 -0
- package/src/runtime/types.ts +148 -0
- package/src/runtime/usage-limit-error.ts +44 -0
- package/src/runtime/use-agent-activity.ts +80 -0
- package/src/runtime/use-conversation-stream.ts +1433 -0
- package/src/runtime/use-conversation-thread.ts +556 -0
- package/src/runtime/use-start-conversation.ts +107 -0
- package/src/runtime/use-thread-history.ts +64 -0
- package/src/runtime/use-thread-search.ts +95 -0
- package/src/runtime/use-turn-edit.ts +99 -0
- package/src/tools.ts +2 -2
- package/src/types.ts +18 -47
- package/src/components/AliaChatMessageList.tsx +0 -532
- package/src/components/ThinkingBlock.tsx +0 -43
- package/src/hooks/useAliaChat.ts +0 -573
- package/src/lib/catalogue.ts +0 -236
- package/src/lib/chat-stream.ts +0 -655
- package/src/lib/chat-transport.ts +0 -98
- package/src/lib/delegation.ts +0 -34
- package/src/lib/work-log-copy.ts +0 -61
|
@@ -7,19 +7,16 @@ import {
|
|
|
7
7
|
} from '@oxy.so/bloom/dropdown-menu';
|
|
8
8
|
import { RiArrowLeftLine, RiDeleteBin6Line, RiMoreFill, RiShare2Line } from '@oxy.so/bloom/icons';
|
|
9
9
|
import { PageHeader } from '@oxy.so/bloom/page-header';
|
|
10
|
-
import {
|
|
11
|
-
import { memo, type ReactNode, useMemo } from 'react';
|
|
12
|
-
import { readAgentCharacter } from '../lib/agent-character';
|
|
10
|
+
import { memo, type ReactNode } from 'react';
|
|
13
11
|
import { useLabels } from '../lib/labels';
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import type { AliaChatAgent } from './AliaChatContent';
|
|
12
|
+
import type { AliaChatParticipants } from '../lib/participants';
|
|
13
|
+
import { AliaParticipants } from './AliaParticipants';
|
|
17
14
|
|
|
18
15
|
export interface AliaChatHeaderProps {
|
|
19
16
|
/** The chat has messages: there is something to share and to clear. */
|
|
20
17
|
hasMessages: boolean;
|
|
21
|
-
/**
|
|
22
|
-
|
|
18
|
+
/** Who is in the conversation (`AliaChatState.participants`): the bar's title. */
|
|
19
|
+
participants: AliaChatParticipants;
|
|
23
20
|
/** Clears the conversation, from the menu. */
|
|
24
21
|
onClear: () => void;
|
|
25
22
|
/** Draws the back button at the start of the bar; omit where there is nowhere to go back to. */
|
|
@@ -32,44 +29,19 @@ export interface AliaChatHeaderProps {
|
|
|
32
29
|
leading?: ReactNode;
|
|
33
30
|
/** The host's own actions, before share and the menu. */
|
|
34
31
|
trailing?: ReactNode;
|
|
35
|
-
/** Pressing
|
|
32
|
+
/** Pressing the participants. Omitted, every avatar in it waves. */
|
|
36
33
|
onProfilePress?: () => void;
|
|
37
34
|
}
|
|
38
35
|
|
|
39
|
-
/**
|
|
40
|
-
* Alia's profile, Bloom's `AgentProfile`: her avatar above a name capsule, joined by the agents
|
|
41
|
-
* that answered ("Alia + Lumi + Atlas"). Pressing it makes every avatar wave.
|
|
42
|
-
*/
|
|
43
|
-
function AliaProfile({ agents, onPress }: { agents: AliaChatAgent[]; onPress?: () => void }) {
|
|
44
|
-
const flights = useAliaFlights();
|
|
45
|
-
const team = useMemo(
|
|
46
|
-
() => [
|
|
47
|
-
{ id: 'alia', name: 'Alia', avatar: ALIA_AVATAR },
|
|
48
|
-
...agents.map((agent) => ({ ...agent, avatar: readAgentCharacter(agent.character) })),
|
|
49
|
-
],
|
|
50
|
-
[agents],
|
|
51
|
-
);
|
|
52
|
-
return (
|
|
53
|
-
<AgentProfile
|
|
54
|
-
agents={team}
|
|
55
|
-
onPress={onPress ?? noop}
|
|
56
|
-
avatarRef={flights?.anchor(ALIA_ANCHORS.profile)}
|
|
57
|
-
avatarHidden={flights?.isLanding(ALIA_ANCHORS.profile)}
|
|
58
|
-
/>
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const noop = () => undefined;
|
|
63
|
-
|
|
64
36
|
/**
|
|
65
37
|
* The chat's top bar: Bloom's `PageHeader`, set up as the shell sets it up for the
|
|
66
|
-
* agents' screens (inline, no scrim, no safe area).
|
|
67
|
-
* there is a conversation: the empty chat already shows the avatar beside its greeting,
|
|
38
|
+
* agents' screens (inline, no scrim, no safe area). The participants are its title only once
|
|
39
|
+
* there is a conversation: the empty chat already shows the lead's avatar beside its greeting,
|
|
68
40
|
* so the bar carries just its buttons until then.
|
|
69
41
|
*/
|
|
70
42
|
export const AliaChatHeader = memo(function AliaChatHeader({
|
|
71
43
|
hasMessages,
|
|
72
|
-
|
|
44
|
+
participants,
|
|
73
45
|
onClear,
|
|
74
46
|
onBack,
|
|
75
47
|
onShare,
|
|
@@ -98,7 +70,11 @@ export const AliaChatHeader = memo(function AliaChatHeader({
|
|
|
98
70
|
</ButtonGroup>
|
|
99
71
|
) : undefined)
|
|
100
72
|
}
|
|
101
|
-
title={
|
|
73
|
+
title={
|
|
74
|
+
hasMessages ? (
|
|
75
|
+
<AliaParticipants participants={participants} onPress={onProfilePress} />
|
|
76
|
+
) : undefined
|
|
77
|
+
}
|
|
102
78
|
titleAlign="center"
|
|
103
79
|
actions={
|
|
104
80
|
<>
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
* />
|
|
13
13
|
* ```
|
|
14
14
|
*
|
|
15
|
-
* Voice is
|
|
16
|
-
*
|
|
17
|
-
*
|
|
15
|
+
* Voice is part of the screen: the button, the call bar, the notices and the transcript in the
|
|
16
|
+
* thread. `voice={false}` makes it text-only; `voice={{ canStartVoice, onCallEnded, … }}` brings
|
|
17
|
+
* the host's own callbacks. The call loads on the first one.
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import type React from 'react';
|
|
@@ -24,16 +24,31 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
|
24
24
|
import { AgentAvatarProvider } from '@oxy.so/bloom/agent-avatar';
|
|
25
25
|
import { ALIA_CHARACTER_RUNTIME_URL } from '../lib/character-runtime';
|
|
26
26
|
import { KeyboardAvoidingView } from '../lib/keyboard';
|
|
27
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
type AliaChatAgentRef,
|
|
29
|
+
AliaChatContent,
|
|
30
|
+
type AliaChatContentProps,
|
|
31
|
+
} from './AliaChatContent';
|
|
28
32
|
import { AliaChatHeader } from './AliaChatHeader';
|
|
29
33
|
import type { AliaSuggestion } from '../types';
|
|
30
34
|
import type { AliaChatLabels } from '../lib/labels';
|
|
31
|
-
import type {
|
|
35
|
+
import type { AliaToolRenderers } from '../lib/tool-renderers';
|
|
36
|
+
import { useShareHandler } from '../hooks/useShareHandler';
|
|
37
|
+
import type { AliaHostTools } from '../runtime/host-tools';
|
|
38
|
+
import type { AliaConversationShare } from '../runtime/sharing';
|
|
32
39
|
|
|
33
40
|
export interface AliaChatScreenProps {
|
|
41
|
+
/** The agent the screen is with, any agent the person can reach; omitted, Alia. */
|
|
42
|
+
agent?: AliaChatAgentRef;
|
|
34
43
|
/** App context injected into system prompt */
|
|
35
44
|
clientContext?: string;
|
|
36
|
-
/**
|
|
45
|
+
/** The UI effects this app performs for the lead (see `AliaChatContentProps.hostTools`). */
|
|
46
|
+
hostTools?: AliaHostTools;
|
|
47
|
+
/** The app's own drawing of catalogue tools' results, keyed `<app>:<tool>`. */
|
|
48
|
+
toolRenderers?: AliaToolRenderers;
|
|
49
|
+
/** The lead's feature gate refused something: show the app's upsell for `key`. */
|
|
50
|
+
onFeatureLocked?: (key: string) => void;
|
|
51
|
+
/** The power level (`auto`, `instant`, `medium`, `high`, `xhigh`, `pro`, `ultra`) or an exact `publisher/model` every turn is sent with; omitted, the server picks. */
|
|
37
52
|
model?: string;
|
|
38
53
|
/** API URL override */
|
|
39
54
|
apiUrl?: string;
|
|
@@ -41,8 +56,11 @@ export interface AliaChatScreenProps {
|
|
|
41
56
|
onBack?: () => void;
|
|
42
57
|
/** Optional header left content instead of the back button (e.g. a drawer's menu button) */
|
|
43
58
|
headerLeft?: React.ReactNode;
|
|
44
|
-
/**
|
|
45
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Draws the header's share button once there is a conversation; omitted, no share button.
|
|
61
|
+
* Pressed, the conversation's read-only link is minted and handed here for the app to show.
|
|
62
|
+
*/
|
|
63
|
+
onShare?: (share: AliaConversationShare) => void;
|
|
46
64
|
/** More rows for the header's menu (`DropdownMenuItem`s from `@oxy.so/bloom/dropdown-menu`). */
|
|
47
65
|
headerMenu?: React.ReactNode;
|
|
48
66
|
/** Optional header right action (e.g., settings button) */
|
|
@@ -53,16 +71,14 @@ export interface AliaChatScreenProps {
|
|
|
53
71
|
welcomeSubtitle?: string;
|
|
54
72
|
/** Welcome screen suggestions */
|
|
55
73
|
welcomeSuggestions?: AliaSuggestion[];
|
|
56
|
-
/** The agents the host knows, with their look: a delegation's thinking avatar is drawn from them until the agent answers. */
|
|
57
|
-
agents?: AliaChatContentProps['agents'];
|
|
58
74
|
/** What the host found for the typed text, shown over the composer (see `onSuggestionQuery`). */
|
|
59
75
|
suggestionMatches?: AliaSuggestion[];
|
|
60
76
|
/** The typed text, once typing has settled, for the host to search suggestions with. */
|
|
61
77
|
onSuggestionQuery?: (query: string) => void;
|
|
62
78
|
/** Dark-mode flag — forwarded to the ambient wave overlay. */
|
|
63
79
|
isDarkMode?: boolean;
|
|
64
|
-
/**
|
|
65
|
-
|
|
80
|
+
/** The chat's voice call: on by default, `false` for text only, an object for the host's callbacks. */
|
|
81
|
+
voice?: AliaChatContentProps['voice'];
|
|
66
82
|
/** Overrides for the chat's own copy (header, composer, plan, research…); omitted, English. */
|
|
67
83
|
labels?: Partial<AliaChatLabels>;
|
|
68
84
|
/** Where the 3D avatar's runtime is served from; omitted, Alia's shared copy (`ALIA_CHARACTER_RUNTIME_URL`). */
|
|
@@ -70,7 +86,11 @@ export interface AliaChatScreenProps {
|
|
|
70
86
|
}
|
|
71
87
|
|
|
72
88
|
export function AliaChatScreen({
|
|
89
|
+
agent,
|
|
73
90
|
clientContext,
|
|
91
|
+
hostTools,
|
|
92
|
+
toolRenderers,
|
|
93
|
+
onFeatureLocked,
|
|
74
94
|
model,
|
|
75
95
|
apiUrl,
|
|
76
96
|
onBack,
|
|
@@ -81,11 +101,10 @@ export function AliaChatScreen({
|
|
|
81
101
|
welcomeGreeting,
|
|
82
102
|
welcomeSubtitle,
|
|
83
103
|
welcomeSuggestions,
|
|
84
|
-
agents,
|
|
85
104
|
suggestionMatches,
|
|
86
105
|
onSuggestionQuery,
|
|
87
106
|
isDarkMode,
|
|
88
|
-
|
|
107
|
+
voice,
|
|
89
108
|
labels,
|
|
90
109
|
characterRuntimeUrl = ALIA_CHARACTER_RUNTIME_URL,
|
|
91
110
|
}: AliaChatScreenProps) {
|
|
@@ -100,20 +119,23 @@ export function AliaChatScreen({
|
|
|
100
119
|
// keyboard's height minus the inset: the composer sits on the keyboard, not
|
|
101
120
|
// one inset above it. (Its parent-relative layout would read as zero overlap
|
|
102
121
|
// this deep in a host's tree, which is why the offset is measured.)
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
122
|
+
const share = useShareHandler(onShare);
|
|
123
|
+
const chrome = useCallback<NonNullable<AliaChatContentProps['chrome']>>(
|
|
124
|
+
({ hasMessages, startOver, participants, conversationId }) => ({
|
|
125
|
+
header: (
|
|
126
|
+
<AliaChatHeader
|
|
127
|
+
hasMessages={hasMessages}
|
|
128
|
+
participants={participants}
|
|
129
|
+
onClear={startOver}
|
|
130
|
+
onBack={onBack}
|
|
131
|
+
onShare={share && conversationId !== undefined ? () => share(conversationId) : undefined}
|
|
132
|
+
menu={headerMenu}
|
|
133
|
+
leading={headerLeft}
|
|
134
|
+
trailing={headerRight}
|
|
135
|
+
/>
|
|
136
|
+
),
|
|
137
|
+
}),
|
|
138
|
+
[onBack, share, headerMenu, headerLeft, headerRight],
|
|
117
139
|
);
|
|
118
140
|
|
|
119
141
|
return (
|
|
@@ -124,19 +146,22 @@ export function AliaChatScreen({
|
|
|
124
146
|
>
|
|
125
147
|
<KeyboardAvoidingView behavior="padding" automaticOffset style={{ flex: 1 }}>
|
|
126
148
|
<AliaChatContent
|
|
149
|
+
agent={agent}
|
|
127
150
|
clientContext={clientContext}
|
|
151
|
+
hostTools={hostTools}
|
|
152
|
+
toolRenderers={toolRenderers}
|
|
153
|
+
onFeatureLocked={onFeatureLocked}
|
|
128
154
|
model={model}
|
|
129
155
|
apiUrl={apiUrl}
|
|
130
156
|
welcomeGreeting={welcomeGreeting}
|
|
131
157
|
welcomeSubtitle={welcomeSubtitle}
|
|
132
158
|
welcomeSuggestions={welcomeSuggestions}
|
|
133
|
-
agents={agents}
|
|
134
159
|
suggestionMatches={suggestionMatches}
|
|
135
160
|
onSuggestionQuery={onSuggestionQuery}
|
|
136
161
|
isDarkMode={isDarkMode}
|
|
137
|
-
|
|
162
|
+
voice={voice}
|
|
138
163
|
labels={labels}
|
|
139
|
-
|
|
164
|
+
chrome={chrome}
|
|
140
165
|
/>
|
|
141
166
|
</KeyboardAvoidingView>
|
|
142
167
|
</View>
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import { AgentAvatarProvider } from '@oxy.so/bloom/agent-avatar';
|
|
2
2
|
import { AvatarFlightLayer, useAvatarFlights } from '@oxy.so/bloom/avatar-flight';
|
|
3
3
|
import { BottomSheet, type BottomSheetRef } from '@oxy.so/bloom/bottom-sheet';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
forwardRef,
|
|
6
|
+
useCallback,
|
|
7
|
+
useEffect,
|
|
8
|
+
useImperativeHandle,
|
|
9
|
+
useMemo,
|
|
10
|
+
useRef,
|
|
11
|
+
useState,
|
|
12
|
+
} from 'react';
|
|
5
13
|
import { useTheme } from '@oxy.so/bloom/theme';
|
|
6
14
|
import { useWindowDimensions, View } from 'react-native';
|
|
7
15
|
import { useSharedValue } from 'react-native-reanimated';
|
|
@@ -9,9 +17,20 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
|
9
17
|
import { BREAKPOINTS } from '@oxy.so/bloom/styles';
|
|
10
18
|
import { ALIA_CHARACTER_RUNTIME_URL } from '../lib/character-runtime';
|
|
11
19
|
import type { AliaChatLabels } from '../lib/labels';
|
|
12
|
-
import type {
|
|
13
|
-
import {
|
|
20
|
+
import type { AliaToolRenderers } from '../lib/tool-renderers';
|
|
21
|
+
import { useShareHandler } from '../hooks/useShareHandler';
|
|
22
|
+
import type { AliaRuntimeHost } from '../runtime/config';
|
|
23
|
+
import type { AliaHostTools } from '../runtime/host-tools';
|
|
24
|
+
import type { AliaConversationShare } from '../runtime/sharing';
|
|
25
|
+
import { useThoughtStore } from '../runtime/thought-store';
|
|
26
|
+
import type { AliaTurn } from '../hooks/useChatComposer';
|
|
27
|
+
import {
|
|
28
|
+
type AliaChatAgentRef,
|
|
29
|
+
AliaChatContent,
|
|
30
|
+
type AliaChatContentProps,
|
|
31
|
+
} from './AliaChatContent';
|
|
14
32
|
import { AliaChatHeader } from './AliaChatHeader';
|
|
33
|
+
import { ThoughtPanel } from './thought/ThoughtPanel';
|
|
15
34
|
import type { AliaSuggestion } from '../types';
|
|
16
35
|
|
|
17
36
|
/**
|
|
@@ -23,13 +42,24 @@ import type { AliaSuggestion } from '../types';
|
|
|
23
42
|
*/
|
|
24
43
|
const SHEET_RADIUS = 40;
|
|
25
44
|
|
|
45
|
+
/** How much of the screen's height the sheet of a turn's work takes. */
|
|
46
|
+
const THOUGHT_SHEET_HEIGHT = 0.7;
|
|
47
|
+
|
|
26
48
|
/** The sheet floats (detached) from tablet width up; on a phone it is flush with the screen edges. */
|
|
27
49
|
const DETACHED_FROM = BREAKPOINTS.md;
|
|
28
50
|
|
|
29
51
|
export interface AliaChatSheetProps {
|
|
52
|
+
/** The agent the sheet opens on, any agent the person can reach; omitted, Alia. */
|
|
53
|
+
agent?: AliaChatAgentRef;
|
|
30
54
|
/** App context injected into system prompt */
|
|
31
55
|
clientContext?: string;
|
|
32
|
-
/**
|
|
56
|
+
/** The UI effects this app performs for the lead (see `AliaChatContentProps.hostTools`). */
|
|
57
|
+
hostTools?: AliaHostTools;
|
|
58
|
+
/** The app's own drawing of catalogue tools' results, keyed `<app>:<tool>`. */
|
|
59
|
+
toolRenderers?: AliaToolRenderers;
|
|
60
|
+
/** The lead's feature gate refused something: show the app's upsell for `key`. */
|
|
61
|
+
onFeatureLocked?: (key: string) => void;
|
|
62
|
+
/** The power level (`auto`, `instant`, `medium`, `high`, `xhigh`, `pro`, `ultra`) or an exact `publisher/model` every turn is sent with; omitted, the server picks. */
|
|
33
63
|
model?: string;
|
|
34
64
|
/** API URL override */
|
|
35
65
|
apiUrl?: string;
|
|
@@ -39,26 +69,23 @@ export interface AliaChatSheetProps {
|
|
|
39
69
|
welcomeSubtitle?: string;
|
|
40
70
|
/** Welcome screen suggestions */
|
|
41
71
|
welcomeSuggestions?: AliaSuggestion[];
|
|
42
|
-
/** The agents the host knows, with their look: a delegation's thinking avatar is drawn from them until the agent answers. */
|
|
43
|
-
agents?: AliaChatContentProps['agents'];
|
|
44
72
|
/** What the host found for the typed text, shown over the composer (see `onSuggestionQuery`). */
|
|
45
73
|
suggestionMatches?: AliaSuggestion[];
|
|
46
74
|
/** The typed text, once typing has settled, for the host to search suggestions with. */
|
|
47
75
|
onSuggestionQuery?: (query: string) => void;
|
|
48
76
|
/** Dark-mode flag — forwarded to the ambient wave overlay (defaults to the sheet's own color scheme). */
|
|
49
77
|
isDarkMode?: boolean;
|
|
50
|
-
/**
|
|
51
|
-
|
|
52
|
-
* voice calls. Omitted, the sheet is text-only and `react-native-audio-api` never
|
|
53
|
-
* enters the bundle.
|
|
54
|
-
*/
|
|
55
|
-
voiceSession?: VoiceSessionComponent;
|
|
78
|
+
/** The chat's voice call: on by default, `false` for text only, an object for the host's callbacks. */
|
|
79
|
+
voice?: AliaChatContentProps['voice'];
|
|
56
80
|
/** Overrides for the chat's own copy (header, composer, plan, research…); omitted, English. */
|
|
57
81
|
labels?: Partial<AliaChatLabels>;
|
|
58
82
|
/** Where the 3D avatar's runtime is served from; omitted, Alia's shared copy (`ALIA_CHARACTER_RUNTIME_URL`). */
|
|
59
83
|
characterRuntimeUrl?: string;
|
|
60
|
-
/**
|
|
61
|
-
|
|
84
|
+
/**
|
|
85
|
+
* Draws the header's share button once there is a conversation; omitted, no share button.
|
|
86
|
+
* Pressed, the conversation's read-only link is minted and handed here for the app to show.
|
|
87
|
+
*/
|
|
88
|
+
onShare?: (share: AliaConversationShare) => void;
|
|
62
89
|
/** More rows for the header's menu (`DropdownMenuItem`s from `@oxy.so/bloom/dropdown-menu`). */
|
|
63
90
|
headerMenu?: React.ReactNode;
|
|
64
91
|
}
|
|
@@ -76,17 +103,20 @@ export interface AliaChatSheetRef {
|
|
|
76
103
|
export const AliaChatSheet = forwardRef<AliaChatSheetRef, AliaChatSheetProps>(
|
|
77
104
|
(
|
|
78
105
|
{
|
|
106
|
+
agent,
|
|
79
107
|
clientContext,
|
|
108
|
+
hostTools,
|
|
109
|
+
toolRenderers,
|
|
110
|
+
onFeatureLocked,
|
|
80
111
|
model,
|
|
81
112
|
apiUrl,
|
|
82
113
|
welcomeGreeting,
|
|
83
114
|
welcomeSubtitle,
|
|
84
115
|
welcomeSuggestions,
|
|
85
|
-
agents,
|
|
86
116
|
suggestionMatches,
|
|
87
117
|
onSuggestionQuery,
|
|
88
118
|
isDarkMode,
|
|
89
|
-
|
|
119
|
+
voice,
|
|
90
120
|
labels,
|
|
91
121
|
characterRuntimeUrl = ALIA_CHARACTER_RUNTIME_URL,
|
|
92
122
|
onShare,
|
|
@@ -133,12 +163,32 @@ export const AliaChatSheet = forwardRef<AliaChatSheetRef, AliaChatSheetProps>(
|
|
|
133
163
|
return true;
|
|
134
164
|
}, []);
|
|
135
165
|
|
|
166
|
+
// What a turn did — its steps, its sources, its reasoning — opens in a sheet over the
|
|
167
|
+
// conversation when the reader asks for it. A turn that begins using tools does not open one by
|
|
168
|
+
// itself: on a phone that would cover the answer being written.
|
|
169
|
+
const thoughtRef = useRef<BottomSheetRef>(null);
|
|
170
|
+
const closeThought = useCallback(() => {
|
|
171
|
+
useThoughtStore.getState().close();
|
|
172
|
+
thoughtRef.current?.dismiss();
|
|
173
|
+
}, []);
|
|
174
|
+
const runtime = useMemo<AliaRuntimeHost>(
|
|
175
|
+
() => ({
|
|
176
|
+
showThought: (opening) => {
|
|
177
|
+
if (opening === 'automatic') return false;
|
|
178
|
+
thoughtRef.current?.present();
|
|
179
|
+
return true;
|
|
180
|
+
},
|
|
181
|
+
}),
|
|
182
|
+
[],
|
|
183
|
+
);
|
|
184
|
+
|
|
136
185
|
const handleDismiss = useCallback(() => {
|
|
137
186
|
conversationStopRef.current?.();
|
|
138
187
|
mainStopRef.current?.();
|
|
139
188
|
conversationRef.current?.dismiss();
|
|
140
189
|
mainRef.current?.dismiss();
|
|
141
|
-
|
|
190
|
+
closeThought();
|
|
191
|
+
}, [closeThought]);
|
|
142
192
|
|
|
143
193
|
useImperativeHandle(
|
|
144
194
|
ref,
|
|
@@ -157,10 +207,11 @@ export const AliaChatSheet = forwardRef<AliaChatSheetRef, AliaChatSheetProps>(
|
|
|
157
207
|
mainStopRef.current?.();
|
|
158
208
|
conversationStopRef.current?.();
|
|
159
209
|
conversationRef.current?.dismiss();
|
|
210
|
+
closeThought();
|
|
160
211
|
return true;
|
|
161
|
-
}, []);
|
|
212
|
+
}, [closeThought]);
|
|
162
213
|
|
|
163
|
-
const { width } = useWindowDimensions();
|
|
214
|
+
const { width, height } = useWindowDimensions();
|
|
164
215
|
const detached = width >= DETACHED_FROM;
|
|
165
216
|
// Flush with the screen edge the sheet runs behind the home indicator: the composer clears it.
|
|
166
217
|
const { bottom } = useSafeAreaInsets();
|
|
@@ -170,13 +221,40 @@ export const AliaChatSheet = forwardRef<AliaChatSheetRef, AliaChatSheetProps>(
|
|
|
170
221
|
|
|
171
222
|
// The avatars fly between the two sheets: one controller, one layer.
|
|
172
223
|
const flights = useAvatarFlights();
|
|
224
|
+
// The conversation's bar: back to the main screen, the participants, share and the menu.
|
|
225
|
+
const share = useShareHandler(onShare);
|
|
226
|
+
const conversationChrome = useCallback<NonNullable<AliaChatContentProps['chrome']>>(
|
|
227
|
+
({ participants, conversationId }) => ({
|
|
228
|
+
header: (
|
|
229
|
+
<View className="pt-8">
|
|
230
|
+
<AliaChatHeader
|
|
231
|
+
participants={participants}
|
|
232
|
+
hasMessages
|
|
233
|
+
onClear={closeConversation}
|
|
234
|
+
onBack={closeConversation}
|
|
235
|
+
onShare={
|
|
236
|
+
share && conversationId !== undefined ? () => share(conversationId) : undefined
|
|
237
|
+
}
|
|
238
|
+
menu={headerMenu}
|
|
239
|
+
/>
|
|
240
|
+
</View>
|
|
241
|
+
),
|
|
242
|
+
}),
|
|
243
|
+
[closeConversation, share, headerMenu],
|
|
244
|
+
);
|
|
173
245
|
const shared = {
|
|
174
246
|
flights,
|
|
247
|
+
layout: 'sheet' as const,
|
|
248
|
+
agent,
|
|
175
249
|
clientContext,
|
|
250
|
+
hostTools,
|
|
251
|
+
toolRenderers,
|
|
252
|
+
onFeatureLocked,
|
|
176
253
|
model,
|
|
177
254
|
apiUrl,
|
|
178
255
|
isDarkMode: isDarkMode ?? isDark,
|
|
179
|
-
|
|
256
|
+
voice,
|
|
257
|
+
runtime,
|
|
180
258
|
bottomInset: detached ? 0 : bottom,
|
|
181
259
|
labels,
|
|
182
260
|
};
|
|
@@ -202,7 +280,6 @@ export const AliaChatSheet = forwardRef<AliaChatSheetRef, AliaChatSheetProps>(
|
|
|
202
280
|
welcomeGreeting={welcomeGreeting}
|
|
203
281
|
welcomeSubtitle={welcomeSubtitle}
|
|
204
282
|
welcomeSuggestions={welcomeSuggestions}
|
|
205
|
-
agents={agents}
|
|
206
283
|
suggestionMatches={suggestionMatches}
|
|
207
284
|
onSuggestionQuery={onSuggestionQuery}
|
|
208
285
|
presentedProgress={mainProgress}
|
|
@@ -227,25 +304,22 @@ export const AliaChatSheet = forwardRef<AliaChatSheetRef, AliaChatSheetProps>(
|
|
|
227
304
|
{...shared}
|
|
228
305
|
initialTurn={conversation.turn}
|
|
229
306
|
presentedProgress={conversationProgress}
|
|
230
|
-
agents={agents}
|
|
231
307
|
onStopReady={registerConversationStop}
|
|
232
|
-
|
|
233
|
-
// the menu.
|
|
234
|
-
header={({ agents }) => (
|
|
235
|
-
<View className="pt-8">
|
|
236
|
-
<AliaChatHeader
|
|
237
|
-
hasMessages
|
|
238
|
-
agents={agents}
|
|
239
|
-
onClear={closeConversation}
|
|
240
|
-
onBack={closeConversation}
|
|
241
|
-
onShare={onShare}
|
|
242
|
-
menu={headerMenu}
|
|
243
|
-
/>
|
|
244
|
-
</View>
|
|
245
|
-
)}
|
|
308
|
+
chrome={conversationChrome}
|
|
246
309
|
/>
|
|
247
310
|
</BottomSheet>
|
|
248
311
|
) : null}
|
|
312
|
+
<BottomSheet
|
|
313
|
+
ref={thoughtRef}
|
|
314
|
+
detached={detached}
|
|
315
|
+
scrollable={false}
|
|
316
|
+
onDismiss={() => useThoughtStore.getState().close()}
|
|
317
|
+
style={sheetStyle}
|
|
318
|
+
>
|
|
319
|
+
<View style={{ height: height * THOUGHT_SHEET_HEIGHT }}>
|
|
320
|
+
<ThoughtPanel onClose={closeThought} />
|
|
321
|
+
</View>
|
|
322
|
+
</BottomSheet>
|
|
249
323
|
<AvatarFlightLayer flights={flights} />
|
|
250
324
|
</AgentAvatarProvider>
|
|
251
325
|
);
|
|
@@ -321,7 +321,7 @@ function AliaComposerBody<T extends AliaSuggestion>({
|
|
|
321
321
|
busy={busy}
|
|
322
322
|
onStop={onStop}
|
|
323
323
|
disabled={disabled || intake.isBusy || !hasContent}
|
|
324
|
-
placeholder={placeholder ?? labels.placeholder}
|
|
324
|
+
placeholder={placeholder ?? labels.placeholder('Alia')}
|
|
325
325
|
addMenu={addMenu ?? EMPTY_ADD_MENU}
|
|
326
326
|
onAddMenuSelect={onAddMenuSelect}
|
|
327
327
|
permissions={modes ?? NO_MODES}
|