@wix/himalaya-cli 0.1308.0 → 0.1310.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/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/dev-server/src/builder.d.ts +7 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/delivery-contract.d.ts +3 -3
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/delivery.d.ts +3 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/host-session.d.ts +31 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/index.d.ts +1 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/wix-payments-contract.d.ts +19 -14
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/wix-payments-wire.d.ts +2 -4
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/wix-payments.d.ts +11 -12
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/schema.d.ts +32 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/validator.d.ts +60 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/iap-checkout/src/apple-iap-contract.d.ts +17 -9
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/iap-checkout/src/iap-order-backend.d.ts +6 -1
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/core.mjs +31 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/iap-checkout.mjs +119 -65
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/iap.mjs +117 -35
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/sdui.mjs +31 -0
- package/dist/cli.mjs +1305 -863
- package/dist/preview-assets.generated.bin +0 -0
- package/guides/app-config.md +2 -2
- package/guides/component-reference.json +2 -2
- package/guides/component-reference.md +10 -0
- package/guides/getting-started.md +40 -0
- package/guides/himi-skill.md +40 -0
- package/guides/qa-builds.md +16 -12
- package/guides/sdk-reference.json +61 -25
- package/package.json +3 -2
- package/templates/beauty-salon/himalaya.content.json +86 -0
- package/templates/beauty-salon-owner/dev/index.ts +574 -181
- package/templates/beauty-salon-owner/himalaya.content.json +179 -0
- package/templates/beauty-salon-owner/tier5-src/_shared/aria-chat.ts +136 -12
- package/templates/beauty-salon-owner/tier5-src/_shared/aria-client-tools.ts +57 -9
- package/templates/beauty-salon-owner/tier5-src/_shared/aria-row-id.ts +14 -0
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-aria/index.ts +3 -0
- package/templates/branded-lite/himalaya.content.json +254 -0
- package/templates/brandy/himalaya.content.json +18 -1
- package/templates/community-club/himalaya.content.json +68 -0
- package/templates/community-club-owner/dev/index.ts +574 -181
- package/templates/community-club-owner/himalaya.content.json +173 -0
- package/templates/community-club-owner/tier5-src/_shared/aria-chat.ts +136 -12
- package/templates/community-club-owner/tier5-src/_shared/aria-client-tools.ts +57 -9
- package/templates/community-club-owner/tier5-src/_shared/aria-row-id.ts +14 -0
- package/templates/community-club-owner/tier5-src/himi-owner-lite-aria/index.ts +3 -0
- package/templates/events-meetups/himalaya.content.json +62 -0
- package/templates/events-meetups-owner/dev/index.ts +574 -181
- package/templates/events-meetups-owner/himalaya.content.json +155 -0
- package/templates/events-meetups-owner/tier5-src/_shared/aria-chat.ts +136 -12
- package/templates/events-meetups-owner/tier5-src/_shared/aria-client-tools.ts +57 -9
- package/templates/events-meetups-owner/tier5-src/_shared/aria-row-id.ts +14 -0
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-aria/index.ts +3 -0
- package/templates/gym-fitness/himalaya.content.json +101 -0
- package/templates/gym-fitness-owner/dev/index.ts +574 -181
- package/templates/gym-fitness-owner/himalaya.content.json +188 -0
- package/templates/gym-fitness-owner/tier5-src/_shared/aria-chat.ts +136 -12
- package/templates/gym-fitness-owner/tier5-src/_shared/aria-client-tools.ts +57 -9
- package/templates/gym-fitness-owner/tier5-src/_shared/aria-row-id.ts +14 -0
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-aria/index.ts +3 -0
- package/templates/himi-chat/himalaya.content.json +8 -0
- package/templates/nonprofit-cause/himalaya.content.json +74 -0
- package/templates/nonprofit-cause-owner/dev/index.ts +574 -181
- package/templates/nonprofit-cause-owner/himalaya.content.json +170 -0
- package/templates/nonprofit-cause-owner/tier5-src/_shared/aria-chat.ts +136 -12
- package/templates/nonprofit-cause-owner/tier5-src/_shared/aria-client-tools.ts +57 -9
- package/templates/nonprofit-cause-owner/tier5-src/_shared/aria-row-id.ts +14 -0
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-aria/index.ts +3 -0
- package/templates/owner-lite/dev/index.ts +574 -181
- package/templates/owner-lite/himalaya.content.json +203 -0
- package/templates/owner-lite/tier5-src/_shared/aria-chat.ts +136 -12
- package/templates/owner-lite/tier5-src/_shared/aria-client-tools.ts +57 -9
- package/templates/owner-lite/tier5-src/_shared/aria-row-id.ts +14 -0
- package/templates/owner-lite/tier5-src/himi-owner-lite-aria/index.ts +3 -0
- package/templates/podcast-creator/himalaya.content.json +71 -0
- package/templates/podcast-creator-owner/dev/index.ts +574 -181
- package/templates/podcast-creator-owner/himalaya.content.json +173 -0
- package/templates/podcast-creator-owner/tier5-src/_shared/aria-chat.ts +136 -12
- package/templates/podcast-creator-owner/tier5-src/_shared/aria-client-tools.ts +57 -9
- package/templates/podcast-creator-owner/tier5-src/_shared/aria-row-id.ts +14 -0
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-aria/index.ts +3 -0
- package/templates/portfolio-creative/himalaya.content.json +80 -0
- package/templates/portfolio-creative-owner/dev/index.ts +574 -181
- package/templates/portfolio-creative-owner/himalaya.content.json +179 -0
- package/templates/portfolio-creative-owner/tier5-src/_shared/aria-chat.ts +136 -12
- package/templates/portfolio-creative-owner/tier5-src/_shared/aria-client-tools.ts +57 -9
- package/templates/portfolio-creative-owner/tier5-src/_shared/aria-row-id.ts +14 -0
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-aria/index.ts +3 -0
- package/templates/restaurant/himalaya.content.json +65 -0
- package/templates/restaurant-owner/dev/index.ts +574 -181
- package/templates/restaurant-owner/himalaya.content.json +188 -0
- package/templates/restaurant-owner/tier5-src/_shared/aria-chat.ts +136 -12
- package/templates/restaurant-owner/tier5-src/_shared/aria-client-tools.ts +57 -9
- package/templates/restaurant-owner/tier5-src/_shared/aria-row-id.ts +14 -0
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-aria/index.ts +3 -0
- package/templates/retail-store/himalaya.content.json +62 -0
- package/templates/retail-store-owner/dev/index.ts +574 -181
- package/templates/retail-store-owner/himalaya.content.json +164 -0
- package/templates/retail-store-owner/tier5-src/_shared/aria-chat.ts +136 -12
- package/templates/retail-store-owner/tier5-src/_shared/aria-client-tools.ts +57 -9
- package/templates/retail-store-owner/tier5-src/_shared/aria-row-id.ts +14 -0
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-aria/index.ts +3 -0
- package/templates/surface-showcase/himalaya.content.json +11 -0
- package/templates/yoga-studio/himalaya.content.json +80 -0
- package/templates/yoga-studio-owner/dev/index.ts +574 -181
- package/templates/yoga-studio-owner/himalaya.content.json +170 -0
- package/templates/yoga-studio-owner/tier5-src/_shared/aria-chat.ts +136 -12
- package/templates/yoga-studio-owner/tier5-src/_shared/aria-client-tools.ts +57 -9
- package/templates/yoga-studio-owner/tier5-src/_shared/aria-row-id.ts +14 -0
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-aria/index.ts +3 -0
|
@@ -1,5 +1,175 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "__HIMI_APP_NAME__",
|
|
3
|
+
"screens": {
|
|
4
|
+
"himi_owner_lite_account": {
|
|
5
|
+
"displayName": "Account"
|
|
6
|
+
},
|
|
7
|
+
"himi_owner_lite_analytics": {
|
|
8
|
+
"displayName": "Analytics Overview"
|
|
9
|
+
},
|
|
10
|
+
"himi_owner_lite_analytics_measure": {
|
|
11
|
+
"displayName": "Analytics Metric Details"
|
|
12
|
+
},
|
|
13
|
+
"himi_owner_lite_analytics_report": {
|
|
14
|
+
"displayName": "Build a report"
|
|
15
|
+
},
|
|
16
|
+
"himi_owner_lite_appointments": {
|
|
17
|
+
"displayName": "Appointments"
|
|
18
|
+
},
|
|
19
|
+
"himi_owner_lite_aria": {
|
|
20
|
+
"displayName": "Aria"
|
|
21
|
+
},
|
|
22
|
+
"himi_owner_lite_aria_history": {
|
|
23
|
+
"displayName": "Aria History"
|
|
24
|
+
},
|
|
25
|
+
"himi_owner_lite_ask": {
|
|
26
|
+
"displayName": "Ask the studio"
|
|
27
|
+
},
|
|
28
|
+
"himi_owner_lite_booking": {
|
|
29
|
+
"displayName": "Booking"
|
|
30
|
+
},
|
|
31
|
+
"himi_owner_lite_brief": {
|
|
32
|
+
"displayName": "Today at the studio"
|
|
33
|
+
},
|
|
34
|
+
"himi_owner_lite_drawer": {
|
|
35
|
+
"displayName": "Menu"
|
|
36
|
+
},
|
|
37
|
+
"himi_owner_lite_event": {
|
|
38
|
+
"displayName": "Workshop"
|
|
39
|
+
},
|
|
40
|
+
"himi_owner_lite_event_form": {
|
|
41
|
+
"displayName": "Event Form"
|
|
42
|
+
},
|
|
43
|
+
"himi_owner_lite_events": {
|
|
44
|
+
"displayName": "Events"
|
|
45
|
+
},
|
|
46
|
+
"himi_owner_lite_home": {
|
|
47
|
+
"displayName": "Business Dashboard"
|
|
48
|
+
},
|
|
49
|
+
"himi_owner_lite_manage": {
|
|
50
|
+
"displayName": "Studio"
|
|
51
|
+
},
|
|
52
|
+
"himi_owner_lite_module_badges": {
|
|
53
|
+
"displayName": "Module Badges"
|
|
54
|
+
},
|
|
55
|
+
"himi_owner_lite_module_contacts": {
|
|
56
|
+
"displayName": "Module Contacts"
|
|
57
|
+
},
|
|
58
|
+
"himi_owner_lite_module_coupons": {
|
|
59
|
+
"displayName": "Module Coupons"
|
|
60
|
+
},
|
|
61
|
+
"himi_owner_lite_module_donationCampaigns": {
|
|
62
|
+
"displayName": "Module Donation Campaigns"
|
|
63
|
+
},
|
|
64
|
+
"himi_owner_lite_module_forms": {
|
|
65
|
+
"displayName": "Module Forms"
|
|
66
|
+
},
|
|
67
|
+
"himi_owner_lite_module_giftCards": {
|
|
68
|
+
"displayName": "Module Gift Cards"
|
|
69
|
+
},
|
|
70
|
+
"himi_owner_lite_module_groups": {
|
|
71
|
+
"displayName": "Module Groups"
|
|
72
|
+
},
|
|
73
|
+
"himi_owner_lite_module_invoices": {
|
|
74
|
+
"displayName": "Module Invoices"
|
|
75
|
+
},
|
|
76
|
+
"himi_owner_lite_module_members": {
|
|
77
|
+
"displayName": "Module Members"
|
|
78
|
+
},
|
|
79
|
+
"himi_owner_lite_module_plans": {
|
|
80
|
+
"displayName": "Module Plans"
|
|
81
|
+
},
|
|
82
|
+
"himi_owner_lite_module_portfolio": {
|
|
83
|
+
"displayName": "Module Portfolio"
|
|
84
|
+
},
|
|
85
|
+
"himi_owner_lite_module_programs": {
|
|
86
|
+
"displayName": "Module Programs"
|
|
87
|
+
},
|
|
88
|
+
"himi_owner_lite_module_reservations": {
|
|
89
|
+
"displayName": "Module Reservations"
|
|
90
|
+
},
|
|
91
|
+
"himi_owner_lite_module_reviews": {
|
|
92
|
+
"displayName": "Module Reviews"
|
|
93
|
+
},
|
|
94
|
+
"himi_owner_lite_module_subscriptions": {
|
|
95
|
+
"displayName": "Module Subscriptions"
|
|
96
|
+
},
|
|
97
|
+
"himi_owner_lite_overview": {
|
|
98
|
+
"displayName": "Overview"
|
|
99
|
+
},
|
|
100
|
+
"himi_owner_lite_post": {
|
|
101
|
+
"displayName": "Journal entry"
|
|
102
|
+
},
|
|
103
|
+
"himi_owner_lite_post_form": {
|
|
104
|
+
"displayName": "Post Form"
|
|
105
|
+
},
|
|
106
|
+
"himi_owner_lite_post_new": {
|
|
107
|
+
"displayName": "Post New"
|
|
108
|
+
},
|
|
109
|
+
"himi_owner_lite_posts": {
|
|
110
|
+
"displayName": "Posts"
|
|
111
|
+
},
|
|
112
|
+
"himi_owner_lite_record_badges": {
|
|
113
|
+
"displayName": "Record Badges"
|
|
114
|
+
},
|
|
115
|
+
"himi_owner_lite_record_contacts": {
|
|
116
|
+
"displayName": "Record Contacts"
|
|
117
|
+
},
|
|
118
|
+
"himi_owner_lite_record_coupons": {
|
|
119
|
+
"displayName": "Record Coupons"
|
|
120
|
+
},
|
|
121
|
+
"himi_owner_lite_record_donationCampaigns": {
|
|
122
|
+
"displayName": "Record Donation Campaigns"
|
|
123
|
+
},
|
|
124
|
+
"himi_owner_lite_record_forms": {
|
|
125
|
+
"displayName": "Record Forms"
|
|
126
|
+
},
|
|
127
|
+
"himi_owner_lite_record_giftCards": {
|
|
128
|
+
"displayName": "Record Gift Cards"
|
|
129
|
+
},
|
|
130
|
+
"himi_owner_lite_record_groups": {
|
|
131
|
+
"displayName": "Record Groups"
|
|
132
|
+
},
|
|
133
|
+
"himi_owner_lite_record_invoices": {
|
|
134
|
+
"displayName": "Record Invoices"
|
|
135
|
+
},
|
|
136
|
+
"himi_owner_lite_record_members": {
|
|
137
|
+
"displayName": "Record Members"
|
|
138
|
+
},
|
|
139
|
+
"himi_owner_lite_record_plans": {
|
|
140
|
+
"displayName": "Record Plans"
|
|
141
|
+
},
|
|
142
|
+
"himi_owner_lite_record_portfolio": {
|
|
143
|
+
"displayName": "Record Portfolio"
|
|
144
|
+
},
|
|
145
|
+
"himi_owner_lite_record_programs": {
|
|
146
|
+
"displayName": "Record Programs"
|
|
147
|
+
},
|
|
148
|
+
"himi_owner_lite_record_reservations": {
|
|
149
|
+
"displayName": "Record Reservations"
|
|
150
|
+
},
|
|
151
|
+
"himi_owner_lite_record_reviews": {
|
|
152
|
+
"displayName": "Record Reviews"
|
|
153
|
+
},
|
|
154
|
+
"himi_owner_lite_record_subscriptions": {
|
|
155
|
+
"displayName": "Record Subscriptions"
|
|
156
|
+
},
|
|
157
|
+
"himi_owner_lite_search": {
|
|
158
|
+
"displayName": "Search records"
|
|
159
|
+
},
|
|
160
|
+
"himi_owner_lite_service": {
|
|
161
|
+
"displayName": "Class"
|
|
162
|
+
},
|
|
163
|
+
"himi_owner_lite_service_form": {
|
|
164
|
+
"displayName": "Service Form"
|
|
165
|
+
},
|
|
166
|
+
"himi_owner_lite_services": {
|
|
167
|
+
"displayName": "Services"
|
|
168
|
+
},
|
|
169
|
+
"himi_owner_lite_sites": {
|
|
170
|
+
"displayName": "Sites"
|
|
171
|
+
}
|
|
172
|
+
},
|
|
3
173
|
"icon": "icon.png",
|
|
4
174
|
"target": "prod",
|
|
5
175
|
"shell": "owner-lite"
|
|
@@ -69,7 +69,7 @@ import {
|
|
|
69
69
|
type AriaAttachmentChip,
|
|
70
70
|
type AriaAttachmentOutcome,
|
|
71
71
|
} from "./aria-attachments.js";
|
|
72
|
-
import { runClientToolExchange, type AriaClientTools } from "./aria-client-tools.js";
|
|
72
|
+
import { runClientToolExchange, type AriaClientTools, type ConsentMemory } from "./aria-client-tools.js";
|
|
73
73
|
import { proseOfParts, scanEnvelopes, stripHidden } from "./aria-widgets.js";
|
|
74
74
|
import { isWebUrl, stripMarkdown } from "./aria-widget-model.js";
|
|
75
75
|
import {
|
|
@@ -90,6 +90,8 @@ import { MENTION_PAGE_SIZE, allowlisted, emptyMentionModel, mentionView,
|
|
|
90
90
|
type MentionItemRow, type MentionLabels, type MentionModel, type MentionProvider, type MentionView } from "./aria-mentions.js";
|
|
91
91
|
import { isTransportFailure, ownerTransportFailureText } from "./owner-load-error.js";
|
|
92
92
|
import { LOCAL_ID_PREFIX, createRowMemo, mintLocalId, transcriptKeys, type RowMemo } from "./aria-hot-path.js";
|
|
93
|
+
import { ariaMessageRowId } from "./aria-row-id.js";
|
|
94
|
+
export { ariaMessageRowId } from "./aria-row-id.js";
|
|
93
95
|
import { createLiveReply } from "./aria-stream.js";
|
|
94
96
|
import { calendarDay, formatClock, formatDateMedium, formatMonthDay } from "./engine-intl.js";
|
|
95
97
|
|
|
@@ -281,6 +283,35 @@ export interface AriaChatState {
|
|
|
281
283
|
attachments: AriaAttachmentChip[];
|
|
282
284
|
/** Collapses the composer's attachment strip while it is empty. */
|
|
283
285
|
attachmentsHidden: boolean;
|
|
286
|
+
/** Transcript scroll — the transcript scroll policy's SEND TOKEN (`transcriptScroll.sendPath`).
|
|
287
|
+
* A new value is a send: the host pins `scrollAnchor` near the top and re-arms
|
|
288
|
+
* following. Empty on the empty state, so a fresh chat rests at the top. */
|
|
289
|
+
scrollSend: string;
|
|
290
|
+
/** Transcript scroll — the component id of the turn to pin (`transcriptScroll.anchorPath`):
|
|
291
|
+
* the newest USER row while a pin is wanted, else empty. Derived in `gated`,
|
|
292
|
+
* because the row changes identity mid-turn — the optimistic local id is replaced
|
|
293
|
+
* by the server's — and the host must follow it without reading a new send. */
|
|
294
|
+
scrollAnchor: string;
|
|
295
|
+
/** The last send named a turn to pin (a question), as opposed to "show the latest"
|
|
296
|
+
* (a resumed conversation). */
|
|
297
|
+
_pinActive: boolean;
|
|
298
|
+
/** The character. The idle hero sits on the empty state, beside the prompt
|
|
299
|
+
* cards and gone with them; on the status line she THINKS while a turn is out with
|
|
300
|
+
* no reply text yet, and TALKS while the reply is being written. Derived in
|
|
301
|
+
* `gated`, in the `hiddenPath` polarity. */
|
|
302
|
+
heroHidden: boolean;
|
|
303
|
+
charThinkingHidden: boolean;
|
|
304
|
+
charTalkingHidden: boolean;
|
|
305
|
+
/** The status row holds the character AND a Spacer, so unlike a bare
|
|
306
|
+
* `collapseWhenEmpty` text it never collapses on its own — with no status and no
|
|
307
|
+
* character it would still cost the screen a stack gap. True when it has nothing
|
|
308
|
+
* to show. */
|
|
309
|
+
statusHidden: boolean;
|
|
310
|
+
/** Collapses the 8pt gap above the prompt cards while they show — the empty state,
|
|
311
|
+
* which is tight on a phone — and keeps it through every reply turn, where the
|
|
312
|
+
* (empty) prompt list's gap is part of the spacing under a reply. Always
|
|
313
|
+
* `!heroHidden`. */
|
|
314
|
+
promptsGapHidden: boolean;
|
|
284
315
|
/** The quick replies the service suggested after the LATEST turn — short
|
|
285
316
|
* answers the owner can send with one tap. They arrive only on the realtime
|
|
286
317
|
* stream, a moment after the reply finishes, so a screen with no stream never
|
|
@@ -523,6 +554,11 @@ export interface AriaChatOptions {
|
|
|
523
554
|
greeting: Resolvable<string>;
|
|
524
555
|
prompts: Resolvable<AriaPromptChip[]>;
|
|
525
556
|
labels?: AriaChatLabels;
|
|
557
|
+
/** The screen this machine paints. Supplying it names the pinned turn for the
|
|
558
|
+
* transcript scroll (`scrollAnchor` = `ariaMessageRowId(screenId, id)`,
|
|
559
|
+
* the id the screen builder stamps on every message row). Omitted, nothing is
|
|
560
|
+
* pinned: the send token alone still makes the host follow the reply. */
|
|
561
|
+
screenId?: string;
|
|
526
562
|
origin?: string;
|
|
527
563
|
assistantId?: string;
|
|
528
564
|
/** Namespace for the local recent-conversation list. Partitioned by site when
|
|
@@ -986,6 +1022,39 @@ function lastReply(s: { messages: AriaBubble[] }): AriaBubble | undefined {
|
|
|
986
1022
|
return last && last.notUser && last.id !== GREETING_ID ? last : undefined;
|
|
987
1023
|
}
|
|
988
1024
|
|
|
1025
|
+
/** The next send token: `t1`, `t2`, … A counter carried IN the state rather than
|
|
1026
|
+
* in the worker, so a worker rebuilt over persisted state never re-issues the
|
|
1027
|
+
* token the host last saw — an equal token is "no send", by design. */
|
|
1028
|
+
export function nextScrollToken(prev: string): string {
|
|
1029
|
+
const n = /^t(\d+)$/.exec(prev)?.[1];
|
|
1030
|
+
return `t${(n ? Number(n) : 0) + 1}`;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
/** No conversation to follow: the empty state rests at the top. */
|
|
1034
|
+
const SCROLL_RESET = { scrollSend: "", _pinActive: false } as const;
|
|
1035
|
+
|
|
1036
|
+
/** A conversation put on screen whole (resumed from History, or rehydrated): open
|
|
1037
|
+
* at its latest turn with nothing pinned. A greeting alone is the empty state. */
|
|
1038
|
+
function openedAtLatest(s: { scrollSend: string }, messages: AriaBubble[]): { scrollSend: string; _pinActive: boolean } {
|
|
1039
|
+
return messages.some((b) => b.role === "USER")
|
|
1040
|
+
? { scrollSend: nextScrollToken(s.scrollSend), _pinActive: false }
|
|
1041
|
+
: SCROLL_RESET;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
/** The newest owner turn, or undefined. */
|
|
1045
|
+
function newestUserBubble(messages: AriaBubble[]): AriaBubble | undefined {
|
|
1046
|
+
for (let i = messages.length - 1; i >= 0; i--) if (messages[i].role === "USER") return messages[i];
|
|
1047
|
+
return undefined;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
/** Has the reply to the newest owner turn started to appear? An assistant row with
|
|
1051
|
+
* words AFTER that turn — while the turn is still out, that is the agent writing. */
|
|
1052
|
+
function replyStarted(messages: AriaBubble[]): boolean {
|
|
1053
|
+
if (!messages.some((b) => b.role === "USER")) return false;
|
|
1054
|
+
const last = messages[messages.length - 1];
|
|
1055
|
+
return !!last && last.role !== "USER" && last.id !== GREETING_ID && !!String(last.content ?? "").trim();
|
|
1056
|
+
}
|
|
1057
|
+
|
|
989
1058
|
/** Chips are visible only before the owner has said anything. */
|
|
990
1059
|
function promptsFor(msgs: AriaBubble[], prompts: AriaPromptChip[]): AriaPromptChip[] {
|
|
991
1060
|
return msgs.some((b) => b.role === "USER") ? [] : prompts;
|
|
@@ -1620,6 +1689,8 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
|
|
|
1620
1689
|
listening: false, micHidden: !o.dictation, listeningHidden: true,
|
|
1621
1690
|
widget: NO_WIDGET, mention: mentionViewNow,
|
|
1622
1691
|
attachments: [], attachmentsHidden: true,
|
|
1692
|
+
scrollSend: "", scrollAnchor: "", _pinActive: false,
|
|
1693
|
+
heroHidden: true, charThinkingHidden: true, charTalkingHidden: true, statusHidden: true, promptsGapHidden: false,
|
|
1623
1694
|
quickReplies: [], quickRepliesHidden: true, _realtime: "off",
|
|
1624
1695
|
};
|
|
1625
1696
|
|
|
@@ -2020,6 +2091,34 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
|
|
|
2020
2091
|
return { confirm: (ask) => t.confirm({ message: consentBody(ask.message) }), userAgent: t.userAgent };
|
|
2021
2092
|
};
|
|
2022
2093
|
|
|
2094
|
+
/** The owner's consent approvals, by conversation: the native Aria app's
|
|
2095
|
+
* `ConsentPolicy`, scoped the way the native app scopes it.
|
|
2096
|
+
*
|
|
2097
|
+
* - Per CONVERSATION and per `consentKey`. A key approved in one conversation
|
|
2098
|
+
* is asked again in another, and a new chat is a new conversation.
|
|
2099
|
+
* - Per BUSINESS too. The native policy lives in a chat session that is rebuilt
|
|
2100
|
+
* for every identity × business, so the site is part of the key here: an
|
|
2101
|
+
* approval never carries across a site switch.
|
|
2102
|
+
* - In memory only. Nothing is written to storage, nothing expires, and there
|
|
2103
|
+
* is nothing to revoke it with. It is gone when the session that holds it
|
|
2104
|
+
* goes: for native that is sign-out, a business switch or the app quitting,
|
|
2105
|
+
* and here it is the worker being torn down.
|
|
2106
|
+
*
|
|
2107
|
+
* Only approvals are recorded, and only after an explicit Confirm
|
|
2108
|
+
* (`answerClientTool`). */
|
|
2109
|
+
const consentApprovals = new Map<string, Set<string>>();
|
|
2110
|
+
const consentMemoryFor = (site: string, convId: string): ConsentMemory => {
|
|
2111
|
+
const scope = `${site}\n${convId}`;
|
|
2112
|
+
return {
|
|
2113
|
+
approved: (key) => consentApprovals.get(scope)?.has(key) ?? false,
|
|
2114
|
+
remember: (key) => {
|
|
2115
|
+
const keys = consentApprovals.get(scope) ?? new Set<string>();
|
|
2116
|
+
keys.add(key);
|
|
2117
|
+
consentApprovals.set(scope, keys);
|
|
2118
|
+
},
|
|
2119
|
+
};
|
|
2120
|
+
};
|
|
2121
|
+
|
|
2023
2122
|
/** Answer the client tools a send's response asked for, WITHOUT holding the
|
|
2024
2123
|
* send open.
|
|
2025
2124
|
*
|
|
@@ -2033,7 +2132,7 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
|
|
|
2033
2132
|
* the composer — can be reached while the owner is being asked. The epoch
|
|
2034
2133
|
* check still guards the result: if the turn has moved on regardless, the
|
|
2035
2134
|
* answer is not sent (`runClientToolExchange`, "abandoned"). */
|
|
2036
|
-
function startToolExchange(convId: string, first: SendMessageResponse): void {
|
|
2135
|
+
function startToolExchange(convId: string, site: string, first: SendMessageResponse): void {
|
|
2037
2136
|
if (!functionCallsOf(first).length) return;
|
|
2038
2137
|
const seq = turnSeq;
|
|
2039
2138
|
const current = () => turnSeq === seq;
|
|
@@ -2049,6 +2148,7 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
|
|
|
2049
2148
|
noteRows(first);
|
|
2050
2149
|
mine.done = runClientToolExchange(first, {
|
|
2051
2150
|
host: toolHost(),
|
|
2151
|
+
memory: consentMemoryFor(site, convId),
|
|
2052
2152
|
callsOf: functionCallsOf,
|
|
2053
2153
|
beforeSend: async () => {
|
|
2054
2154
|
const now = await assistantRowsNow(convId);
|
|
@@ -2507,6 +2607,9 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
|
|
|
2507
2607
|
// reason a scrollback widget keeps its prose and loses its controls.
|
|
2508
2608
|
widget: NO_WIDGET,
|
|
2509
2609
|
...retireDictation(),
|
|
2610
|
+
// Transcript scroll — a send: the host pins this question near the top and lets the
|
|
2611
|
+
// reply grow into the space beneath it.
|
|
2612
|
+
scrollSend: nextScrollToken(s.scrollSend), _pinActive: true,
|
|
2510
2613
|
};
|
|
2511
2614
|
inFlightSend = content;
|
|
2512
2615
|
// Held outside the try so a FAILED send still reports the conversation it
|
|
@@ -2672,7 +2775,7 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
|
|
|
2672
2775
|
const sent: AriaChatState = { ...marked, _abortId: abortId, _turnId: abortId };
|
|
2673
2776
|
inFlightSend = null;
|
|
2674
2777
|
// After the second bump, so the exchange is keyed to THIS turn's epoch.
|
|
2675
|
-
startToolExchange(convId, answer);
|
|
2778
|
+
startToolExchange(convId, site, answer);
|
|
2676
2779
|
if (o.poll) {
|
|
2677
2780
|
// Return NOW. The host owns the clock from here; `tick` merges the
|
|
2678
2781
|
// reply in when it lands.
|
|
@@ -2799,12 +2902,28 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
|
|
|
2799
2902
|
const listening = s.listening === true;
|
|
2800
2903
|
const micHidden = !o.dictation || dictationAvailable === false || listening;
|
|
2801
2904
|
const listeningHidden = !listening;
|
|
2802
|
-
//
|
|
2905
|
+
// The transcript scroll + the character, derived here for the reason the gates above
|
|
2906
|
+
// are: one exit every action passes through, so no reducer can leave a stale
|
|
2907
|
+
// anchor pointing at a row that has since been replaced, or a character still
|
|
2908
|
+
// thinking after the reply landed.
|
|
2909
|
+
const pinRow = s._pinActive && o.screenId ? newestUserBubble(s.messages) : undefined;
|
|
2910
|
+
const scrollAnchor = pinRow ? ariaMessageRowId(o.screenId!, pinRow.id) : "";
|
|
2911
|
+
const inFlight = turnInFlight(s);
|
|
2912
|
+
const talking = inFlight && replyStarted(s.messages);
|
|
2913
|
+
const charThinkingHidden = !(inFlight && !talking);
|
|
2914
|
+
const charTalkingHidden = !talking;
|
|
2915
|
+
const heroHidden = !(s.prompts && s.prompts.length > 0);
|
|
2916
|
+
const promptsGapHidden = !heroHidden;
|
|
2803
2917
|
const messagesHidden = s.messages.length === 0;
|
|
2918
|
+
const statusHidden = !s.statusText && charThinkingHidden && charTalkingHidden;
|
|
2804
2919
|
const actioned = s.stopHidden === hidden && s.replyActionsHidden === replyActionsHidden
|
|
2805
|
-
&& s.micHidden === micHidden && s.listeningHidden === listeningHidden
|
|
2920
|
+
&& s.micHidden === micHidden && s.listeningHidden === listeningHidden
|
|
2921
|
+
&& s.scrollAnchor === scrollAnchor && s.charThinkingHidden === charThinkingHidden
|
|
2922
|
+
&& s.charTalkingHidden === charTalkingHidden && s.heroHidden === heroHidden && s.messagesHidden === messagesHidden
|
|
2923
|
+
&& s.statusHidden === statusHidden && s.promptsGapHidden === promptsGapHidden
|
|
2806
2924
|
? s
|
|
2807
|
-
: { ...s, stopHidden: hidden, replyActionsHidden, micHidden, listeningHidden,
|
|
2925
|
+
: { ...s, stopHidden: hidden, replyActionsHidden, micHidden, listeningHidden,
|
|
2926
|
+
scrollAnchor, charThinkingHidden, charTalkingHidden, heroHidden, messagesHidden, statusHidden, promptsGapHidden };
|
|
2808
2927
|
// The attachment projection rides the same exit, for the same reason: one
|
|
2809
2928
|
// place that every action's result passes through, so no reducer can hand
|
|
2810
2929
|
// back a stale copy of the chips. Seeded first, so the very first action of
|
|
@@ -3013,7 +3132,9 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
|
|
|
3013
3132
|
// shown at once; a note carried over would say "Copied" beside an
|
|
3014
3133
|
// answer from a different conversation that nobody copied.
|
|
3015
3134
|
return leaving(await withWidgetItems({ ...s, status: "ready", _convId: resumeId, _siteId: site, _pending: "", statusText: "", error: "", copyNote: "",
|
|
3016
|
-
messages: resumed, ...observed(raw, s.widget), prompts: promptsFor(resumed, promptList())
|
|
3135
|
+
messages: resumed, ...observed(raw, s.widget), prompts: promptsFor(resumed, promptList()),
|
|
3136
|
+
// Transcript scroll — open a resumed conversation at its latest turn, pinning nothing.
|
|
3137
|
+
...openedAtLatest(s, resumed) }));
|
|
3017
3138
|
} catch { /* expired or unreachable — fall through to a fresh one */ }
|
|
3018
3139
|
}
|
|
3019
3140
|
// Opening a screen runs `load` more than once — the bundle self-kicks at
|
|
@@ -3042,15 +3163,18 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
|
|
|
3042
3163
|
// here, and a chip pack that localizes beside a greeting that does not
|
|
3043
3164
|
// is the visible half of the same bug.
|
|
3044
3165
|
const messages = isGreetingOnly(s.messages) ? greetingOnly() : withSpoken(s.messages);
|
|
3045
|
-
|
|
3166
|
+
// Transcript scroll — a conversation rehydrated with no send token (state persisted
|
|
3167
|
+
// before tokens existed) still opens at its latest turn.
|
|
3168
|
+
const opened = s.scrollSend || isGreetingOnly(messages) ? {} : openedAtLatest(s, messages);
|
|
3169
|
+
return { ...s, status: "ready", messages, prompts: promptsFor(messages, promptList()), ...opened };
|
|
3046
3170
|
}
|
|
3047
3171
|
try {
|
|
3048
3172
|
return leaving({ ...s, status: "ready", _convId: await createOnce(site), _siteId: site, _pending: "", statusText: "",
|
|
3049
|
-
messages: greetingOnly(), ...observed([]), prompts: promptList() });
|
|
3173
|
+
messages: greetingOnly(), ...observed([]), prompts: promptList(), ...SCROLL_RESET });
|
|
3050
3174
|
} catch (e) {
|
|
3051
3175
|
// Even if create fails, show the greeting; submit retries the create.
|
|
3052
3176
|
return leaving({ ...s, status: "ready", _convId: "", _siteId: site, _pending: "", ...failureSlots(e, unavailableText),
|
|
3053
|
-
messages: greetingOnly(), ...observed([]), prompts: promptList() });
|
|
3177
|
+
messages: greetingOnly(), ...observed([]), prompts: promptList(), ...SCROLL_RESET });
|
|
3054
3178
|
}
|
|
3055
3179
|
},
|
|
3056
3180
|
|
|
@@ -3142,11 +3266,11 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
|
|
|
3142
3266
|
// previous one, which reads as ➕ having done nothing.
|
|
3143
3267
|
turnSeq += 1;
|
|
3144
3268
|
return slow({ ...s, ...retired, status: "ready", _convId: conversationIdOf(res), _siteId: site, input: "", statusText: "", error: "",
|
|
3145
|
-
_pending: "", _abortId: "", _turnId: "", copyNote: "", messages: greetingOnly(), ...observed([]), prompts: promptList(), ok: false });
|
|
3269
|
+
_pending: "", _abortId: "", _turnId: "", copyNote: "", messages: greetingOnly(), ...observed([]), prompts: promptList(), ok: false, ...SCROLL_RESET });
|
|
3146
3270
|
} catch (e) {
|
|
3147
3271
|
turnSeq += 1; // see above — a failed create commits state too
|
|
3148
3272
|
return slow({ ...s, ...retired, status: "ready", _convId: "", _siteId: site, input: "", ...failureSlots(e, unavailableText),
|
|
3149
|
-
_pending: "", _abortId: "", _turnId: "", copyNote: "", messages: greetingOnly(), ...observed([]), prompts: promptList(), ok: false });
|
|
3273
|
+
_pending: "", _abortId: "", _turnId: "", copyNote: "", messages: greetingOnly(), ...observed([]), prompts: promptList(), ok: false, ...SCROLL_RESET });
|
|
3150
3274
|
}
|
|
3151
3275
|
},
|
|
3152
3276
|
|
|
@@ -21,11 +21,16 @@
|
|
|
21
21
|
// * approval needs an explicit tap. Anything else — a dismissal, a host with
|
|
22
22
|
// nothing to ask with, a presenter that threw — is NOT consent; the first
|
|
23
23
|
// is answered as a refusal and the others as a failure, never as a yes.
|
|
24
|
-
// *
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
24
|
+
// * an approval is remembered exactly as the native Aria app remembers it,
|
|
25
|
+
// and no further: per conversation, keyed by the call's `consentKey`,
|
|
26
|
+
// approvals only (a refusal is asked again), held in memory only, with no
|
|
27
|
+
// expiry, and gone with the session that holds it. A call without a
|
|
28
|
+
// `consentKey` — or with an empty or blank one, where the native clients
|
|
29
|
+
// are laxer — is never remembered. A remembered approval is answered
|
|
30
|
+
// without asking and says so on the wire (`rememberedConsent: true`), so
|
|
31
|
+
// the agent can tell a fresh tap from a standing one. The memory itself
|
|
32
|
+
// is the caller's (`ConsentMemory`): this file reads and writes it, but
|
|
33
|
+
// what counts as "the same conversation" is the caller's to say.
|
|
29
34
|
// * the BUTTONS are ours. The agent may suggest `approveText`/`denyText`, and
|
|
30
35
|
// they are ignored: an approve button labelled "Cancel" is how a model that
|
|
31
36
|
// cannot forge the tap gets the owner to make it.
|
|
@@ -58,6 +63,26 @@ export interface ClientToolCall {
|
|
|
58
63
|
params: Record<string, unknown>;
|
|
59
64
|
}
|
|
60
65
|
|
|
66
|
+
/** Approvals already given, by `consentKey`, in ONE conversation: the native
|
|
67
|
+
* app's `ConsentPolicy`. The caller hands in the memory of the conversation the
|
|
68
|
+
* call belongs to; this module never decides the scope. */
|
|
69
|
+
export interface ConsentMemory {
|
|
70
|
+
/** Was `consentKey` approved earlier in this conversation? */
|
|
71
|
+
approved: (consentKey: string) => boolean;
|
|
72
|
+
/** Record an approval. Called only after an explicit Confirm. */
|
|
73
|
+
remember: (consentKey: string) => void;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** The key a consent call can be remembered under, or `undefined` when it has
|
|
77
|
+
* none. Both native clients accept any string here, the empty one included
|
|
78
|
+
* (`params["consentKey"] as? String`). We do not: an empty or blank key names
|
|
79
|
+
* no action at all, and remembering it would let one approval wave through
|
|
80
|
+
* every later keyless-in-all-but-name request in the conversation. */
|
|
81
|
+
export function consentKeyOf(call: ClientToolCall): string | undefined {
|
|
82
|
+
const k = call.params.consentKey;
|
|
83
|
+
return typeof k === "string" && k.trim() !== "" ? k : undefined;
|
|
84
|
+
}
|
|
85
|
+
|
|
61
86
|
/** What the host must provide to run the built-ins. Omit it and every call is
|
|
62
87
|
* answered "not found" — honest, and it unblocks the agent. */
|
|
63
88
|
export interface AriaClientTools {
|
|
@@ -123,8 +148,14 @@ export function failedResult(call: { id?: string; name?: string }, message: stri
|
|
|
123
148
|
|
|
124
149
|
/** The owner's answer. A refusal is still a SUCCESSFUL tool run — the tool's job
|
|
125
150
|
* was to ask, and it did; the model replies to the refusal. */
|
|
126
|
-
export function consentResult(call: ClientToolCall, approved: boolean): FunctionCallResult {
|
|
127
|
-
|
|
151
|
+
export function consentResult(call: ClientToolCall, approved: boolean, remembered = false): FunctionCallResult {
|
|
152
|
+
// `rememberedConsent` is written only when it is true, which is the native
|
|
153
|
+
// clients' exact payload: a fresh answer is byte-for-byte what it was before.
|
|
154
|
+
// It is OURS to set: one the agent put in the call's own arguments is dropped,
|
|
155
|
+
// so a fresh tap can never go out claiming to be a standing approval.
|
|
156
|
+
const { rememberedConsent: _agentSupplied, ...params } = call.params;
|
|
157
|
+
const payload = { ...params, userConsent: approved, ...(remembered ? { rememberedConsent: true } : {}) };
|
|
158
|
+
return { ...addressOf(call), success: true, widgetResponse: JSON.stringify(payload) };
|
|
128
159
|
}
|
|
129
160
|
|
|
130
161
|
export function knowledgeResult(call: ClientToolCall, userAgent: string): FunctionCallResult {
|
|
@@ -134,11 +165,21 @@ export function knowledgeResult(call: ClientToolCall, userAgent: string): Functi
|
|
|
134
165
|
/** Answer ONE call. Never rejects: every outcome, including a host that threw,
|
|
135
166
|
* becomes a result the agent can read, because an unanswered call is a turn
|
|
136
167
|
* that never ends. */
|
|
137
|
-
export async function answerClientTool(
|
|
168
|
+
export async function answerClientTool(
|
|
169
|
+
call: FunctionCall,
|
|
170
|
+
host: AriaClientTools | undefined,
|
|
171
|
+
memory?: ConsentMemory,
|
|
172
|
+
): Promise<FunctionCallResult> {
|
|
138
173
|
const parsed = parseClientToolCall(call);
|
|
139
174
|
if (!parsed) return failedResult(call && typeof call === "object" ? call : {}, "arguments are not a JSON object");
|
|
140
175
|
if (!host) return notFoundResult(parsed);
|
|
141
176
|
if (parsed.toolId === CONSENT_TOOL) {
|
|
177
|
+
const key = consentKeyOf(parsed);
|
|
178
|
+
// The memory is checked before the owner is asked. A key approved earlier in
|
|
179
|
+
// this conversation is answered without a second question, as the native
|
|
180
|
+
// `ConsentCoordinator.request` does: it checks its policy first and returns
|
|
181
|
+
// `remembered: true` without showing anything.
|
|
182
|
+
if (key !== undefined && memory?.approved(key)) return consentResult(parsed, true, true);
|
|
142
183
|
const message = typeof parsed.params.message === "string" ? parsed.params.message : "";
|
|
143
184
|
let approved: boolean;
|
|
144
185
|
try {
|
|
@@ -150,6 +191,10 @@ export async function answerClientTool(call: FunctionCall, host: AriaClientTools
|
|
|
150
191
|
} catch (e) {
|
|
151
192
|
return failedResult(parsed, `the owner could not be asked (${String((e as Error)?.message ?? e)})`);
|
|
152
193
|
}
|
|
194
|
+
// Recorded the moment the owner taps Confirm, as the native coordinator's
|
|
195
|
+
// `resolve` records it: before the result is sent, so it holds even for a
|
|
196
|
+
// turn that is then abandoned. A refusal is never recorded; it is asked again.
|
|
197
|
+
if (approved && key !== undefined) memory?.remember(key);
|
|
153
198
|
return consentResult(parsed, approved);
|
|
154
199
|
}
|
|
155
200
|
if (parsed.toolId === KNOWLEDGE_TOOL) {
|
|
@@ -182,6 +227,9 @@ export type ClientToolOutcome =
|
|
|
182
227
|
|
|
183
228
|
export interface ClientToolExchange {
|
|
184
229
|
host?: AriaClientTools;
|
|
230
|
+
/** The approvals of the conversation this exchange runs in. Omit it and every
|
|
231
|
+
* consent is asked. */
|
|
232
|
+
memory?: ConsentMemory;
|
|
185
233
|
/** The calls a response is waiting on — `functionCallsOf` from the chat client. */
|
|
186
234
|
callsOf: (res: SendMessageResponse) => FunctionCall[];
|
|
187
235
|
/** Send one round of results; resolves with the service's next response. */
|
|
@@ -222,7 +270,7 @@ export async function runClientToolExchange(first: SendMessageResponse, x: Clien
|
|
|
222
270
|
if (!x.stillCurrent()) return "abandoned";
|
|
223
271
|
if (calls.length > 1) throw clientToolError(`the assistant asked for ${calls.length} client tools at once`);
|
|
224
272
|
if (rounds >= max) throw clientToolError(`the assistant asked for more than ${max} client tools in one turn`);
|
|
225
|
-
const result = await answerClientTool(calls[0], x.host);
|
|
273
|
+
const result = await answerClientTool(calls[0], x.host, x.memory);
|
|
226
274
|
if (!x.stillCurrent()) return "abandoned";
|
|
227
275
|
if (x.beforeSend) {
|
|
228
276
|
await x.beforeSend();
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// The id a message row carries, written ONCE for both halves of Aria: the screen
|
|
2
|
+
// builder (aria-chat-screen.ts) stamps it on the row template as
|
|
3
|
+
// `ariaMessageRowId(screenId, "$item.id")`, and the worker (aria-chat.ts) names the row
|
|
4
|
+
// the screen's `transcriptScroll` should pin as `ariaMessageRowId(screenId, bubble.id)`.
|
|
5
|
+
// A row renamed on one side and not the other would pin nothing, silently — so neither
|
|
6
|
+
// side spells the format itself.
|
|
7
|
+
//
|
|
8
|
+
// Its own module, not a member of aria-chat.ts, because the screen builder runs on the
|
|
9
|
+
// server that builds descriptors and must not pull the whole conversation machine (and
|
|
10
|
+
// its SDK imports) in to read one template string.
|
|
11
|
+
|
|
12
|
+
export function ariaMessageRowId(screenId: string, bubbleId: string): string {
|
|
13
|
+
return `${screenId}.msg_${bubbleId}`;
|
|
14
|
+
}
|
|
@@ -186,6 +186,9 @@ const core = createAriaChat({
|
|
|
186
186
|
// rejected by the descriptor validator, so omitting this is the correct
|
|
187
187
|
// behaviour there and the card degrades to prose.
|
|
188
188
|
subtree: { slotId: WIDGET_SUBTREE_SLOT, service: SERVICE },
|
|
189
|
+
// Transcript scroll: names the row the transcript scroll pins on a send. Must be the
|
|
190
|
+
// descriptor's screen id — the row ids are `<screenId>.msg_<bubble id>`.
|
|
191
|
+
screenId: "himi_owner_lite_aria",
|
|
189
192
|
greeting: () => copyParam(
|
|
190
193
|
"ownerAria.greeting",
|
|
191
194
|
"Hi, I'm Aria, your business and web design expert.\nI'm here to help you complete tasks, answer questions, create content and guide you to useful features.",
|