@wix/himalaya-cli 0.1313.0 → 0.1314.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/stdlib/wix-sdk/genie-chat/src/index.d.ts +44 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-genie-chat.mjs +25 -0
- package/dist/cli.mjs +4 -2
- package/guides/sdk-reference.json +36 -0
- package/package.json +2 -2
- package/templates/beauty-salon-owner/dev/index.ts +23 -1
- package/templates/beauty-salon-owner/tier5-src/_shared/aria-chat.ts +364 -18
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-aria/index.ts +22 -0
- package/templates/community-club-owner/dev/index.ts +23 -1
- package/templates/community-club-owner/tier5-src/_shared/aria-chat.ts +364 -18
- package/templates/community-club-owner/tier5-src/himi-owner-lite-aria/index.ts +22 -0
- package/templates/events-meetups-owner/dev/index.ts +23 -1
- package/templates/events-meetups-owner/tier5-src/_shared/aria-chat.ts +364 -18
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-aria/index.ts +22 -0
- package/templates/gym-fitness-owner/dev/index.ts +23 -1
- package/templates/gym-fitness-owner/tier5-src/_shared/aria-chat.ts +364 -18
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-aria/index.ts +22 -0
- package/templates/nonprofit-cause-owner/dev/index.ts +23 -1
- package/templates/nonprofit-cause-owner/tier5-src/_shared/aria-chat.ts +364 -18
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-aria/index.ts +22 -0
- package/templates/owner-lite/dev/index.ts +23 -1
- package/templates/owner-lite/tier5-src/_shared/aria-chat.ts +364 -18
- package/templates/owner-lite/tier5-src/himi-owner-lite-aria/index.ts +22 -0
- package/templates/podcast-creator-owner/dev/index.ts +23 -1
- package/templates/podcast-creator-owner/tier5-src/_shared/aria-chat.ts +364 -18
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-aria/index.ts +22 -0
- package/templates/portfolio-creative-owner/dev/index.ts +23 -1
- package/templates/portfolio-creative-owner/tier5-src/_shared/aria-chat.ts +364 -18
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-aria/index.ts +22 -0
- package/templates/restaurant-owner/dev/index.ts +23 -1
- package/templates/restaurant-owner/tier5-src/_shared/aria-chat.ts +364 -18
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-aria/index.ts +22 -0
- package/templates/retail-store-owner/dev/index.ts +23 -1
- package/templates/retail-store-owner/tier5-src/_shared/aria-chat.ts +364 -18
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-aria/index.ts +22 -0
- package/templates/yoga-studio-owner/dev/index.ts +23 -1
- package/templates/yoga-studio-owner/tier5-src/_shared/aria-chat.ts +364 -18
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-aria/index.ts +22 -0
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
functionCallsOf,
|
|
38
38
|
getConversationMessages,
|
|
39
39
|
getConversations,
|
|
40
|
+
getSuggestedActions,
|
|
40
41
|
itemsOf,
|
|
41
42
|
listInstalledProviders,
|
|
42
43
|
mentionReference,
|
|
@@ -46,6 +47,7 @@ import {
|
|
|
46
47
|
queryItems,
|
|
47
48
|
sendFunctionCallResults,
|
|
48
49
|
sendMessage,
|
|
50
|
+
suggestedActionsOf,
|
|
49
51
|
triggerAssistantMessage,
|
|
50
52
|
uploadTargetUrl,
|
|
51
53
|
userMessageIdOf,
|
|
@@ -186,6 +188,26 @@ export interface AriaChatState {
|
|
|
186
188
|
messagesHidden: boolean;
|
|
187
189
|
/** Non-empty ONLY on the greeting-only empty state. */
|
|
188
190
|
prompts: AriaPromptChip[];
|
|
191
|
+
/** Collapses the loading placeholder that stands in for `prompts` while this
|
|
192
|
+
* conversation's suggestions are being fetched (`AriaChatOptions.suggestions`).
|
|
193
|
+
* Derived in `gated`, like `stopHidden`: shown only while a fetch is out AND
|
|
194
|
+
* the owner has not said anything yet. */
|
|
195
|
+
promptsLoadingHidden: boolean;
|
|
196
|
+
/** The service's suggestions for THIS conversation, as cards — fetched once
|
|
197
|
+
* when the conversation's empty state was minted, and kept with it, so a later
|
|
198
|
+
* `load` or `tick` redraws the same cards instead of asking again. Empty means
|
|
199
|
+
* "none": the app's own `prompts` are drawn instead. */
|
|
200
|
+
_suggested: AriaPromptChip[];
|
|
201
|
+
/** The fetch this state is waiting on, or "". Only the outcome carrying THIS
|
|
202
|
+
* token may land — one minted for a conversation the owner has since left
|
|
203
|
+
* (➕, a business switch) is dropped. */
|
|
204
|
+
_suggestToken: string;
|
|
205
|
+
/** Whether THIS conversation has been through the suggestions step at all.
|
|
206
|
+
* False only on a conversation minted before an app passed `suggestions` —
|
|
207
|
+
* a persisted snapshot from an older bundle, overlaid on these defaults at a
|
|
208
|
+
* cold boot. Its empty `_suggested` would otherwise read as "the service had
|
|
209
|
+
* none" and keep the fallback cards on screen until the owner pressed ➕. */
|
|
210
|
+
_suggestAsked: boolean;
|
|
189
211
|
_convId: string;
|
|
190
212
|
/** The metaSite the conversation belongs to. Conversations are scoped to the
|
|
191
213
|
* signed instance that created them, so a site switch must not resume one. */
|
|
@@ -524,7 +546,76 @@ export interface AriaAttachmentPorts {
|
|
|
524
546
|
settled(outcome: AriaAttachmentOutcome): void;
|
|
525
547
|
}
|
|
526
548
|
|
|
549
|
+
/** Server-provided prompt cards — the chat-service's sibling knowledge service
|
|
550
|
+
* (`getSuggestedActions`), which the native Aria app fetches once per
|
|
551
|
+
* conversation, keeps the top 3 of, and caches for an hour.
|
|
552
|
+
*
|
|
553
|
+
* Omit it and the empty state shows `AriaChatOptions.prompts`, exactly as before.
|
|
554
|
+
* Supply it and those become the FALLBACK: drawn whenever the service answers
|
|
555
|
+
* nothing or fails — which in a published web app is always, because it refuses
|
|
556
|
+
* a url off the gateway (`webwix_owner_only`). */
|
|
557
|
+
export interface AriaSuggestionOptions {
|
|
558
|
+
/** Hand a finished fetch back to the service as a NEW action
|
|
559
|
+
* (`suggestionsSettled`), with the token it was started under. A fresh dispatch
|
|
560
|
+
* rather than a return value for the reason `AriaAttachmentPorts.settled` is
|
|
561
|
+
* one: the fetch must not hold the greeting off the screen, so the action that
|
|
562
|
+
* starts it returns first, with a loading placeholder where the cards go.
|
|
563
|
+
*
|
|
564
|
+
* Return the dispatch's promise. A settle can arrive in the instant between a
|
|
565
|
+
* newer action computing its result and the runtime committing it, when there
|
|
566
|
+
* is nothing it may safely change; the worker then dispatches it again once
|
|
567
|
+
* that promise resolves, and stops as soon as the cards have landed. */
|
|
568
|
+
settled(token: string): void | Promise<unknown>;
|
|
569
|
+
/** The owner's language, as BCP-47. Part of the cache key. Default: whatever
|
|
570
|
+
* the worker runtime reports (`runtimeLocale`), else `"en"`. */
|
|
571
|
+
locale?: () => string;
|
|
572
|
+
/** The glyph on every server card. The service sends none, and the card has a
|
|
573
|
+
* slot for one; the app's own cards keep their own. */
|
|
574
|
+
emoji?: string;
|
|
575
|
+
/** WHO the suggestions are for — the signed-in account, as any stable string.
|
|
576
|
+
* The service answers on the account's cookie, so its suggestions belong to the
|
|
577
|
+
* account, not only to the site: two owners of one business can be told
|
|
578
|
+
* different things. The cache is keyed on this as well as site + locale, and is
|
|
579
|
+
* emptied whenever it changes, so a worker that outlives a sign-out never shows
|
|
580
|
+
* the previous owner's cards.
|
|
581
|
+
*
|
|
582
|
+
* Omit it, or let it resolve `""` or throw, and the account is UNKNOWN: every
|
|
583
|
+
* conversation then asks the service afresh and nothing is cached, because a
|
|
584
|
+
* cache that cannot tell two accounts apart must not be shared between them. */
|
|
585
|
+
principal?: () => string | Promise<string>;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/** How many suggestions become cards. The native Aria app's number. */
|
|
589
|
+
export const ARIA_SUGGESTION_LIMIT = 3;
|
|
590
|
+
/** How long one site + locale's suggestions are reused before being asked for
|
|
591
|
+
* again. The native Aria app's number. */
|
|
592
|
+
export const ARIA_SUGGESTION_TTL_MS = 60 * 60 * 1000;
|
|
593
|
+
/** How many conversations' suggestion fetches the worker keeps an answer for
|
|
594
|
+
* until they land. Overlapping actions mint one each; only the oldest are
|
|
595
|
+
* pruned past this, never a newer one's. */
|
|
596
|
+
export const ARIA_SUGGESTION_PENDING_LIMIT = 8;
|
|
597
|
+
|
|
598
|
+
/** The owner's locale as the worker runtime knows it: the browser's on the web,
|
|
599
|
+
* `Intl` where the engine ships it (JavaScriptCore, Hermes), else `"en"` — the
|
|
600
|
+
* service's own default, and what QuickJS (no `Intl`) falls back to. Never
|
|
601
|
+
* throws. */
|
|
602
|
+
export function runtimeLocale(): string {
|
|
603
|
+
try {
|
|
604
|
+
const nav = (globalThis as { navigator?: { language?: unknown } }).navigator;
|
|
605
|
+
if (typeof nav?.language === "string" && nav.language.trim()) return nav.language.trim();
|
|
606
|
+
} catch { /* no navigator */ }
|
|
607
|
+
try {
|
|
608
|
+
const intl = (globalThis as { Intl?: { DateTimeFormat?: () => { resolvedOptions(): { locale?: unknown } } } }).Intl;
|
|
609
|
+
const loc = intl?.DateTimeFormat?.().resolvedOptions().locale;
|
|
610
|
+
if (typeof loc === "string" && loc.trim()) return loc.trim();
|
|
611
|
+
} catch { /* no Intl */ }
|
|
612
|
+
return "en";
|
|
613
|
+
}
|
|
614
|
+
|
|
527
615
|
export interface AriaChatOptions {
|
|
616
|
+
/** Server-provided prompt cards, with `prompts` as the fallback. See
|
|
617
|
+
* `AriaSuggestionOptions`. */
|
|
618
|
+
suggestions?: AriaSuggestionOptions;
|
|
528
619
|
/** Photo attachments in the composer. Omit it and the attach actions are
|
|
529
620
|
* inert — which is also what the descriptor does, since it paints no attach
|
|
530
621
|
* control unless the app passes the matching actions. */
|
|
@@ -1496,6 +1587,12 @@ export interface AriaChatCore {
|
|
|
1496
1587
|
* carries the rest of the turn, at the full rate. */
|
|
1497
1588
|
streamEnded(s: AriaChatState): AriaChatState;
|
|
1498
1589
|
streamFailed(s: AriaChatState, err?: unknown): AriaChatState;
|
|
1590
|
+
/** A suggestions fetch finished (`AriaSuggestionOptions.settled`). The arg names
|
|
1591
|
+
* the token it was started under (`{ id }` or the bare string); the cards
|
|
1592
|
+
* themselves never ride the dispatch — only the token does. An outcome for a
|
|
1593
|
+
* conversation the owner has since left changes nothing — the action then
|
|
1594
|
+
* resolves `undefined`, which leaves the stored state untouched. */
|
|
1595
|
+
suggestionsSettled(s: AriaChatState, arg?: AriaActionArg): AriaChatState | undefined;
|
|
1499
1596
|
}
|
|
1500
1597
|
|
|
1501
1598
|
const POLL_ID = "aria-turn";
|
|
@@ -1576,6 +1673,211 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
|
|
|
1576
1673
|
};
|
|
1577
1674
|
const toolFailedText = () => res(o.labels?.toolFailed ?? `${assistant()} couldn't finish that. Try again.`);
|
|
1578
1675
|
const promptList = () => res(o.prompts).map((p) => ({ ...p, label: promptLabel(p) }));
|
|
1676
|
+
|
|
1677
|
+
// ---- Server suggestions (see `AriaSuggestionOptions`) --------------------
|
|
1678
|
+
//
|
|
1679
|
+
// The cache lives HERE, in worker memory, keyed by site + locale and aged by
|
|
1680
|
+
// `Date.now()` — a worker has no timer, so "cached for an hour" is a timestamp
|
|
1681
|
+
// compared when the next conversation asks, never an expiry that fires. It does
|
|
1682
|
+
// not survive the process, which is the right failure: a cold start asks once.
|
|
1683
|
+
// What DOES survive is each conversation's own cards (`_suggested`), because
|
|
1684
|
+
// they ride the persisted state — so a conversation never changes its cards
|
|
1685
|
+
// under the owner, even when the cache entry it came from expires.
|
|
1686
|
+
const suggestionCache = new Map<string, { at: number; chips: AriaPromptChip[] }>();
|
|
1687
|
+
/** One request per account + site + locale at a time: a `load` and a ➕ racing
|
|
1688
|
+
* share it. */
|
|
1689
|
+
const suggestionFetches = new Map<string, Promise<AriaPromptChip[]>>();
|
|
1690
|
+
/** The account the cache and the in-flight fetches belong to. Both are emptied
|
|
1691
|
+
* the moment a different one asks (`AriaSuggestionOptions.principal`). */
|
|
1692
|
+
let suggestionPrincipal = "";
|
|
1693
|
+
/** The signed-in account, or `""` when it cannot be told. Never throws. */
|
|
1694
|
+
const principalNow = async (): Promise<string> => {
|
|
1695
|
+
const read = o.suggestions?.principal;
|
|
1696
|
+
if (!read) return "";
|
|
1697
|
+
try { return String((await read()) ?? "").trim(); } catch { return ""; }
|
|
1698
|
+
};
|
|
1699
|
+
/** What each token's fetch came back with, and WHOSE it is: the business,
|
|
1700
|
+
* account and language it was asked for. `chips` is `null` while the fetch is
|
|
1701
|
+
* out; a failure is recorded as `[]`, so the app's own prompts are drawn.
|
|
1702
|
+
*
|
|
1703
|
+
* An outcome lands only on a state of exactly its own scope (see
|
|
1704
|
+
* `withSettledSuggestions`) — never on a state whose business, account or
|
|
1705
|
+
* language has moved on, however the commits interleave. And a placeholder
|
|
1706
|
+
* whose token is not here at all (pruned, from a previous process, or dropped
|
|
1707
|
+
* for its scope) is never left waiting: it falls back to the app's own cards
|
|
1708
|
+
* and the next `load` asks again. That guarantee is what lets this map be
|
|
1709
|
+
* bounded (ARIA_SUGGESTION_PENDING_LIMIT) without ever stranding a skeleton. */
|
|
1710
|
+
interface SuggestOutcome { site: string; principal: string; locale: string; convId: string; chips: AriaPromptChip[] | null }
|
|
1711
|
+
const suggestOutcomes = new Map<string, SuggestOutcome>();
|
|
1712
|
+
/** The language the suggestions are asked in right now. Never throws. */
|
|
1713
|
+
const localeNow = (): string => {
|
|
1714
|
+
try { return (o.suggestions?.locale ?? runtimeLocale)() || "en"; } catch { return "en"; }
|
|
1715
|
+
};
|
|
1716
|
+
/** Tokens whose cards have landed at least once — ends `hand`'s re-dispatch. */
|
|
1717
|
+
const landedTokens = new Set<string>();
|
|
1718
|
+
let suggestSeq = 0;
|
|
1719
|
+
const nowMs = (): number => { try { return Date.now(); } catch { return 0; } };
|
|
1720
|
+
const suggestionEmoji = o.suggestions?.emoji ?? "✨";
|
|
1721
|
+
/** The cards a conversation shows: its own suggestions when it has any, the
|
|
1722
|
+
* app's prompts otherwise — and NOTHING while its fetch is still out, where the
|
|
1723
|
+
* loading placeholder stands in. */
|
|
1724
|
+
const chipsOf = (s: Pick<AriaChatState, "_suggested" | "_suggestToken">): AriaPromptChip[] =>
|
|
1725
|
+
s._suggestToken ? [] : s._suggested && s._suggested.length ? s._suggested : promptList();
|
|
1726
|
+
/** `key` is `""` for an UNKNOWN account: the answer is then neither shared with
|
|
1727
|
+
* a racing request nor cached. */
|
|
1728
|
+
const fetchSuggestions = (key: string, locale: string): Promise<AriaPromptChip[]> => {
|
|
1729
|
+
const running = key ? suggestionFetches.get(key) : undefined;
|
|
1730
|
+
if (running) return running;
|
|
1731
|
+
const started = getSuggestedActions({ assistantId: o.assistantId, locale }).then((r) => {
|
|
1732
|
+
const list: AriaPromptChip[] = suggestedActionsOf(r).slice(0, ARIA_SUGGESTION_LIMIT).map((a, i) => {
|
|
1733
|
+
// `title` is what the card says and `text` is what it SENDS — the
|
|
1734
|
+
// service's `respondText`, a different string on purpose. `description`
|
|
1735
|
+
// is not drawn: the native Aria app's suggestion card shows the title
|
|
1736
|
+
// alone on both platforms (ChatEmptyState.swift / ChatEmptyState.kt in
|
|
1737
|
+
// wix-private/aria-native-apps), so `sub` stays empty and the card is one
|
|
1738
|
+
// line whether the service sends a description or not.
|
|
1739
|
+
const chip = { id: `sg${i + 1}`, emoji: suggestionEmoji, title: a.title, sub: "", text: a.respondText };
|
|
1740
|
+
return { ...chip, label: promptLabel(chip) };
|
|
1741
|
+
});
|
|
1742
|
+
// An empty answer is an answer, and is cached like any other. A FAILURE is
|
|
1743
|
+
// not: the next conversation asks again. Only an entry still registered as
|
|
1744
|
+
// in flight is cached — one the account switch below already dropped belongs
|
|
1745
|
+
// to the previous owner.
|
|
1746
|
+
if (key && suggestionFetches.get(key) === started) {
|
|
1747
|
+
suggestionCache.set(key, { at: nowMs(), chips: list });
|
|
1748
|
+
suggestionFetches.delete(key);
|
|
1749
|
+
}
|
|
1750
|
+
return list;
|
|
1751
|
+
}, (e: unknown) => {
|
|
1752
|
+
if (key && suggestionFetches.get(key) === started) suggestionFetches.delete(key);
|
|
1753
|
+
throw e;
|
|
1754
|
+
});
|
|
1755
|
+
if (key) suggestionFetches.set(key, started);
|
|
1756
|
+
return started;
|
|
1757
|
+
};
|
|
1758
|
+
/** `next` with this conversation's suggestions attached: from the cache when a
|
|
1759
|
+
* fresh entry exists, otherwise a fetch is started and `next` shows the
|
|
1760
|
+
* placeholder until `suggestionsSettled` lands. Called ONLY where a
|
|
1761
|
+
* conversation's empty state is minted — that is what makes the fetch once per
|
|
1762
|
+
* conversation rather than once per `load`. */
|
|
1763
|
+
const withSuggestions = async (next: AriaChatState, site: string): Promise<AriaChatState> => {
|
|
1764
|
+
const port = o.suggestions;
|
|
1765
|
+
if (!port) return next;
|
|
1766
|
+
// A conversation the owner has already spoken in shows no cards at all.
|
|
1767
|
+
if (next.messages.some((b) => b.role === "USER")) return { ...next, _suggested: [], _suggestToken: "", _suggestAsked: true };
|
|
1768
|
+
const locale = localeNow();
|
|
1769
|
+
// The answer is the ACCOUNT's (it rides the account cookie), so a different
|
|
1770
|
+
// account empties the cache rather than being served the last one's cards.
|
|
1771
|
+
const principal = await principalNow();
|
|
1772
|
+
if (principal !== suggestionPrincipal) {
|
|
1773
|
+
suggestionCache.clear();
|
|
1774
|
+
suggestionFetches.clear();
|
|
1775
|
+
suggestionPrincipal = principal;
|
|
1776
|
+
}
|
|
1777
|
+
const key = principal ? `${principal}|${site}|${locale}` : "";
|
|
1778
|
+
const hit = key ? suggestionCache.get(key) : undefined;
|
|
1779
|
+
const now = nowMs();
|
|
1780
|
+
// `now >= at` too: a clock that went BACKWARDS would make an entry look fresh
|
|
1781
|
+
// for as long as it stays behind, so it is treated as expired instead.
|
|
1782
|
+
if (hit && now >= hit.at && now - hit.at < ARIA_SUGGESTION_TTL_MS) {
|
|
1783
|
+
const ready = { ...next, _suggested: hit.chips, _suggestToken: "", _suggestAsked: true };
|
|
1784
|
+
return { ...ready, prompts: promptsFor(ready.messages, chipsOf(ready)) };
|
|
1785
|
+
}
|
|
1786
|
+
suggestSeq += 1;
|
|
1787
|
+
// Stamped with the clock as well as the counter: the counter restarts with the
|
|
1788
|
+
// process, and a persisted token from the previous one must not match a new one.
|
|
1789
|
+
const token = `sg-${now}-${suggestSeq}`;
|
|
1790
|
+
// Every token keeps its OWN entry, bound to its scope. Not "only the newest
|
|
1791
|
+
// can matter": actions are not serialized, so an older `load` or ➕ that minted
|
|
1792
|
+
// its token first can still COMMIT last, putting that older token on screen —
|
|
1793
|
+
// and its answer must then still be here to land (or be refused for its
|
|
1794
|
+
// scope). Kept after landing, too, so a stale commit that puts the token back
|
|
1795
|
+
// is cleared again by the next `gated`. Pruned only as the oldest past the
|
|
1796
|
+
// bound; a pruned token that is still on screen falls back rather than waits.
|
|
1797
|
+
// With NO identity to tell owners apart (`principal` is ""), the only thing
|
|
1798
|
+
// that changes when one owner signs out and another signs in on the same
|
|
1799
|
+
// business is the conversation: a sign-in opens a new one. So an
|
|
1800
|
+
// unknown-account answer is bound to the conversation it was asked for, and
|
|
1801
|
+
// a new conversation drops every other conversation's unknown-account answer
|
|
1802
|
+
// still out — a late one must never reach the next owner's screen. (A
|
|
1803
|
+
// dropped answer is not a stuck placeholder: see `withSettledSuggestions`.)
|
|
1804
|
+
if (!principal) {
|
|
1805
|
+
for (const [other, entry] of suggestOutcomes) {
|
|
1806
|
+
if (!entry.principal && entry.convId !== next._convId) {
|
|
1807
|
+
suggestOutcomes.delete(other);
|
|
1808
|
+
landedTokens.delete(other);
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
suggestOutcomes.set(token, { site, principal, locale, convId: next._convId, chips: null });
|
|
1813
|
+
while (suggestOutcomes.size > ARIA_SUGGESTION_PENDING_LIMIT) {
|
|
1814
|
+
const oldest = suggestOutcomes.keys().next().value;
|
|
1815
|
+
if (oldest === undefined) break;
|
|
1816
|
+
suggestOutcomes.delete(oldest);
|
|
1817
|
+
landedTokens.delete(oldest);
|
|
1818
|
+
}
|
|
1819
|
+
const hand = (chips: AriaPromptChip[]) => {
|
|
1820
|
+
const entry = suggestOutcomes.get(token);
|
|
1821
|
+
if (!entry) return;
|
|
1822
|
+
entry.chips = chips;
|
|
1823
|
+
// Dispatch the settle, and again after each one resolves while the outcome
|
|
1824
|
+
// is still waiting: the action that minted this token may have computed its
|
|
1825
|
+
// result but not yet had it committed, and a settle in that instant can only
|
|
1826
|
+
// leave the state alone (see `suggestionsSettled`). The outcome leaving the
|
|
1827
|
+
// map — landed, or dropped for a newer conversation — ends it; so does the
|
|
1828
|
+
// bound, after which the next commit applies it (`gated`).
|
|
1829
|
+
let tries = 0;
|
|
1830
|
+
const attempt = (): void => {
|
|
1831
|
+
if (suggestOutcomes.get(token)?.chips !== chips || landedTokens.has(token) || tries >= 5) return;
|
|
1832
|
+
tries += 1;
|
|
1833
|
+
let sent: unknown;
|
|
1834
|
+
try { sent = port.settled(token); } catch { return; }
|
|
1835
|
+
if (sent && typeof (sent as Promise<unknown>).then === "function") (sent as Promise<unknown>).then(attempt, () => {});
|
|
1836
|
+
};
|
|
1837
|
+
attempt();
|
|
1838
|
+
};
|
|
1839
|
+
fetchSuggestions(key, locale).then(hand, () => hand([]));
|
|
1840
|
+
return { ...next, _suggested: [], _suggestToken: token, _suggestAsked: true, prompts: [] };
|
|
1841
|
+
};
|
|
1842
|
+
/** `s` with its fetch's cards in place, when that fetch has finished; `s`
|
|
1843
|
+
* itself otherwise. Run on every commit (`gated`) as well as by
|
|
1844
|
+
* `suggestionsSettled`, so the cards land on whichever comes first — the
|
|
1845
|
+
* settle dispatch, or the commit of the action that started the fetch. */
|
|
1846
|
+
const withSettledSuggestions = (s: AriaChatState): AriaChatState => {
|
|
1847
|
+
const token = s._suggestToken;
|
|
1848
|
+
if (!token) return s;
|
|
1849
|
+
const entry = suggestOutcomes.get(token);
|
|
1850
|
+
// A placeholder nobody holds an answer for — pruned past the bound, minted by a
|
|
1851
|
+
// previous process, or dropped below for its scope — must not wait for good.
|
|
1852
|
+
// It shows the app's own cards now, and `_suggestAsked: false` makes the next
|
|
1853
|
+
// `load` ask again, for whatever scope it is in by then.
|
|
1854
|
+
if (!entry) return asksAgain(s);
|
|
1855
|
+
// The answer is ONE business's, account's and language's. A commit that puts
|
|
1856
|
+
// this token on a state of another scope — or after the owner has moved to
|
|
1857
|
+
// another business or signed in as someone else — must not show it there.
|
|
1858
|
+
// An unknown-account answer goes to its OWN conversation only, and never to one
|
|
1859
|
+
// with no id (a failed create), where nothing ties it to an owner.
|
|
1860
|
+
const unknownElsewhere = !entry.principal && (!entry.convId || entry.convId !== s._convId);
|
|
1861
|
+
if (entry.site !== s._siteId || entry.site !== siteOf() || entry.principal !== suggestionPrincipal
|
|
1862
|
+
|| entry.locale !== localeNow() || unknownElsewhere) {
|
|
1863
|
+
suggestOutcomes.delete(token);
|
|
1864
|
+
landedTokens.delete(token);
|
|
1865
|
+
return asksAgain(s);
|
|
1866
|
+
}
|
|
1867
|
+
// Still out: the placeholder is right where it is.
|
|
1868
|
+
if (!entry.chips) return s;
|
|
1869
|
+
// NOT deleted: a later commit of a state from before this one can carry the
|
|
1870
|
+
// same token back, and it must land again rather than wait forever.
|
|
1871
|
+
landedTokens.add(token);
|
|
1872
|
+
const ready: AriaChatState = { ...s, _suggestToken: "", _suggested: entry.chips };
|
|
1873
|
+
return { ...ready, prompts: promptsFor(ready.messages, chipsOf(ready)) };
|
|
1874
|
+
};
|
|
1875
|
+
/** `s` without its placeholder: the app's own cards, and asked for afresh on the
|
|
1876
|
+
* next `load` (which only re-asks a conversation that has not been asked). */
|
|
1877
|
+
const asksAgain = (s: AriaChatState): AriaChatState => {
|
|
1878
|
+
const fresh: AriaChatState = { ...s, _suggestToken: "", _suggested: [], _suggestAsked: false };
|
|
1879
|
+
return { ...fresh, prompts: promptsFor(fresh.messages, chipsOf(fresh)) };
|
|
1880
|
+
};
|
|
1579
1881
|
const siteOf = o.siteId ?? (() => "");
|
|
1580
1882
|
const resumeOf = o.resumeConversationId ?? inputConversationId;
|
|
1581
1883
|
const historyKeyFor = (site: string) => scopedKey(o.historyKey, site);
|
|
@@ -1683,7 +1985,8 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
|
|
|
1683
1985
|
|
|
1684
1986
|
const initialState: AriaChatState = {
|
|
1685
1987
|
status: "loading", statusText: "", error: "", input: "", copyNote: "", replyActionsHidden: true,
|
|
1686
|
-
messages: [], messagesHidden: true, prompts: [],
|
|
1988
|
+
messages: [], messagesHidden: true, prompts: [], promptsLoadingHidden: true, _suggested: [], _suggestToken: "", _suggestAsked: false,
|
|
1989
|
+
_convId: "", _siteId: "", _pending: "", _pendingRefs: "", _pendingAttach: [],
|
|
1687
1990
|
_pendingAfter: 0, _pendingId: "", _replyAfter: 0, _srvUsers: 0, _srvAssistants: 0,
|
|
1688
1991
|
_abortId: "", _turnId: "", stopHidden: true, ok: false,
|
|
1689
1992
|
listening: false, micHidden: !o.dictation, listeningHidden: true,
|
|
@@ -2885,8 +3188,11 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
|
|
|
2885
3188
|
};
|
|
2886
3189
|
const gated = (input: AriaChatState): AriaChatState => {
|
|
2887
3190
|
mentionSite();
|
|
2888
|
-
//
|
|
2889
|
-
|
|
3191
|
+
// A suggestions fetch that finished before this commit lands now — see
|
|
3192
|
+
// `withSettledSuggestions`, and `suggestionsSettled` for why its own
|
|
3193
|
+
// dispatch can arrive too early to. Then the live reply, so every rule
|
|
3194
|
+
// below sees what will be painted.
|
|
3195
|
+
const s = withLive(withSettledSuggestions(input));
|
|
2890
3196
|
// Stop goes once the stream says the model has finished writing: there is
|
|
2891
3197
|
// nothing left for an abort to save, and the turn only waits on the server
|
|
2892
3198
|
// to close it.
|
|
@@ -2902,6 +3208,9 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
|
|
|
2902
3208
|
const listening = s.listening === true;
|
|
2903
3209
|
const micHidden = !o.dictation || dictationAvailable === false || listening;
|
|
2904
3210
|
const listeningHidden = !listening;
|
|
3211
|
+
// The suggestions placeholder: shown only while a fetch is out AND the owner
|
|
3212
|
+
// has not said anything yet (a message sent meanwhile empties the cards).
|
|
3213
|
+
const promptsLoadingHidden = !s._suggestToken || s.messages.some((b) => b.role === "USER");
|
|
2905
3214
|
// The transcript scroll + the character, derived here for the reason the gates above
|
|
2906
3215
|
// are: one exit every action passes through, so no reducer can leave a stale
|
|
2907
3216
|
// anchor pointing at a row that has since been replaced, or a character still
|
|
@@ -2912,7 +3221,10 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
|
|
|
2912
3221
|
const talking = inFlight && replyStarted(s.messages);
|
|
2913
3222
|
const charThinkingHidden = !(inFlight && !talking);
|
|
2914
3223
|
const charTalkingHidden = !talking;
|
|
2915
|
-
|
|
3224
|
+
// The empty state is still the empty state while its suggestions are out:
|
|
3225
|
+
// the placeholder stands where the cards go, so the hero and the gap rules
|
|
3226
|
+
// treat it as cards on screen rather than flickering the character away.
|
|
3227
|
+
const heroHidden = !((s.prompts && s.prompts.length > 0) || !promptsLoadingHidden);
|
|
2916
3228
|
const promptsGapHidden = !heroHidden;
|
|
2917
3229
|
const messagesHidden = s.messages.length === 0;
|
|
2918
3230
|
const statusHidden = !s.statusText && charThinkingHidden && charTalkingHidden;
|
|
@@ -2921,9 +3233,10 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
|
|
|
2921
3233
|
&& s.scrollAnchor === scrollAnchor && s.charThinkingHidden === charThinkingHidden
|
|
2922
3234
|
&& s.charTalkingHidden === charTalkingHidden && s.heroHidden === heroHidden && s.messagesHidden === messagesHidden
|
|
2923
3235
|
&& s.statusHidden === statusHidden && s.promptsGapHidden === promptsGapHidden
|
|
3236
|
+
&& s.promptsLoadingHidden === promptsLoadingHidden
|
|
2924
3237
|
? s
|
|
2925
3238
|
: { ...s, stopHidden: hidden, replyActionsHidden, micHidden, listeningHidden,
|
|
2926
|
-
scrollAnchor, charThinkingHidden, charTalkingHidden, heroHidden, messagesHidden, statusHidden, promptsGapHidden };
|
|
3239
|
+
scrollAnchor, charThinkingHidden, charTalkingHidden, heroHidden, messagesHidden, statusHidden, promptsGapHidden, promptsLoadingHidden };
|
|
2927
3240
|
// The attachment projection rides the same exit, for the same reason: one
|
|
2928
3241
|
// place that every action's result passes through, so no reducer can hand
|
|
2929
3242
|
// back a stale copy of the chips. Seeded first, so the very first action of
|
|
@@ -3131,10 +3444,10 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
|
|
|
3131
3444
|
// `copyNote: ""` — a resumed transcript ends in a reply, so the row is
|
|
3132
3445
|
// shown at once; a note carried over would say "Copied" beside an
|
|
3133
3446
|
// answer from a different conversation that nobody copied.
|
|
3134
|
-
return leaving(await withWidgetItems({ ...s, status: "ready", _convId: resumeId, _siteId: site, _pending: "", statusText: "", error: "", copyNote: "",
|
|
3135
|
-
messages: resumed, ...observed(raw, s.widget), prompts: promptsFor(resumed, promptList()),
|
|
3447
|
+
return leaving(await withWidgetItems(await withSuggestions({ ...s, status: "ready", _convId: resumeId, _siteId: site, _pending: "", statusText: "", error: "", copyNote: "",
|
|
3448
|
+
messages: resumed, ...observed(raw, s.widget), _suggested: [], _suggestToken: "", prompts: promptsFor(resumed, promptList()),
|
|
3136
3449
|
// Transcript scroll — open a resumed conversation at its latest turn, pinning nothing.
|
|
3137
|
-
...openedAtLatest(s, resumed) }));
|
|
3450
|
+
...openedAtLatest(s, resumed) }, site)));
|
|
3138
3451
|
} catch { /* expired or unreachable — fall through to a fresh one */ }
|
|
3139
3452
|
}
|
|
3140
3453
|
// Opening a screen runs `load` more than once — the bundle self-kicks at
|
|
@@ -3166,15 +3479,21 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
|
|
|
3166
3479
|
// Transcript scroll — a conversation rehydrated with no send token (state persisted
|
|
3167
3480
|
// before tokens existed) still opens at its latest turn.
|
|
3168
3481
|
const opened = s.scrollSend || isGreetingOnly(messages) ? {} : openedAtLatest(s, messages);
|
|
3169
|
-
|
|
3482
|
+
const reused: AriaChatState = { ...s, status: "ready", messages, prompts: promptsFor(messages, chipsOf(s)), ...opened };
|
|
3483
|
+
// A persisted state can be waiting on a fetch that died with the previous
|
|
3484
|
+
// process; nothing would ever settle it, so ask again. And one minted by a
|
|
3485
|
+
// bundle from before the suggestions existed never asked at all — the
|
|
3486
|
+
// update every owner's last conversation goes through — so it asks now.
|
|
3487
|
+
const orphaned = !!s._suggestToken && !suggestOutcomes.has(s._suggestToken);
|
|
3488
|
+
return orphaned || (!!o.suggestions && !s._suggestAsked) ? await withSuggestions(reused, site) : reused;
|
|
3170
3489
|
}
|
|
3171
3490
|
try {
|
|
3172
|
-
return leaving({ ...s, status: "ready", _convId: await createOnce(site), _siteId: site, _pending: "", statusText: "",
|
|
3173
|
-
messages: greetingOnly(), ...observed([]), prompts: promptList(), ...SCROLL_RESET });
|
|
3491
|
+
return leaving(await withSuggestions({ ...s, status: "ready", _convId: await createOnce(site), _siteId: site, _pending: "", statusText: "",
|
|
3492
|
+
messages: greetingOnly(), ...observed([]), prompts: promptList(), ...SCROLL_RESET }, site));
|
|
3174
3493
|
} catch (e) {
|
|
3175
3494
|
// Even if create fails, show the greeting; submit retries the create.
|
|
3176
|
-
return leaving({ ...s, status: "ready", _convId: "", _siteId: site, _pending: "", ...failureSlots(e, unavailableText),
|
|
3177
|
-
messages: greetingOnly(), ...observed([]), prompts: promptList(), ...SCROLL_RESET });
|
|
3495
|
+
return leaving(await withSuggestions({ ...s, status: "ready", _convId: "", _siteId: site, _pending: "", ...failureSlots(e, unavailableText),
|
|
3496
|
+
messages: greetingOnly(), ...observed([]), prompts: promptList(), ...SCROLL_RESET }, site));
|
|
3178
3497
|
}
|
|
3179
3498
|
},
|
|
3180
3499
|
|
|
@@ -3265,12 +3584,12 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
|
|
|
3265
3584
|
// and the old observed counts back — the fresh chat snapping back to the
|
|
3266
3585
|
// previous one, which reads as ➕ having done nothing.
|
|
3267
3586
|
turnSeq += 1;
|
|
3268
|
-
return slow({ ...s, ...retired, status: "ready", _convId: conversationIdOf(res), _siteId: site, input: "", statusText: "", error: "",
|
|
3269
|
-
_pending: "", _abortId: "", _turnId: "", copyNote: "", messages: greetingOnly(), ...observed([]), prompts: promptList(), ok: false, ...SCROLL_RESET });
|
|
3587
|
+
return slow(await withSuggestions({ ...s, ...retired, status: "ready", _convId: conversationIdOf(res), _siteId: site, input: "", statusText: "", error: "",
|
|
3588
|
+
_pending: "", _abortId: "", _turnId: "", copyNote: "", messages: greetingOnly(), ...observed([]), prompts: promptList(), ok: false, ...SCROLL_RESET }, site));
|
|
3270
3589
|
} catch (e) {
|
|
3271
3590
|
turnSeq += 1; // see above — a failed create commits state too
|
|
3272
|
-
return slow({ ...s, ...retired, status: "ready", _convId: "", _siteId: site, input: "", ...failureSlots(e, unavailableText),
|
|
3273
|
-
_pending: "", _abortId: "", _turnId: "", copyNote: "", messages: greetingOnly(), ...observed([]), prompts: promptList(), ok: false, ...SCROLL_RESET });
|
|
3591
|
+
return slow(await withSuggestions({ ...s, ...retired, status: "ready", _convId: "", _siteId: site, input: "", ...failureSlots(e, unavailableText),
|
|
3592
|
+
_pending: "", _abortId: "", _turnId: "", copyNote: "", messages: greetingOnly(), ...observed([]), prompts: promptList(), ok: false, ...SCROLL_RESET }, site));
|
|
3274
3593
|
}
|
|
3275
3594
|
},
|
|
3276
3595
|
|
|
@@ -3347,7 +3666,7 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
|
|
|
3347
3666
|
messages: painted,
|
|
3348
3667
|
_pending: failed ? "" : pending,
|
|
3349
3668
|
...observed(raw, s.widget),
|
|
3350
|
-
prompts: promptsFor(painted,
|
|
3669
|
+
prompts: promptsFor(painted, chipsOf(s)),
|
|
3351
3670
|
statusText: ended ? "" : s.statusText,
|
|
3352
3671
|
...(ended ? { _abortId: "" } : {}),
|
|
3353
3672
|
...(failed ? toolFailureSlots(failed) : {}),
|
|
@@ -3833,6 +4152,29 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
|
|
|
3833
4152
|
return { ...withoutNotice(latest()), error: "" };
|
|
3834
4153
|
},
|
|
3835
4154
|
|
|
4155
|
+
suggestionsSettled: (s, arg) => {
|
|
4156
|
+
const token = typeof arg === "string" ? arg.trim() : typeof arg === "object" && arg ? String(arg.id ?? "").trim() : "";
|
|
4157
|
+
if (!token) return undefined;
|
|
4158
|
+
// The cards come from the worker's own memory, never the dispatch: only the
|
|
4159
|
+
// token rides the action, so no host's arg marshalling can mangle them.
|
|
4160
|
+
//
|
|
4161
|
+
// A token that does not match is left alone rather than dropped. It is
|
|
4162
|
+
// either stale — ➕ or a business switch minted a newer one — or the action
|
|
4163
|
+
// that minted it has not COMMITTED yet: `load` awaits more work after its
|
|
4164
|
+
// reducer returns, so this settle can run against the state from before it.
|
|
4165
|
+
// In that case the outcome is still waiting, and `gated` applies it on that
|
|
4166
|
+
// commit — whichever action commits it, and in whatever order.
|
|
4167
|
+
//
|
|
4168
|
+
// Either way the answer is `undefined`, NOT `s`. The runtime commits whatever
|
|
4169
|
+
// an action returns, and this one reads its state when it is dispatched: a
|
|
4170
|
+
// settle that lands while a slower action (a business switch's `load`) is
|
|
4171
|
+
// still running would commit the state from before that action and wipe out
|
|
4172
|
+
// its result. `undefined` is the runtime's "change nothing".
|
|
4173
|
+
if (token !== s._suggestToken) return undefined;
|
|
4174
|
+
const landed = withSettledSuggestions(s);
|
|
4175
|
+
return landed === s ? undefined : landed;
|
|
4176
|
+
},
|
|
4177
|
+
|
|
3836
4178
|
attachmentSettled: (s, arg) => {
|
|
3837
4179
|
const id = chipIdOf(arg);
|
|
3838
4180
|
if (!id) return s;
|
|
@@ -4111,6 +4453,10 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
|
|
|
4111
4453
|
mentionRemove: (s, arg) => gated(impl.mentionRemove(s, arg)),
|
|
4112
4454
|
attach: (s, arg) => impl.attach(s, arg).then(gated),
|
|
4113
4455
|
attachmentSettled: (s, arg) => gated(impl.attachmentSettled(s, arg)),
|
|
4456
|
+
suggestionsSettled: (s, arg) => {
|
|
4457
|
+
const r = impl.suggestionsSettled(s, arg);
|
|
4458
|
+
return r ? gated(r) : r;
|
|
4459
|
+
},
|
|
4114
4460
|
removeAttachment: (s, arg) => gated(impl.removeAttachment(s, arg)),
|
|
4115
4461
|
retryAttachment: (s, arg) => gated(impl.retryAttachment(s, arg)),
|
|
4116
4462
|
streamEvent: (s, event) => impl.streamEvent(s, event).then((r) => (r ? gated(r) : r)),
|
|
@@ -31,6 +31,7 @@ import { availability, recognizeOnce, stop as stopSpeech } from "@himalaya/speec
|
|
|
31
31
|
import { capturePhoto } from "@himalaya/camera";
|
|
32
32
|
import { put } from "@himalaya/presigned-upload";
|
|
33
33
|
import { alert } from "@himalaya/ui";
|
|
34
|
+
import { auth } from "@himalaya/wix-platform";
|
|
34
35
|
import {
|
|
35
36
|
activeSiteId,
|
|
36
37
|
createAriaChat,
|
|
@@ -193,7 +194,24 @@ const core = createAriaChat({
|
|
|
193
194
|
"ownerAria.greeting",
|
|
194
195
|
"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.",
|
|
195
196
|
),
|
|
197
|
+
// The four cards above are the FALLBACK now: the service's own suggestions for
|
|
198
|
+
// this business replace them when it has any. It answers on device with
|
|
199
|
+
// the owner's account cookie; a published web app refuses the call outright
|
|
200
|
+
// (`webwix_owner_only`), so there it always draws the four.
|
|
196
201
|
prompts,
|
|
202
|
+
suggestions: {
|
|
203
|
+
// A NEW action, for the reason `attachments.settled` below is one: the fetch
|
|
204
|
+
// must not hold the greeting back, so `load` returns first and the cards land
|
|
205
|
+
// after it. `svc` is read at call time, long after it exists.
|
|
206
|
+
settled: (token) => svc.suggestionsSettled({ id: token }),
|
|
207
|
+
// The suggestions are the signed-in ACCOUNT's (the call rides its cookie), so
|
|
208
|
+
// the cache is keyed on who that is and emptied when it changes. An identity
|
|
209
|
+
// the host cannot read is "", which turns the cache off rather than sharing it:
|
|
210
|
+
// every conversation then asks once, fresh. That is the case on __HIMI_APP_NAME__'s
|
|
211
|
+
// native shells today, which do not register the wix_platform capability, so
|
|
212
|
+
// `authIdentity` is refused there and the cache stays off.
|
|
213
|
+
principal: async () => (await auth.getIdentity()).userId ?? "",
|
|
214
|
+
},
|
|
197
215
|
labels: {
|
|
198
216
|
assistant: () => copyParam("ownerAria.name", "Aria"),
|
|
199
217
|
you: () => copyParam("ownerAria.you", "You"),
|
|
@@ -351,6 +369,7 @@ const svc = defineService<AriaChatState, {
|
|
|
351
369
|
attachmentSettled: (s: AriaChatState, arg?: AriaActionArg) => AriaChatState;
|
|
352
370
|
removeAttachment: (s: AriaChatState, arg?: AriaActionArg) => AriaChatState;
|
|
353
371
|
retryAttachment: (s: AriaChatState, arg?: AriaActionArg) => AriaChatState;
|
|
372
|
+
suggestionsSettled: (s: AriaChatState, arg?: AriaActionArg) => AriaChatState | undefined;
|
|
354
373
|
}>(SERVICE, {
|
|
355
374
|
state: core.initialState,
|
|
356
375
|
actions: {
|
|
@@ -418,6 +437,9 @@ const svc = defineService<AriaChatState, {
|
|
|
418
437
|
streamEvent: core.streamEvent,
|
|
419
438
|
streamEnded: core.streamEnded,
|
|
420
439
|
streamFailed: core.streamFailed,
|
|
440
|
+
// The empty state's server suggestions landing. Dispatched by the
|
|
441
|
+
// fetch itself (see `suggestions.settled` above), never by the descriptor.
|
|
442
|
+
suggestionsSettled: core.suggestionsSettled,
|
|
421
443
|
},
|
|
422
444
|
});
|
|
423
445
|
|
|
@@ -57234,6 +57234,27 @@ const SCREENS = runtimeSetting({
|
|
|
57234
57234
|
"hiddenPath": "$service:himiOwnerLiteAria.replyActionsHidden"
|
|
57235
57235
|
}
|
|
57236
57236
|
},
|
|
57237
|
+
{
|
|
57238
|
+
"id": "himi_owner_lite_aria.prompts.loading",
|
|
57239
|
+
"body": {
|
|
57240
|
+
"package": "himalaya",
|
|
57241
|
+
"type": "Skeleton",
|
|
57242
|
+
"props": {
|
|
57243
|
+
"height": 48,
|
|
57244
|
+
"cornerRadius": 12,
|
|
57245
|
+
"count": 3
|
|
57246
|
+
}
|
|
57247
|
+
},
|
|
57248
|
+
"modifiers": {
|
|
57249
|
+
"hiddenPath": "$service:himiOwnerLiteAria.promptsLoadingHidden",
|
|
57250
|
+
"padding": {
|
|
57251
|
+
"leading": 0,
|
|
57252
|
+
"trailing": 0,
|
|
57253
|
+
"top": 0,
|
|
57254
|
+
"bottom": 0
|
|
57255
|
+
}
|
|
57256
|
+
}
|
|
57257
|
+
},
|
|
57237
57258
|
{
|
|
57238
57259
|
"id": "himi_owner_lite_aria.prompts",
|
|
57239
57260
|
"body": {
|
|
@@ -61354,7 +61375,8 @@ const SCREENS = runtimeSetting({
|
|
|
61354
61375
|
"speech",
|
|
61355
61376
|
"camera",
|
|
61356
61377
|
"presigned_upload",
|
|
61357
|
-
"stream"
|
|
61378
|
+
"stream",
|
|
61379
|
+
"wix_platform"
|
|
61358
61380
|
],
|
|
61359
61381
|
"minBinaryVersion": "0.1.0",
|
|
61360
61382
|
"bundleVersion": "0.0.1"
|