@burdenoff/website-sdk 2026.1105.1 → 2026.1105.2
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/README.md +40 -1
- package/dist/avatar-live/index.d.mts +2 -2
- package/dist/avatar-live/index.d.ts +2 -2
- package/dist/avatar-live/index.js +11 -1
- package/dist/avatar-live/index.js.map +1 -1
- package/dist/avatar-live/index.mjs +11 -1
- package/dist/avatar-live/index.mjs.map +1 -1
- package/dist/avatar-live-view/index.d.mts +2 -2
- package/dist/avatar-live-view/index.d.ts +2 -2
- package/dist/avatar-live-view/index.js +653 -32
- package/dist/avatar-live-view/index.js.map +1 -1
- package/dist/avatar-live-view/index.mjs +653 -32
- package/dist/avatar-live-view/index.mjs.map +1 -1
- package/dist/{explore-avatar-live-config-OO0hvQCK.d.mts → explore-avatar-live-config-BL8FTm0I.d.mts} +1 -1
- package/dist/{explore-avatar-live-config-C-OwXNje.d.ts → explore-avatar-live-config-auLKxSzx.d.ts} +1 -1
- package/dist/index.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/{types-CXqaLlNB.d.mts → types-CTUJNvQu.d.mts} +10 -0
- package/dist/{types-CXqaLlNB.d.ts → types-CTUJNvQu.d.ts} +10 -0
- package/package.json +1 -1
|
@@ -11,6 +11,8 @@ var jsxRuntime = require('react/jsx-runtime');
|
|
|
11
11
|
// src/components/explore-avatar-api.ts
|
|
12
12
|
var AVAILABILITY_TIMEOUT_MS = 8e3;
|
|
13
13
|
var CREATE_TIMEOUT_MS = 3e4;
|
|
14
|
+
var TURN_TIMEOUT_MS = 8e3;
|
|
15
|
+
var TURN_ANSWER_TIMEOUT_MS = 25e3;
|
|
14
16
|
var MESSAGE_ID = /^[A-Za-z0-9_-]{1,64}$/;
|
|
15
17
|
var STT_TOKEN = /^[A-Za-z0-9._~-]{1,4096}$/;
|
|
16
18
|
var CONVERSATION_TOKEN = /^[A-Za-z0-9._~+/=-]{16,512}$/;
|
|
@@ -115,6 +117,21 @@ function errorCode(body) {
|
|
|
115
117
|
const { code } = body.error;
|
|
116
118
|
return typeof code === "string" ? code : null;
|
|
117
119
|
}
|
|
120
|
+
var MAX_FOLLOW_UP_ANSWERS = 16;
|
|
121
|
+
var MAX_FOLLOW_UP_SECONDS = 600;
|
|
122
|
+
var FOLLOW_UP_KEYS = ["idleSeconds", "maxAnswerWaitSeconds", "maxAnswers"];
|
|
123
|
+
function isWholeBetween(value, max) {
|
|
124
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 1 && value <= max;
|
|
125
|
+
}
|
|
126
|
+
function readFollowUps(value) {
|
|
127
|
+
if (!isRecord(value)) return void 0;
|
|
128
|
+
const keys = Object.keys(value).sort();
|
|
129
|
+
const { maxAnswers, idleSeconds, maxAnswerWaitSeconds } = value;
|
|
130
|
+
if (keys.length !== FOLLOW_UP_KEYS.length || keys.some((key, at) => key !== FOLLOW_UP_KEYS[at]) || !isWholeBetween(maxAnswers, MAX_FOLLOW_UP_ANSWERS) || !isWholeBetween(idleSeconds, MAX_FOLLOW_UP_SECONDS) || !isWholeBetween(maxAnswerWaitSeconds, MAX_FOLLOW_UP_SECONDS)) {
|
|
131
|
+
return void 0;
|
|
132
|
+
}
|
|
133
|
+
return { maxAnswers, idleSeconds, maxAnswerWaitSeconds };
|
|
134
|
+
}
|
|
118
135
|
function readCreated(body) {
|
|
119
136
|
if (!isRecord(body)) return FAILED;
|
|
120
137
|
const { sessionId, endSecret } = body;
|
|
@@ -132,6 +149,7 @@ function readCreated(body) {
|
|
|
132
149
|
if (typeof livekitUrl !== "string" || !livekitUrl.startsWith("wss://") || typeof participantToken !== "string" || participantToken === "" || typeof workerIdentity !== "string" || workerIdentity === "" || typeof locale !== "string" || typeof text !== "string") {
|
|
133
150
|
return unusable;
|
|
134
151
|
}
|
|
152
|
+
const followUps = readFollowUps(body.followUps);
|
|
135
153
|
return {
|
|
136
154
|
kind: "created",
|
|
137
155
|
session: {
|
|
@@ -140,7 +158,8 @@ function readCreated(body) {
|
|
|
140
158
|
endsAt,
|
|
141
159
|
join: { livekitUrl, participantToken, workerIdentity },
|
|
142
160
|
captions: { locale, text },
|
|
143
|
-
excerpt: body.excerpt === true
|
|
161
|
+
excerpt: body.excerpt === true,
|
|
162
|
+
...followUps === void 0 ? {} : { followUps }
|
|
144
163
|
}
|
|
145
164
|
};
|
|
146
165
|
}
|
|
@@ -271,6 +290,96 @@ function sendEnd(target) {
|
|
|
271
290
|
} catch {
|
|
272
291
|
}
|
|
273
292
|
}
|
|
293
|
+
var TURN_REFUSALS = /* @__PURE__ */ new Map([
|
|
294
|
+
["no_time", "no_time"],
|
|
295
|
+
["max_answers", "max_answers"],
|
|
296
|
+
["not_eligible", "not_eligible"],
|
|
297
|
+
["answer_claimed", "answer_claimed"],
|
|
298
|
+
["limits", "limits"],
|
|
299
|
+
["paused", "paused"],
|
|
300
|
+
["busy", "busy"]
|
|
301
|
+
]);
|
|
302
|
+
var MAX_TURN_RETRY_AFTER_SECONDS = 600;
|
|
303
|
+
function retryAfterOf(body) {
|
|
304
|
+
if (!isRecord(body) || !isRecord(body.error)) return void 0;
|
|
305
|
+
const { retryAfterSeconds } = body.error;
|
|
306
|
+
return isWholeBetween(retryAfterSeconds, MAX_TURN_RETRY_AFTER_SECONDS) ? retryAfterSeconds : void 0;
|
|
307
|
+
}
|
|
308
|
+
var TURN_KEY = /^[A-Za-z0-9_-]{8,64}$/;
|
|
309
|
+
var LOST = { kind: "failed", lost: true };
|
|
310
|
+
var TURN_FAILED = { kind: "failed", lost: false };
|
|
311
|
+
function readAccepted(body) {
|
|
312
|
+
if (!isRecord(body) || body.accepted !== true || !isRecord(body.captions)) {
|
|
313
|
+
return LOST;
|
|
314
|
+
}
|
|
315
|
+
const { locale, text } = body.captions;
|
|
316
|
+
if (typeof locale !== "string" || typeof text !== "string") return LOST;
|
|
317
|
+
return {
|
|
318
|
+
kind: "accepted",
|
|
319
|
+
captions: { locale, text },
|
|
320
|
+
excerpt: body.excerpt === true
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
function readTurn(kind, status, body) {
|
|
324
|
+
if (status === 200) {
|
|
325
|
+
if (kind === "answer") return readAccepted(body);
|
|
326
|
+
return isRecord(body) && body.ok === true ? { kind: "ok" } : LOST;
|
|
327
|
+
}
|
|
328
|
+
const code = errorCode(body);
|
|
329
|
+
const refusal = code === null ? void 0 : TURN_REFUSALS.get(code);
|
|
330
|
+
if (refusal !== void 0) {
|
|
331
|
+
const retryAfterSeconds = retryAfterOf(body);
|
|
332
|
+
return retryAfterSeconds === void 0 ? { kind: "refused", reason: refusal } : { kind: "refused", reason: refusal, retryAfterSeconds };
|
|
333
|
+
}
|
|
334
|
+
if (status === 404) return { kind: "gone" };
|
|
335
|
+
return status >= 500 ? LOST : TURN_FAILED;
|
|
336
|
+
}
|
|
337
|
+
function requestTurn(target) {
|
|
338
|
+
const { kind, messageId, idempotencyKey } = target;
|
|
339
|
+
const nonce = newIdempotencyKey();
|
|
340
|
+
if (!SESSION_ID.test(target.sessionId) || !END_SECRET.test(target.secret) || nonce === null || (kind === "answer" ? messageId === void 0 || !MESSAGE_ID.test(messageId) : messageId !== void 0 || idempotencyKey !== void 0) || idempotencyKey !== void 0 && !TURN_KEY.test(idempotencyKey)) {
|
|
341
|
+
return Promise.resolve(TURN_FAILED);
|
|
342
|
+
}
|
|
343
|
+
const payload = JSON.stringify({
|
|
344
|
+
secret: target.secret,
|
|
345
|
+
kind,
|
|
346
|
+
nonce,
|
|
347
|
+
...messageId === void 0 ? {} : { messageId },
|
|
348
|
+
...idempotencyKey === void 0 ? {} : { idempotencyKey }
|
|
349
|
+
});
|
|
350
|
+
const controller = new AbortController();
|
|
351
|
+
const answer = (async () => {
|
|
352
|
+
try {
|
|
353
|
+
const response = await fetch(
|
|
354
|
+
`${target.base}/sessions/${encodeURIComponent(target.sessionId)}/turn`,
|
|
355
|
+
{
|
|
356
|
+
method: "POST",
|
|
357
|
+
credentials: "omit",
|
|
358
|
+
cache: "no-store",
|
|
359
|
+
headers: { ...target.headers },
|
|
360
|
+
body: payload,
|
|
361
|
+
signal: controller.signal
|
|
362
|
+
}
|
|
363
|
+
);
|
|
364
|
+
return readTurn(kind, response.status, await readJson(response));
|
|
365
|
+
} catch {
|
|
366
|
+
return LOST;
|
|
367
|
+
}
|
|
368
|
+
})();
|
|
369
|
+
return new Promise((resolve) => {
|
|
370
|
+
const timer = setTimeout(
|
|
371
|
+
() => {
|
|
372
|
+
controller.abort();
|
|
373
|
+
resolve(LOST);
|
|
374
|
+
},
|
|
375
|
+
kind === "answer" ? TURN_ANSWER_TIMEOUT_MS : TURN_TIMEOUT_MS
|
|
376
|
+
);
|
|
377
|
+
void answer.then((result) => {
|
|
378
|
+
clearTimeout(timer);
|
|
379
|
+
resolve(result);
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
}
|
|
274
383
|
var BOT_BINDING_PREFIX = "boff-web-avatar-cdata/v1";
|
|
275
384
|
function webCryptoSubtle() {
|
|
276
385
|
const subtle = globalThis.crypto?.subtle;
|
|
@@ -368,7 +477,14 @@ var EN = {
|
|
|
368
477
|
usedToday: "You've had today's avatar session. Answers will show here as text.",
|
|
369
478
|
needsAnswerAny: "Ask a question here, and the avatar reads the answer aloud.",
|
|
370
479
|
startNewChatDetailAny: "The avatar reads aloud only the first answer in a new chat started in the Avatar tab, provided it passes the speech checks.",
|
|
371
|
-
notEligibleAny: "The avatar can't read this answer aloud. The answer and sources are in Chat."
|
|
480
|
+
notEligibleAny: "The avatar can't read this answer aloud. The answer and sources are in Chat.",
|
|
481
|
+
followUpIdle: "The avatar ends after about thirty seconds without a question.",
|
|
482
|
+
followUpNoTime: "There isn't enough time left in this session to read that answer. It's in Chat.",
|
|
483
|
+
followUpMaxAnswers: "The avatar has read all the answers it can in this session. This one is in Chat.",
|
|
484
|
+
followUpLast: "That was the last answer the avatar can read in this session.",
|
|
485
|
+
followUpLimits: "The live avatar has reached its limit for now. This answer is in Chat.",
|
|
486
|
+
followUpPaused: "The live avatar is paused right now. This answer is in Chat.",
|
|
487
|
+
followUpUnread: "The avatar couldn't read that answer. It's in Chat."
|
|
372
488
|
};
|
|
373
489
|
var ZH = {
|
|
374
490
|
checking: "\u6B63\u5728\u68C0\u67E5\u5B9E\u65F6\u865A\u62DF\u5F62\u8C61\u662F\u5426\u53EF\u7528\u2026",
|
|
@@ -429,7 +545,16 @@ var ZH = {
|
|
|
429
545
|
usedToday: "\u60A8\u4ECA\u5929\u5DF2\u4F7F\u7528\u8FC7\u865A\u62DF\u5F62\u8C61\u3002\u56DE\u7B54\u5C06\u4EE5\u6587\u5B57\u663E\u793A\u5728\u8FD9\u91CC\u3002",
|
|
430
546
|
needsAnswerAny: "\u5728\u8FD9\u91CC\u63D0\u95EE\uFF0C\u865A\u62DF\u5F62\u8C61\u4F1A\u6717\u8BFB\u56DE\u7B54\u3002",
|
|
431
547
|
startNewChatDetailAny: "\u865A\u62DF\u5F62\u8C61\u53EA\u6717\u8BFB\u5728\u201C\u865A\u62DF\u5F62\u8C61\u201D\u6807\u7B7E\u9875\u4E2D\u5F00\u59CB\u7684\u65B0\u804A\u5929\u7684\u7B2C\u4E00\u4E2A\u56DE\u7B54\uFF0C\u4E14\u8BE5\u56DE\u7B54\u987B\u901A\u8FC7\u8BED\u97F3\u68C0\u67E5\u3002",
|
|
432
|
-
notEligibleAny: "\u865A\u62DF\u5F62\u8C61\u65E0\u6CD5\u6717\u8BFB\u6B64\u56DE\u7B54\u3002\u56DE\u7B54\u548C\u6765\u6E90\u5728\u804A\u5929\u4E2D\u3002"
|
|
548
|
+
notEligibleAny: "\u865A\u62DF\u5F62\u8C61\u65E0\u6CD5\u6717\u8BFB\u6B64\u56DE\u7B54\u3002\u56DE\u7B54\u548C\u6765\u6E90\u5728\u804A\u5929\u4E2D\u3002",
|
|
549
|
+
// TRANSLATION REVIEW PENDING (BOFF-9797): draft, the lines of a session that reads the answers after the first
|
|
550
|
+
// (Stage D).
|
|
551
|
+
followUpIdle: "\u5982\u679C\u7EA6\u4E09\u5341\u79D2\u5185\u6CA1\u6709\u65B0\u95EE\u9898\uFF0C\u865A\u62DF\u5F62\u8C61\u5C31\u4F1A\u7ED3\u675F\u3002",
|
|
552
|
+
followUpNoTime: "\u672C\u6B21\u4F1A\u8BDD\u5269\u4F59\u65F6\u95F4\u4E0D\u8DB3\uFF0C\u65E0\u6CD5\u6717\u8BFB\u8BE5\u56DE\u7B54\u3002\u56DE\u7B54\u5728\u804A\u5929\u4E2D\u3002",
|
|
553
|
+
followUpMaxAnswers: "\u865A\u62DF\u5F62\u8C61\u5DF2\u6717\u8BFB\u672C\u6B21\u4F1A\u8BDD\u53EF\u6717\u8BFB\u7684\u5168\u90E8\u56DE\u7B54\u3002\u6B64\u56DE\u7B54\u5728\u804A\u5929\u4E2D\u3002",
|
|
554
|
+
followUpLast: "\u8FD9\u662F\u865A\u62DF\u5F62\u8C61\u5728\u672C\u6B21\u4F1A\u8BDD\u4E2D\u53EF\u6717\u8BFB\u7684\u6700\u540E\u4E00\u4E2A\u56DE\u7B54\u3002",
|
|
555
|
+
followUpLimits: "\u5B9E\u65F6\u865A\u62DF\u5F62\u8C61\u6682\u65F6\u5DF2\u8FBE\u5230\u4F7F\u7528\u4E0A\u9650\u3002\u6B64\u56DE\u7B54\u5728\u804A\u5929\u4E2D\u3002",
|
|
556
|
+
followUpPaused: "\u5B9E\u65F6\u865A\u62DF\u5F62\u8C61\u76EE\u524D\u5DF2\u6682\u505C\u3002\u6B64\u56DE\u7B54\u5728\u804A\u5929\u4E2D\u3002",
|
|
557
|
+
followUpUnread: "\u865A\u62DF\u5F62\u8C61\u65E0\u6CD5\u6717\u8BFB\u8BE5\u56DE\u7B54\u3002\u56DE\u7B54\u5728\u804A\u5929\u4E2D\u3002"
|
|
433
558
|
};
|
|
434
559
|
var HI = {
|
|
435
560
|
checking: "\u091C\u093E\u0901\u091A \u0939\u094B \u0930\u0939\u0940 \u0939\u0948 \u0915\u093F \u0932\u093E\u0907\u0935 \u0905\u0935\u0924\u093E\u0930 \u0909\u092A\u0932\u092C\u094D\u0927 \u0939\u0948 \u092F\u093E \u0928\u0939\u0940\u0902\u2026",
|
|
@@ -490,7 +615,16 @@ var HI = {
|
|
|
490
615
|
usedToday: "\u0906\u091C \u0915\u093E \u0905\u0935\u0924\u093E\u0930 \u0938\u0924\u094D\u0930 \u0906\u092A \u0932\u0947 \u091A\u0941\u0915\u0947 \u0939\u0948\u0902\u0964 \u0909\u0924\u094D\u0924\u0930 \u092F\u0939\u093E\u0901 \u091F\u0947\u0915\u094D\u0938\u094D\u091F \u0915\u0947 \u0930\u0942\u092A \u092E\u0947\u0902 \u0926\u093F\u0916\u0947\u0902\u0917\u0947\u0964",
|
|
491
616
|
needsAnswerAny: "\u092F\u0939\u093E\u0901 \u092A\u094D\u0930\u0936\u094D\u0928 \u092A\u0942\u091B\u0947\u0902, \u0914\u0930 \u0905\u0935\u0924\u093E\u0930 \u0909\u0924\u094D\u0924\u0930 \u091C\u093C\u094B\u0930 \u0938\u0947 \u092A\u0922\u093C\u0947\u0917\u093E\u0964",
|
|
492
617
|
startNewChatDetailAny: "\u0905\u0935\u0924\u093E\u0930 \u0915\u0947\u0935\u0932 \u0905\u0935\u0924\u093E\u0930 \u091F\u0948\u092C \u092E\u0947\u0902 \u0936\u0941\u0930\u0942 \u0915\u0940 \u0917\u0908 \u0928\u0908 \u091A\u0948\u091F \u0915\u093E \u092A\u0939\u0932\u093E \u0909\u0924\u094D\u0924\u0930 \u091C\u093C\u094B\u0930 \u0938\u0947 \u092A\u0922\u093C\u0924\u093E \u0939\u0948, \u092C\u0936\u0930\u094D\u0924\u0947 \u0935\u0939 \u0935\u093E\u0923\u0940 \u091C\u093E\u0901\u091A \u092E\u0947\u0902 \u092A\u093E\u0938 \u0939\u094B\u0964",
|
|
493
|
-
notEligibleAny: "\u0905\u0935\u0924\u093E\u0930 \u092F\u0939 \u0909\u0924\u094D\u0924\u0930 \u091C\u093C\u094B\u0930 \u0938\u0947 \u0928\u0939\u0940\u0902 \u092A\u0922\u093C \u0938\u0915\u0924\u093E\u0964 \u0909\u0924\u094D\u0924\u0930 \u0914\u0930 \u0938\u094D\u0930\u094B\u0924 \u091A\u0948\u091F \u092E\u0947\u0902 \u0939\u0948\u0902\u0964"
|
|
618
|
+
notEligibleAny: "\u0905\u0935\u0924\u093E\u0930 \u092F\u0939 \u0909\u0924\u094D\u0924\u0930 \u091C\u093C\u094B\u0930 \u0938\u0947 \u0928\u0939\u0940\u0902 \u092A\u0922\u093C \u0938\u0915\u0924\u093E\u0964 \u0909\u0924\u094D\u0924\u0930 \u0914\u0930 \u0938\u094D\u0930\u094B\u0924 \u091A\u0948\u091F \u092E\u0947\u0902 \u0939\u0948\u0902\u0964",
|
|
619
|
+
// TRANSLATION REVIEW PENDING (BOFF-9797): draft, the lines of a session that reads the answers after the first
|
|
620
|
+
// (Stage D).
|
|
621
|
+
followUpIdle: "\u092C\u093F\u0928\u093E \u0915\u093F\u0938\u0940 \u092A\u094D\u0930\u0936\u094D\u0928 \u0915\u0947 \u0932\u0917\u092D\u0917 \u0924\u0940\u0938 \u0938\u0947\u0915\u0902\u0921 \u092C\u093E\u0926 \u0905\u0935\u0924\u093E\u0930 \u0938\u092E\u093E\u092A\u094D\u0924 \u0939\u094B \u091C\u093E\u0924\u093E \u0939\u0948\u0964",
|
|
622
|
+
followUpNoTime: "\u0907\u0938 \u0938\u0924\u094D\u0930 \u092E\u0947\u0902 \u0935\u0939 \u0909\u0924\u094D\u0924\u0930 \u092A\u0922\u093C\u0928\u0947 \u0915\u0947 \u0932\u093F\u090F \u092A\u0930\u094D\u092F\u093E\u092A\u094D\u0924 \u0938\u092E\u092F \u0928\u0939\u0940\u0902 \u092C\u091A\u093E \u0939\u0948\u0964 \u0909\u0924\u094D\u0924\u0930 \u091A\u0948\u091F \u092E\u0947\u0902 \u0939\u0948\u0964",
|
|
623
|
+
followUpMaxAnswers: "\u0905\u0935\u0924\u093E\u0930 \u0907\u0938 \u0938\u0924\u094D\u0930 \u092E\u0947\u0902 \u091C\u093F\u0924\u0928\u0947 \u0909\u0924\u094D\u0924\u0930 \u092A\u0922\u093C \u0938\u0915\u0924\u093E \u0939\u0948, \u0938\u092C \u092A\u0922\u093C \u091A\u0941\u0915\u093E \u0939\u0948\u0964 \u092F\u0939 \u0909\u0924\u094D\u0924\u0930 \u091A\u0948\u091F \u092E\u0947\u0902 \u0939\u0948\u0964",
|
|
624
|
+
followUpLast: "\u092F\u0939 \u0907\u0938 \u0938\u0924\u094D\u0930 \u092E\u0947\u0902 \u0905\u0935\u0924\u093E\u0930 \u0926\u094D\u0935\u093E\u0930\u093E \u092A\u0922\u093C\u093E \u091C\u093E \u0938\u0915\u0928\u0947 \u0935\u093E\u0932\u093E \u0905\u0902\u0924\u093F\u092E \u0909\u0924\u094D\u0924\u0930 \u0925\u093E\u0964",
|
|
625
|
+
followUpLimits: "\u0932\u093E\u0907\u0935 \u0905\u0935\u0924\u093E\u0930 \u0905\u092D\u0940 \u0905\u092A\u0928\u0940 \u0938\u0940\u092E\u093E \u0924\u0915 \u092A\u0939\u0941\u0901\u091A \u0917\u092F\u093E \u0939\u0948\u0964 \u092F\u0939 \u0909\u0924\u094D\u0924\u0930 \u091A\u0948\u091F \u092E\u0947\u0902 \u0939\u0948\u0964",
|
|
626
|
+
followUpPaused: "\u0932\u093E\u0907\u0935 \u0905\u0935\u0924\u093E\u0930 \u0905\u092D\u0940 \u0930\u0941\u0915\u093E \u0939\u0941\u0906 \u0939\u0948\u0964 \u092F\u0939 \u0909\u0924\u094D\u0924\u0930 \u091A\u0948\u091F \u092E\u0947\u0902 \u0939\u0948\u0964",
|
|
627
|
+
followUpUnread: "\u0905\u0935\u0924\u093E\u0930 \u0935\u0939 \u0909\u0924\u094D\u0924\u0930 \u0928\u0939\u0940\u0902 \u092A\u0922\u093C \u0938\u0915\u093E\u0964 \u0909\u0924\u094D\u0924\u0930 \u091A\u0948\u091F \u092E\u0947\u0902 \u0939\u0948\u0964"
|
|
494
628
|
};
|
|
495
629
|
var ES = {
|
|
496
630
|
checking: "Comprobando si el avatar en vivo est\xE1 disponible\u2026",
|
|
@@ -551,7 +685,16 @@ var ES = {
|
|
|
551
685
|
usedToday: "Ya has usado la sesi\xF3n de avatar de hoy. Las respuestas se mostrar\xE1n aqu\xED como texto.",
|
|
552
686
|
needsAnswerAny: "Haz una pregunta aqu\xED y el avatar leer\xE1 la respuesta en voz alta.",
|
|
553
687
|
startNewChatDetailAny: "El avatar solo lee en voz alta la primera respuesta de un chat nuevo iniciado en la pesta\xF1a Avatar, si supera las comprobaciones de voz.",
|
|
554
|
-
notEligibleAny: "El avatar no puede leer esta respuesta en voz alta. La respuesta y las fuentes est\xE1n en el chat."
|
|
688
|
+
notEligibleAny: "El avatar no puede leer esta respuesta en voz alta. La respuesta y las fuentes est\xE1n en el chat.",
|
|
689
|
+
// TRANSLATION REVIEW PENDING (BOFF-9797): draft, the lines of a session that reads the answers after the first
|
|
690
|
+
// (Stage D).
|
|
691
|
+
followUpIdle: "El avatar termina tras unos treinta segundos sin preguntas.",
|
|
692
|
+
followUpNoTime: "No queda tiempo suficiente en esta sesi\xF3n para leer esa respuesta. Est\xE1 en el chat.",
|
|
693
|
+
followUpMaxAnswers: "El avatar ya ha le\xEDdo todas las respuestas que puede en esta sesi\xF3n. Esta est\xE1 en el chat.",
|
|
694
|
+
followUpLast: "Esa fue la \xFAltima respuesta que el avatar puede leer en esta sesi\xF3n.",
|
|
695
|
+
followUpLimits: "El avatar en vivo ha alcanzado su l\xEDmite por ahora. Esta respuesta est\xE1 en el chat.",
|
|
696
|
+
followUpPaused: "El avatar en vivo est\xE1 en pausa ahora mismo. Esta respuesta est\xE1 en el chat.",
|
|
697
|
+
followUpUnread: "El avatar no pudo leer esa respuesta. Est\xE1 en el chat."
|
|
555
698
|
};
|
|
556
699
|
var AR = {
|
|
557
700
|
checking: "\u062C\u0627\u0631\u064D \u0627\u0644\u062A\u062D\u0642\u0642 \u0645\u0646 \u062A\u0648\u0641\u0631 \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0627\u0644\u0645\u0628\u0627\u0634\u0631\u2026",
|
|
@@ -612,7 +755,16 @@ var AR = {
|
|
|
612
755
|
usedToday: "\u0644\u0642\u062F \u0627\u0633\u062A\u062E\u062F\u0645\u062A \u062C\u0644\u0633\u0629 \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0644\u0647\u0630\u0627 \u0627\u0644\u064A\u0648\u0645. \u0633\u062A\u0638\u0647\u0631 \u0627\u0644\u0625\u062C\u0627\u0628\u0627\u062A \u0647\u0646\u0627 \u0646\u0635\u064B\u0627.",
|
|
613
756
|
needsAnswerAny: "\u0627\u0637\u0631\u062D \u0633\u0624\u0627\u0644\u064B\u0627 \u0647\u0646\u0627\u060C \u0648\u0633\u064A\u0642\u0631\u0623 \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0627\u0644\u0625\u062C\u0627\u0628\u0629 \u0628\u0635\u0648\u062A \u0639\u0627\u0644\u064D.",
|
|
614
757
|
startNewChatDetailAny: "\u064A\u0642\u0631\u0623 \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0628\u0635\u0648\u062A \u0639\u0627\u0644\u064D \u0627\u0644\u0625\u062C\u0627\u0628\u0629 \u0627\u0644\u0623\u0648\u0644\u0649 \u0641\u0642\u0637 \u0641\u064A \u062F\u0631\u062F\u0634\u0629 \u062C\u062F\u064A\u062F\u0629 \u0628\u062F\u0623\u062A \u0641\u064A \u0639\u0644\u0627\u0645\u0629 \u062A\u0628\u0648\u064A\u0628 \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631\u060C \u0628\u0634\u0631\u0637 \u0623\u0646 \u062A\u062C\u062A\u0627\u0632 \u0641\u062D\u0648\u0635 \u0627\u0644\u0646\u0637\u0642.",
|
|
615
|
-
notEligibleAny: "\u0644\u0627 \u064A\u0645\u0643\u0646 \u0644\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0642\u0631\u0627\u0621\u0629 \u0647\u0630\u0647 \u0627\u0644\u0625\u062C\u0627\u0628\u0629 \u0628\u0635\u0648\u062A \u0639\u0627\u0644\u064D. \u0627\u0644\u0625\u062C\u0627\u0628\u0629 \u0648\u0627\u0644\u0645\u0635\u0627\u062F\u0631 \u0645\u0648\u062C\u0648\u062F\u0629 \u0641\u064A \u0627\u0644\u062F\u0631\u062F\u0634\u0629."
|
|
758
|
+
notEligibleAny: "\u0644\u0627 \u064A\u0645\u0643\u0646 \u0644\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0642\u0631\u0627\u0621\u0629 \u0647\u0630\u0647 \u0627\u0644\u0625\u062C\u0627\u0628\u0629 \u0628\u0635\u0648\u062A \u0639\u0627\u0644\u064D. \u0627\u0644\u0625\u062C\u0627\u0628\u0629 \u0648\u0627\u0644\u0645\u0635\u0627\u062F\u0631 \u0645\u0648\u062C\u0648\u062F\u0629 \u0641\u064A \u0627\u0644\u062F\u0631\u062F\u0634\u0629.",
|
|
759
|
+
// TRANSLATION REVIEW PENDING (BOFF-9797): draft, the lines of a session that reads the answers after the first
|
|
760
|
+
// (Stage D).
|
|
761
|
+
followUpIdle: "\u064A\u0646\u062A\u0647\u064A \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0628\u0639\u062F \u0646\u062D\u0648 \u062B\u0644\u0627\u062B\u064A\u0646 \u062B\u0627\u0646\u064A\u0629 \u0645\u0646 \u062F\u0648\u0646 \u0633\u0624\u0627\u0644.",
|
|
762
|
+
followUpNoTime: "\u0644\u0645 \u064A\u062A\u0628\u0642\u064E\u0651 \u0648\u0642\u062A \u0643\u0627\u0641\u064D \u0641\u064A \u0647\u0630\u0647 \u0627\u0644\u062C\u0644\u0633\u0629 \u0644\u0642\u0631\u0627\u0621\u0629 \u062A\u0644\u0643 \u0627\u0644\u0625\u062C\u0627\u0628\u0629. \u0625\u0646\u0647\u0627 \u0641\u064A \u0627\u0644\u062F\u0631\u062F\u0634\u0629.",
|
|
763
|
+
followUpMaxAnswers: "\u0642\u0631\u0623 \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0643\u0644 \u0627\u0644\u0625\u062C\u0627\u0628\u0627\u062A \u0627\u0644\u062A\u064A \u064A\u0645\u0643\u0646\u0647 \u0642\u0631\u0627\u0621\u062A\u0647\u0627 \u0641\u064A \u0647\u0630\u0647 \u0627\u0644\u062C\u0644\u0633\u0629. \u0647\u0630\u0647 \u0627\u0644\u0625\u062C\u0627\u0628\u0629 \u0641\u064A \u0627\u0644\u062F\u0631\u062F\u0634\u0629.",
|
|
764
|
+
followUpLast: "\u0643\u0627\u0646\u062A \u062A\u0644\u0643 \u0622\u062E\u0631 \u0625\u062C\u0627\u0628\u0629 \u064A\u0645\u0643\u0646 \u0644\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0642\u0631\u0627\u0621\u062A\u0647\u0627 \u0641\u064A \u0647\u0630\u0647 \u0627\u0644\u062C\u0644\u0633\u0629.",
|
|
765
|
+
followUpLimits: "\u0628\u0644\u063A \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0627\u0644\u0645\u0628\u0627\u0634\u0631 \u062D\u062F\u0647 \u0627\u0644\u0622\u0646. \u0647\u0630\u0647 \u0627\u0644\u0625\u062C\u0627\u0628\u0629 \u0641\u064A \u0627\u0644\u062F\u0631\u062F\u0634\u0629.",
|
|
766
|
+
followUpPaused: "\u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0627\u0644\u0645\u0628\u0627\u0634\u0631 \u0645\u062A\u0648\u0642\u0641 \u0645\u0624\u0642\u062A\u064B\u0627 \u0627\u0644\u0622\u0646. \u0647\u0630\u0647 \u0627\u0644\u0625\u062C\u0627\u0628\u0629 \u0641\u064A \u0627\u0644\u062F\u0631\u062F\u0634\u0629.",
|
|
767
|
+
followUpUnread: "\u062A\u0639\u0630\u0651\u0631 \u0639\u0644\u0649 \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0642\u0631\u0627\u0621\u0629 \u062A\u0644\u0643 \u0627\u0644\u0625\u062C\u0627\u0628\u0629. \u0625\u0646\u0647\u0627 \u0641\u064A \u0627\u0644\u062F\u0631\u062F\u0634\u0629."
|
|
616
768
|
};
|
|
617
769
|
var TA = {
|
|
618
770
|
checking: "\u0BA8\u0BC7\u0BB0\u0BB2\u0BC8 \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0B95\u0BBF\u0B9F\u0BC8\u0B95\u0BCD\u0B95\u0BBF\u0BB1\u0BA4\u0BBE \u0B8E\u0BA9\u0B9A\u0BCD \u0B9A\u0BB0\u0BBF\u0BAA\u0BBE\u0BB0\u0BCD\u0B95\u0BCD\u0B95\u0BBF\u0BB1\u0BA4\u0BC1\u2026",
|
|
@@ -673,7 +825,16 @@ var TA = {
|
|
|
673
825
|
usedToday: "\u0B87\u0BA9\u0BCD\u0BB1\u0BC8\u0BAF \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0B85\u0BAE\u0BB0\u0BCD\u0BB5\u0BC8 \u0BA8\u0BC0\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0BAA\u0BAF\u0BA9\u0BCD\u0BAA\u0B9F\u0BC1\u0BA4\u0BCD\u0BA4\u0BBF\u0BB5\u0BBF\u0B9F\u0BCD\u0B9F\u0BC0\u0BB0\u0BCD\u0B95\u0BB3\u0BCD. \u0BAA\u0BA4\u0BBF\u0BB2\u0BCD\u0B95\u0BB3\u0BCD \u0B87\u0B99\u0BCD\u0B95\u0BC7 \u0B89\u0BB0\u0BC8\u0BAF\u0BBE\u0B95\u0B95\u0BCD \u0B95\u0BBE\u0B9F\u0BCD\u0B9F\u0BAA\u0BCD\u0BAA\u0B9F\u0BC1\u0BAE\u0BCD.",
|
|
674
826
|
needsAnswerAny: "\u0B87\u0B99\u0BCD\u0B95\u0BC7 \u0B92\u0BB0\u0BC1 \u0B95\u0BC7\u0BB3\u0BCD\u0BB5\u0BBF\u0BAF\u0BC8\u0B95\u0BCD \u0B95\u0BC7\u0BB3\u0BC1\u0B99\u0BCD\u0B95\u0BB3\u0BCD, \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BC8 \u0B89\u0BB0\u0B95\u0BCD\u0B95\u0BAA\u0BCD \u0BAA\u0B9F\u0BBF\u0B95\u0BCD\u0B95\u0BC1\u0BAE\u0BCD.",
|
|
675
827
|
startNewChatDetailAny: "\u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0BA4\u0BBE\u0BB5\u0BB2\u0BBF\u0BB2\u0BCD \u0BA4\u0BCA\u0B9F\u0B99\u0BCD\u0B95\u0BBF\u0BAF \u0BAA\u0BC1\u0BA4\u0BBF\u0BAF \u0B85\u0BB0\u0B9F\u0BCD\u0B9F\u0BC8\u0BAF\u0BBF\u0BA9\u0BCD \u0BAE\u0BC1\u0BA4\u0BB2\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BC8 \u0BAE\u0B9F\u0BCD\u0B9F\u0BC1\u0BAE\u0BC7, \u0B85\u0BA4\u0BC1 \u0BAA\u0BC7\u0B9A\u0BCD\u0B9A\u0BC1\u0B9A\u0BCD \u0B9A\u0BB0\u0BBF\u0BAA\u0BBE\u0BB0\u0BCD\u0BAA\u0BCD\u0BAA\u0BC1\u0B95\u0BB3\u0BBF\u0BB2\u0BCD \u0BA4\u0BC7\u0BB1\u0BBF\u0BA9\u0BBE\u0BB2\u0BCD, \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0B89\u0BB0\u0B95\u0BCD\u0B95\u0BAA\u0BCD \u0BAA\u0B9F\u0BBF\u0B95\u0BCD\u0B95\u0BC1\u0BAE\u0BCD.",
|
|
676
|
-
notEligibleAny: "\u0B87\u0BA8\u0BCD\u0BA4\u0BAA\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BC8 \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BBE\u0BB2\u0BCD \u0B89\u0BB0\u0B95\u0BCD\u0B95\u0BAA\u0BCD \u0BAA\u0B9F\u0BBF\u0B95\u0BCD\u0B95 \u0BAE\u0BC1\u0B9F\u0BBF\u0BAF\u0BBE\u0BA4\u0BC1. \u0BAA\u0BA4\u0BBF\u0BB2\u0BC1\u0BAE\u0BCD \u0B86\u0BA4\u0BBE\u0BB0\u0B99\u0BCD\u0B95\u0BB3\u0BC1\u0BAE\u0BCD \u0B85\u0BB0\u0B9F\u0BCD\u0B9F\u0BC8\u0BAF\u0BBF\u0BB2\u0BCD \u0B89\u0BB3\u0BCD\u0BB3\u0BA9."
|
|
828
|
+
notEligibleAny: "\u0B87\u0BA8\u0BCD\u0BA4\u0BAA\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BC8 \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BBE\u0BB2\u0BCD \u0B89\u0BB0\u0B95\u0BCD\u0B95\u0BAA\u0BCD \u0BAA\u0B9F\u0BBF\u0B95\u0BCD\u0B95 \u0BAE\u0BC1\u0B9F\u0BBF\u0BAF\u0BBE\u0BA4\u0BC1. \u0BAA\u0BA4\u0BBF\u0BB2\u0BC1\u0BAE\u0BCD \u0B86\u0BA4\u0BBE\u0BB0\u0B99\u0BCD\u0B95\u0BB3\u0BC1\u0BAE\u0BCD \u0B85\u0BB0\u0B9F\u0BCD\u0B9F\u0BC8\u0BAF\u0BBF\u0BB2\u0BCD \u0B89\u0BB3\u0BCD\u0BB3\u0BA9.",
|
|
829
|
+
// TRANSLATION REVIEW PENDING (BOFF-9797): draft, the lines of a session that reads the answers after the first
|
|
830
|
+
// (Stage D).
|
|
831
|
+
followUpIdle: "\u0B95\u0BC7\u0BB3\u0BCD\u0BB5\u0BBF \u0B87\u0BB2\u0BCD\u0BB2\u0BBE\u0BAE\u0BB2\u0BCD \u0B9A\u0BC1\u0BAE\u0BBE\u0BB0\u0BCD \u0BAE\u0BC1\u0BAA\u0BCD\u0BAA\u0BA4\u0BC1 \u0BB5\u0BBF\u0BA9\u0BBE\u0B9F\u0BBF\u0B95\u0BB3\u0BCD \u0B95\u0BB4\u0BBF\u0BA4\u0BCD\u0BA4\u0BC1 \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0B9F\u0BC8\u0BAF\u0BC1\u0BAE\u0BCD.",
|
|
832
|
+
followUpNoTime: "\u0B85\u0BA8\u0BCD\u0BA4\u0BAA\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BC8\u0BAA\u0BCD \u0BAA\u0B9F\u0BBF\u0B95\u0BCD\u0B95 \u0B87\u0BA8\u0BCD\u0BA4 \u0B85\u0BAE\u0BB0\u0BCD\u0BB5\u0BBF\u0BB2\u0BCD \u0BAA\u0BCB\u0BA4\u0BC1\u0BAE\u0BBE\u0BA9 \u0BA8\u0BC7\u0BB0\u0BAE\u0BCD \u0B87\u0BB2\u0BCD\u0BB2\u0BC8. \u0B85\u0BA4\u0BC1 \u0B85\u0BB0\u0B9F\u0BCD\u0B9F\u0BC8\u0BAF\u0BBF\u0BB2\u0BCD \u0B89\u0BB3\u0BCD\u0BB3\u0BA4\u0BC1.",
|
|
833
|
+
followUpMaxAnswers: "\u0B87\u0BA8\u0BCD\u0BA4 \u0B85\u0BAE\u0BB0\u0BCD\u0BB5\u0BBF\u0BB2\u0BCD \u0BAA\u0B9F\u0BBF\u0B95\u0BCD\u0B95\u0B95\u0BCD\u0B95\u0BC2\u0B9F\u0BBF\u0BAF \u0B8E\u0BB2\u0BCD\u0BB2\u0BBE\u0BAA\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BCD\u0B95\u0BB3\u0BC8\u0BAF\u0BC1\u0BAE\u0BCD \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0BAA\u0B9F\u0BBF\u0BA4\u0BCD\u0BA4\u0BC1\u0BB5\u0BBF\u0B9F\u0BCD\u0B9F\u0BA4\u0BC1. \u0B87\u0BA8\u0BCD\u0BA4\u0BAA\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BCD \u0B85\u0BB0\u0B9F\u0BCD\u0B9F\u0BC8\u0BAF\u0BBF\u0BB2\u0BCD \u0B89\u0BB3\u0BCD\u0BB3\u0BA4\u0BC1.",
|
|
834
|
+
followUpLast: "\u0B87\u0BA8\u0BCD\u0BA4 \u0B85\u0BAE\u0BB0\u0BCD\u0BB5\u0BBF\u0BB2\u0BCD \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0BAA\u0B9F\u0BBF\u0B95\u0BCD\u0B95\u0B95\u0BCD\u0B95\u0BC2\u0B9F\u0BBF\u0BAF \u0B95\u0B9F\u0BC8\u0B9A\u0BBF \u0BAA\u0BA4\u0BBF\u0BB2\u0BCD \u0B85\u0BA4\u0BC1\u0BA4\u0BBE\u0BA9\u0BCD.",
|
|
835
|
+
followUpLimits: "\u0BA8\u0BC7\u0BB0\u0BB2\u0BC8 \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0BA4\u0BB1\u0BCD\u0BAA\u0BCB\u0BA4\u0BC1 \u0B85\u0BA4\u0BA9\u0BCD \u0BB5\u0BB0\u0BAE\u0BCD\u0BAA\u0BC8 \u0B8E\u0B9F\u0BCD\u0B9F\u0BBF\u0BB5\u0BBF\u0B9F\u0BCD\u0B9F\u0BA4\u0BC1. \u0B87\u0BA8\u0BCD\u0BA4\u0BAA\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BCD \u0B85\u0BB0\u0B9F\u0BCD\u0B9F\u0BC8\u0BAF\u0BBF\u0BB2\u0BCD \u0B89\u0BB3\u0BCD\u0BB3\u0BA4\u0BC1.",
|
|
836
|
+
followUpPaused: "\u0BA8\u0BC7\u0BB0\u0BB2\u0BC8 \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0BA4\u0BB1\u0BCD\u0BAA\u0BCB\u0BA4\u0BC1 \u0B87\u0B9F\u0BC8\u0BA8\u0BBF\u0BB1\u0BC1\u0BA4\u0BCD\u0BA4\u0BAA\u0BCD\u0BAA\u0B9F\u0BCD\u0B9F\u0BC1\u0BB3\u0BCD\u0BB3\u0BA4\u0BC1. \u0B87\u0BA8\u0BCD\u0BA4\u0BAA\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BCD \u0B85\u0BB0\u0B9F\u0BCD\u0B9F\u0BC8\u0BAF\u0BBF\u0BB2\u0BCD \u0B89\u0BB3\u0BCD\u0BB3\u0BA4\u0BC1.",
|
|
837
|
+
followUpUnread: "\u0B85\u0BA8\u0BCD\u0BA4\u0BAA\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BC8 \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BBE\u0BB2\u0BCD \u0BAA\u0B9F\u0BBF\u0B95\u0BCD\u0B95 \u0BAE\u0BC1\u0B9F\u0BBF\u0BAF\u0BB5\u0BBF\u0BB2\u0BCD\u0BB2\u0BC8. \u0B85\u0BA4\u0BC1 \u0B85\u0BB0\u0B9F\u0BCD\u0B9F\u0BC8\u0BAF\u0BBF\u0BB2\u0BCD \u0B89\u0BB3\u0BCD\u0BB3\u0BA4\u0BC1."
|
|
677
838
|
};
|
|
678
839
|
var STRINGS = {
|
|
679
840
|
en: EN,
|
|
@@ -823,6 +984,46 @@ var CONSENT = /* @__PURE__ */ new Map([
|
|
|
823
984
|
questions: "any"
|
|
824
985
|
}
|
|
825
986
|
}
|
|
987
|
+
],
|
|
988
|
+
[
|
|
989
|
+
// Stage D (BOFF-9797), a DRAFT for the operator's review that no environment uses yet (global-speech-svc reserves the
|
|
990
|
+
// same two versions): web-consent-2026-11-12-b with only its intro changed. The avatar reads the answer to each
|
|
991
|
+
// question asked in the Avatar tab, in one session, waits about thirty seconds after an answer for the next question,
|
|
992
|
+
// then ends; the session lasts at most three minutes, and an answer that does not fit the time left is read in part
|
|
993
|
+
// (and said so) or not read, and stays in Chat. The notice files of record and their digests are a gitops change of
|
|
994
|
+
// their own, made with the review.
|
|
995
|
+
"web-consent-2026-11-19-d",
|
|
996
|
+
{
|
|
997
|
+
en: {
|
|
998
|
+
title: "Live AI avatar (beta)",
|
|
999
|
+
introProduct: null,
|
|
1000
|
+
intro: "This is an AI avatar, not a real person. It reads aloud the answers to the questions you ask in the Avatar tab, provided each passes the speech checks. After an answer, it waits about thirty seconds for your next question, then ends. A session lasts at most three minutes; if an answer doesn't fit in the time left, it reads only the first part and says so, or doesn't read it, and the full answer stays in Chat. The answers are AI-generated and can be wrong. Don't put personal details in a question you type.",
|
|
1001
|
+
processing: "When you start, Cloudflare checks for bots. The answer text goes to Google to create the voice. Our server sends that voice through LiveKit to Protoface, which makes the avatar video streamed through LiveKit. Your browser connects to LiveKit, which sees your IP address.",
|
|
1002
|
+
storage: "Your microphone and camera are not used. We do not record the live session or store the generated voice. We keep a record of this consent. The privacy notice explains our safeguards, storage periods and the providers' processing. For people aged eighteen and over.",
|
|
1003
|
+
privacyLink: "How we handle this",
|
|
1004
|
+
botCheck: "turnstile",
|
|
1005
|
+
questions: "any",
|
|
1006
|
+
followUps: true
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
],
|
|
1010
|
+
[
|
|
1011
|
+
// The words of web-consent-2026-11-19-d without "When you start, Cloudflare checks for bots.", for a server that runs
|
|
1012
|
+
// no bot check. A DRAFT, like it.
|
|
1013
|
+
"web-consent-2026-11-20-d",
|
|
1014
|
+
{
|
|
1015
|
+
en: {
|
|
1016
|
+
title: "Live AI avatar (beta)",
|
|
1017
|
+
introProduct: null,
|
|
1018
|
+
intro: "This is an AI avatar, not a real person. It reads aloud the answers to the questions you ask in the Avatar tab, provided each passes the speech checks. After an answer, it waits about thirty seconds for your next question, then ends. A session lasts at most three minutes; if an answer doesn't fit in the time left, it reads only the first part and says so, or doesn't read it, and the full answer stays in Chat. The answers are AI-generated and can be wrong. Don't put personal details in a question you type.",
|
|
1019
|
+
processing: "The answer text goes to Google to create the voice. Our server sends that voice through LiveKit to Protoface, which makes the avatar video streamed through LiveKit. Your browser connects to LiveKit, which sees your IP address.",
|
|
1020
|
+
storage: "Your microphone and camera are not used. We do not record the live session or store the generated voice. We keep a record of this consent. The privacy notice explains our safeguards, storage periods and the providers' processing. For people aged eighteen and over.",
|
|
1021
|
+
privacyLink: "How we handle this",
|
|
1022
|
+
botCheck: "off",
|
|
1023
|
+
questions: "any",
|
|
1024
|
+
followUps: true
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
826
1027
|
]
|
|
827
1028
|
]);
|
|
828
1029
|
function avatarConsentCopy(version, locale) {
|
|
@@ -913,7 +1114,8 @@ function useAvatarMic({
|
|
|
913
1114
|
base,
|
|
914
1115
|
offer,
|
|
915
1116
|
refreshOffer,
|
|
916
|
-
onText
|
|
1117
|
+
onText,
|
|
1118
|
+
onStart
|
|
917
1119
|
}) {
|
|
918
1120
|
const S = avatarMicStrings(locale);
|
|
919
1121
|
const copy = avatarMicNotice(AVATAR_MIC_NOTICE_VERSION, locale);
|
|
@@ -935,8 +1137,8 @@ function useAvatarMic({
|
|
|
935
1137
|
const buttonRef = react.useRef(null);
|
|
936
1138
|
const allowRef = react.useRef(null);
|
|
937
1139
|
const usedEpochRef = react.useRef(0);
|
|
938
|
-
const latest = react.useRef({ offer, refreshOffer, onText, base });
|
|
939
|
-
latest.current = { offer, refreshOffer, onText, base };
|
|
1140
|
+
const latest = react.useRef({ offer, refreshOffer, onText, onStart, base });
|
|
1141
|
+
latest.current = { offer, refreshOffer, onText, onStart, base };
|
|
940
1142
|
const cancel = react.useCallback(() => {
|
|
941
1143
|
seqRef.current += 1;
|
|
942
1144
|
setLoading(false);
|
|
@@ -999,6 +1201,7 @@ function useAvatarMic({
|
|
|
999
1201
|
cancel();
|
|
1000
1202
|
return;
|
|
1001
1203
|
}
|
|
1204
|
+
latest.current.onStart?.();
|
|
1002
1205
|
if (!agreedRef.current) {
|
|
1003
1206
|
setNoticeOpen(true);
|
|
1004
1207
|
void loadAvatarMic().catch(() => void 0);
|
|
@@ -1035,7 +1238,13 @@ function useAvatarMic({
|
|
|
1035
1238
|
}
|
|
1036
1239
|
}, [state]);
|
|
1037
1240
|
if (!possible || S === null || copy === null) {
|
|
1038
|
-
return {
|
|
1241
|
+
return {
|
|
1242
|
+
button: null,
|
|
1243
|
+
notice: null,
|
|
1244
|
+
lines: null,
|
|
1245
|
+
cancel,
|
|
1246
|
+
recording: false
|
|
1247
|
+
};
|
|
1039
1248
|
}
|
|
1040
1249
|
const status = withdrawn !== null ? S[FAILURE_TEXT[withdrawn]] : loading2 ? S.micStarting : loadFailed ? S.micFailed : state.name === "starting" ? S.micStarting : state.name === "listening" ? S.micListening : state.name === "finishing" ? S.micFinishing : state.name === "done" ? state.text === "" ? S.micNoSpeech : state.limit ? S.micDoneLimit : S.micDone : state.name === "failed" ? S[FAILURE_TEXT[state.reason]] : "";
|
|
1041
1250
|
const heard = offered && (state.name === "listening" || state.name === "finishing") ? state.text : "";
|
|
@@ -1139,7 +1348,13 @@ function useAvatarMic({
|
|
|
1139
1348
|
}
|
|
1140
1349
|
)
|
|
1141
1350
|
] });
|
|
1142
|
-
return {
|
|
1351
|
+
return {
|
|
1352
|
+
button,
|
|
1353
|
+
notice,
|
|
1354
|
+
lines,
|
|
1355
|
+
cancel,
|
|
1356
|
+
recording: offered && state.name === "listening"
|
|
1357
|
+
};
|
|
1143
1358
|
}
|
|
1144
1359
|
|
|
1145
1360
|
// src/components/explore-avatar-turnstile.ts
|
|
@@ -1262,6 +1477,12 @@ var END_GRACE_MS = 1e4;
|
|
|
1262
1477
|
var CLOCK_TOLERANCE_MS = 5e3;
|
|
1263
1478
|
var UNHEARD_GRACE_MS = 5e3;
|
|
1264
1479
|
var PLAYOUT_TAIL_MS = 5e3;
|
|
1480
|
+
var QUIET_PLAYED_OUT_MS = 2500;
|
|
1481
|
+
var OWED_VOICE_WAIT_MS = 15e3;
|
|
1482
|
+
var LISTENING_MARK_MS = 1e4;
|
|
1483
|
+
var BUSY_RETRY_MS = 2e3;
|
|
1484
|
+
var BUSY_RETRY_MAX_MS = 8e3;
|
|
1485
|
+
var MAX_BUSY_RETRIES = 24;
|
|
1265
1486
|
var SERVICE_MAX_AGE_MS = 15 * 6e4;
|
|
1266
1487
|
var ANSWER_OFFER_MS = SERVICE_MAX_AGE_MS - 6e4;
|
|
1267
1488
|
var MAX_TIMER_MS = 2147483647;
|
|
@@ -1276,6 +1497,12 @@ function stopAttending(handle) {
|
|
|
1276
1497
|
if (handle.tail !== null) clearTimeout(handle.tail);
|
|
1277
1498
|
handle.unheard = null;
|
|
1278
1499
|
handle.tail = null;
|
|
1500
|
+
const run = handle.followUps;
|
|
1501
|
+
if (run === null) return;
|
|
1502
|
+
if (run.quiet !== null) clearTimeout(run.quiet);
|
|
1503
|
+
if (run.retry !== null) clearTimeout(run.retry);
|
|
1504
|
+
run.quiet = null;
|
|
1505
|
+
run.retry = null;
|
|
1279
1506
|
}
|
|
1280
1507
|
var REFUSAL_TEXT = {
|
|
1281
1508
|
busy: "busy",
|
|
@@ -1286,6 +1513,18 @@ var REFUSAL_TEXT = {
|
|
|
1286
1513
|
not_eligible: "notEligible",
|
|
1287
1514
|
answer_claimed: "answerClaimed"
|
|
1288
1515
|
};
|
|
1516
|
+
var FOLLOW_UP_TEXT = {
|
|
1517
|
+
no_time: "followUpNoTime",
|
|
1518
|
+
max_answers: "followUpMaxAnswers",
|
|
1519
|
+
// The same line as a first answer the checks refuse: it stays in Chat as text.
|
|
1520
|
+
not_eligible: "notSpoken",
|
|
1521
|
+
answer_claimed: "answerClaimed",
|
|
1522
|
+
limits: "followUpLimits",
|
|
1523
|
+
paused: "followUpPaused",
|
|
1524
|
+
busy: "followUpUnread",
|
|
1525
|
+
answer_failed: "answerFailed",
|
|
1526
|
+
failed: "followUpUnread"
|
|
1527
|
+
};
|
|
1289
1528
|
var EMPTY = {
|
|
1290
1529
|
kind: "empty",
|
|
1291
1530
|
answerId: null,
|
|
@@ -1360,7 +1599,11 @@ function inFlight(conversation) {
|
|
|
1360
1599
|
return conversation?.phase === "sending" || conversation?.phase === "streaming";
|
|
1361
1600
|
}
|
|
1362
1601
|
function acceptsQuestion(slot) {
|
|
1363
|
-
|
|
1602
|
+
if (!slot.any || slot.textMode || slot.reason !== null) return false;
|
|
1603
|
+
return slot.phase === "needs-answer" ? slot.kind === "empty" : slot.phase === "live" && slot.followUp;
|
|
1604
|
+
}
|
|
1605
|
+
function followUpBox(phase, run, token) {
|
|
1606
|
+
return phase.name === "live" && run !== null && phase.session.followUp !== null && !phase.session.followUp.closed && token === run.token;
|
|
1364
1607
|
}
|
|
1365
1608
|
function micOfferOf(result) {
|
|
1366
1609
|
return result.kind === "available" && result.offer.stt !== void 0 ? { ...result.offer.stt, receivedAt: Date.now() } : null;
|
|
@@ -1515,6 +1758,7 @@ function AvatarLiveView({
|
|
|
1515
1758
|
const textBoxRef = react.useRef(false);
|
|
1516
1759
|
const [micOffer, setMicOffer] = react.useState(null);
|
|
1517
1760
|
const micCancelRef = react.useRef(() => void 0);
|
|
1761
|
+
const sendDueRef = react.useRef(() => void 0);
|
|
1518
1762
|
const checkRef = react.useRef(null);
|
|
1519
1763
|
const askedLocale = react.useRef(locale);
|
|
1520
1764
|
const startRef = react.useRef(null);
|
|
@@ -1603,10 +1847,13 @@ function AvatarLiveView({
|
|
|
1603
1847
|
if (!handle || handle.done || latest.current.live.endUnattended !== true) {
|
|
1604
1848
|
return;
|
|
1605
1849
|
}
|
|
1606
|
-
const
|
|
1607
|
-
if (
|
|
1850
|
+
const shown2 = phaseRef.current;
|
|
1851
|
+
if (shown2.name !== "pending" && shown2.name !== "live") return;
|
|
1852
|
+
const current = shown2.name;
|
|
1608
1853
|
const away = !inSight();
|
|
1609
|
-
|
|
1854
|
+
const followUp2 = shown2.session.followUp;
|
|
1855
|
+
const asking = followUp2 !== null && followUp2.pending !== null;
|
|
1856
|
+
if (away && handle.playedOut && current === "live" && !asking) {
|
|
1610
1857
|
finish("finished", "user");
|
|
1611
1858
|
return;
|
|
1612
1859
|
}
|
|
@@ -1625,6 +1872,184 @@ function AvatarLiveView({
|
|
|
1625
1872
|
}
|
|
1626
1873
|
}, UNHEARD_GRACE_MS);
|
|
1627
1874
|
}, [finish, inSight]);
|
|
1875
|
+
const showFollowUp = react.useCallback(
|
|
1876
|
+
(handle, next, spoken) => {
|
|
1877
|
+
const current = phaseRef.current;
|
|
1878
|
+
if (sessionRef.current !== handle || handle.done || current.name !== "pending" && current.name !== "live" || current.session.followUp === null) {
|
|
1879
|
+
return;
|
|
1880
|
+
}
|
|
1881
|
+
const session = {
|
|
1882
|
+
...current.session,
|
|
1883
|
+
...spoken,
|
|
1884
|
+
followUp: { ...current.session.followUp, ...next }
|
|
1885
|
+
};
|
|
1886
|
+
go(
|
|
1887
|
+
current.name === "live" ? { name: "live", session } : { name: "pending", session }
|
|
1888
|
+
);
|
|
1889
|
+
},
|
|
1890
|
+
[go]
|
|
1891
|
+
);
|
|
1892
|
+
const tell = react.useCallback(
|
|
1893
|
+
(handle, kind2) => {
|
|
1894
|
+
if (handle.done || handle.followUps === null) return;
|
|
1895
|
+
void requestTurn({
|
|
1896
|
+
base: handle.base,
|
|
1897
|
+
headers: handle.headers,
|
|
1898
|
+
sessionId: handle.session.sessionId,
|
|
1899
|
+
secret: handle.session.endSecret,
|
|
1900
|
+
kind: kind2
|
|
1901
|
+
});
|
|
1902
|
+
},
|
|
1903
|
+
[]
|
|
1904
|
+
);
|
|
1905
|
+
const whenQuiet = react.useCallback(
|
|
1906
|
+
(handle, delay) => {
|
|
1907
|
+
const run = handle.followUps;
|
|
1908
|
+
if (run === null) return;
|
|
1909
|
+
const wait = (ms) => {
|
|
1910
|
+
if (run.quiet !== null) clearTimeout(run.quiet);
|
|
1911
|
+
run.quiet = setTimeout(() => {
|
|
1912
|
+
run.quiet = null;
|
|
1913
|
+
if (sessionRef.current !== handle || handle.done || run.voice) {
|
|
1914
|
+
return;
|
|
1915
|
+
}
|
|
1916
|
+
if (run.owed) {
|
|
1917
|
+
run.owed = false;
|
|
1918
|
+
wait(OWED_VOICE_WAIT_MS);
|
|
1919
|
+
return;
|
|
1920
|
+
}
|
|
1921
|
+
handle.playedOut = true;
|
|
1922
|
+
showFollowUp(handle, { speaking: false });
|
|
1923
|
+
attend();
|
|
1924
|
+
sendDueRef.current(handle);
|
|
1925
|
+
}, ms);
|
|
1926
|
+
};
|
|
1927
|
+
wait(delay);
|
|
1928
|
+
},
|
|
1929
|
+
[attend, showFollowUp]
|
|
1930
|
+
);
|
|
1931
|
+
const heardVoice = react.useCallback(
|
|
1932
|
+
(handle, speaking) => {
|
|
1933
|
+
const run = handle.followUps;
|
|
1934
|
+
if (run === null) return;
|
|
1935
|
+
run.voice = speaking;
|
|
1936
|
+
if (!speaking) {
|
|
1937
|
+
whenQuiet(handle, QUIET_PLAYED_OUT_MS);
|
|
1938
|
+
return;
|
|
1939
|
+
}
|
|
1940
|
+
if (run.quiet !== null) clearTimeout(run.quiet);
|
|
1941
|
+
run.quiet = null;
|
|
1942
|
+
run.heard = true;
|
|
1943
|
+
run.starts += 1;
|
|
1944
|
+
run.owed = false;
|
|
1945
|
+
handle.playedOut = false;
|
|
1946
|
+
showFollowUp(handle, { speaking: true });
|
|
1947
|
+
},
|
|
1948
|
+
[showFollowUp, whenQuiet]
|
|
1949
|
+
);
|
|
1950
|
+
const sendDue = react.useCallback(
|
|
1951
|
+
(handle) => {
|
|
1952
|
+
const run = handle.followUps;
|
|
1953
|
+
const due = run?.due ?? null;
|
|
1954
|
+
if (run === null || due === null || due.sending || run.retry !== null || handle.done || sessionRef.current !== handle || run.heard && !handle.playedOut) {
|
|
1955
|
+
return;
|
|
1956
|
+
}
|
|
1957
|
+
due.sending = true;
|
|
1958
|
+
const starts = run.starts;
|
|
1959
|
+
const read = () => requestTurn({
|
|
1960
|
+
base: handle.base,
|
|
1961
|
+
headers: handle.headers,
|
|
1962
|
+
sessionId: handle.session.sessionId,
|
|
1963
|
+
secret: handle.session.endSecret,
|
|
1964
|
+
kind: "answer",
|
|
1965
|
+
messageId: due.messageId,
|
|
1966
|
+
idempotencyKey: due.idempotencyKey
|
|
1967
|
+
});
|
|
1968
|
+
const notRead = (unread, closed) => {
|
|
1969
|
+
run.due = null;
|
|
1970
|
+
showFollowUp(handle, {
|
|
1971
|
+
pending: null,
|
|
1972
|
+
unread: due.known ?? unread,
|
|
1973
|
+
...closed ? { closed } : {}
|
|
1974
|
+
});
|
|
1975
|
+
attend();
|
|
1976
|
+
};
|
|
1977
|
+
void (async () => {
|
|
1978
|
+
let result = await read();
|
|
1979
|
+
if (result.kind === "failed" && result.lost && !handle.done) {
|
|
1980
|
+
result = await read();
|
|
1981
|
+
}
|
|
1982
|
+
due.sending = false;
|
|
1983
|
+
if (sessionRef.current !== handle || handle.done || run.due !== due) {
|
|
1984
|
+
return;
|
|
1985
|
+
}
|
|
1986
|
+
if (result.kind === "refused" && result.reason === "busy" && due.busy < MAX_BUSY_RETRIES) {
|
|
1987
|
+
due.busy += 1;
|
|
1988
|
+
if (run.heard && !handle.playedOut) return;
|
|
1989
|
+
const delay = Math.max(
|
|
1990
|
+
result.retryAfterSeconds === void 0 ? BUSY_RETRY_MS : result.retryAfterSeconds * 1e3,
|
|
1991
|
+
Math.min(BUSY_RETRY_MAX_MS, BUSY_RETRY_MS * 2 ** (due.busy - 1))
|
|
1992
|
+
);
|
|
1993
|
+
if (Date.now() + delay >= handle.endsAt) {
|
|
1994
|
+
notRead("no_time", false);
|
|
1995
|
+
return;
|
|
1996
|
+
}
|
|
1997
|
+
run.retry = setTimeout(() => {
|
|
1998
|
+
run.retry = null;
|
|
1999
|
+
sendDueRef.current(handle);
|
|
2000
|
+
}, delay);
|
|
2001
|
+
return;
|
|
2002
|
+
}
|
|
2003
|
+
if (result.kind !== "accepted") {
|
|
2004
|
+
notRead(
|
|
2005
|
+
result.kind === "refused" ? result.reason : "failed",
|
|
2006
|
+
result.kind === "gone" || result.kind === "refused" && result.reason === "max_answers"
|
|
2007
|
+
);
|
|
2008
|
+
return;
|
|
2009
|
+
}
|
|
2010
|
+
run.due = null;
|
|
2011
|
+
run.answers += 1;
|
|
2012
|
+
const heardSince = run.starts !== starts;
|
|
2013
|
+
run.owed = !heardSince;
|
|
2014
|
+
const playing = !heardSince || run.voice || run.quiet !== null;
|
|
2015
|
+
if (playing) handle.playedOut = false;
|
|
2016
|
+
showFollowUp(
|
|
2017
|
+
handle,
|
|
2018
|
+
{
|
|
2019
|
+
speaking: playing,
|
|
2020
|
+
pending: null,
|
|
2021
|
+
unread: null,
|
|
2022
|
+
closed: run.answers >= run.limits.maxAnswers
|
|
2023
|
+
},
|
|
2024
|
+
{ captions: result.captions, excerpt: result.excerpt }
|
|
2025
|
+
);
|
|
2026
|
+
if (!playing) {
|
|
2027
|
+
attend();
|
|
2028
|
+
return;
|
|
2029
|
+
}
|
|
2030
|
+
if (run.owed && run.heard && !run.voice) whenQuiet(handle, 0);
|
|
2031
|
+
})();
|
|
2032
|
+
},
|
|
2033
|
+
[attend, showFollowUp, whenQuiet]
|
|
2034
|
+
);
|
|
2035
|
+
sendDueRef.current = sendDue;
|
|
2036
|
+
const readFollowUp = react.useCallback(
|
|
2037
|
+
(handle, messageId, known) => {
|
|
2038
|
+
const run = handle.followUps;
|
|
2039
|
+
if (run === null) return;
|
|
2040
|
+
run.sent.add(messageId);
|
|
2041
|
+
const idempotencyKey = newIdempotencyKey();
|
|
2042
|
+
if (idempotencyKey === null) {
|
|
2043
|
+
showFollowUp(handle, { pending: null, unread: known ?? "failed" });
|
|
2044
|
+
attend();
|
|
2045
|
+
return;
|
|
2046
|
+
}
|
|
2047
|
+
run.due = { messageId, idempotencyKey, known, busy: 0, sending: false };
|
|
2048
|
+
showFollowUp(handle, { pending: "reading", unread: null });
|
|
2049
|
+
sendDue(handle);
|
|
2050
|
+
},
|
|
2051
|
+
[attend, sendDue, showFollowUp]
|
|
2052
|
+
);
|
|
1628
2053
|
const cancelStart = react.useCallback((reason) => {
|
|
1629
2054
|
const run = startRef.current;
|
|
1630
2055
|
if (!run) return;
|
|
@@ -1688,16 +2113,32 @@ function AvatarLiveView({
|
|
|
1688
2113
|
});
|
|
1689
2114
|
}, [applyAvailability, go]);
|
|
1690
2115
|
const begin = react.useCallback(
|
|
1691
|
-
(session,
|
|
2116
|
+
(session, context) => {
|
|
2117
|
+
const { offer } = context;
|
|
1692
2118
|
const received = Date.now();
|
|
1693
2119
|
const longest = offer.maxSessionSeconds * 1e3;
|
|
1694
2120
|
const ahead = session.endsAt - received;
|
|
1695
2121
|
const endsAt = ahead > 0 && ahead <= longest + CLOCK_TOLERANCE_MS ? session.endsAt : received + longest;
|
|
2122
|
+
const followUps = session.followUps !== void 0 && offer.copy.followUps === true && offer.questions === "any" ? {
|
|
2123
|
+
limits: session.followUps,
|
|
2124
|
+
token: context.conversationToken,
|
|
2125
|
+
answers: 1,
|
|
2126
|
+
voice: false,
|
|
2127
|
+
heard: false,
|
|
2128
|
+
starts: 0,
|
|
2129
|
+
owed: true,
|
|
2130
|
+
quiet: null,
|
|
2131
|
+
asked: null,
|
|
2132
|
+
due: null,
|
|
2133
|
+
retry: null,
|
|
2134
|
+
sent: /* @__PURE__ */ new Set([context.messageId]),
|
|
2135
|
+
seq: 0
|
|
2136
|
+
} : null;
|
|
1696
2137
|
sessionRef.current = {
|
|
1697
2138
|
session,
|
|
1698
|
-
base:
|
|
1699
|
-
headers:
|
|
1700
|
-
module,
|
|
2139
|
+
base: context.base,
|
|
2140
|
+
headers: context.headers,
|
|
2141
|
+
module: context.module,
|
|
1701
2142
|
room: null,
|
|
1702
2143
|
joined: null,
|
|
1703
2144
|
endsAt,
|
|
@@ -1707,6 +2148,7 @@ function AvatarLiveView({
|
|
|
1707
2148
|
playedOut: false,
|
|
1708
2149
|
unheard: null,
|
|
1709
2150
|
tail: null,
|
|
2151
|
+
followUps,
|
|
1710
2152
|
done: false
|
|
1711
2153
|
};
|
|
1712
2154
|
setBlocked(false);
|
|
@@ -1716,7 +2158,13 @@ function AvatarLiveView({
|
|
|
1716
2158
|
session: {
|
|
1717
2159
|
endsAt,
|
|
1718
2160
|
captions: session.captions,
|
|
1719
|
-
excerpt: session.excerpt
|
|
2161
|
+
excerpt: session.excerpt,
|
|
2162
|
+
followUp: followUps === null ? null : {
|
|
2163
|
+
speaking: true,
|
|
2164
|
+
pending: null,
|
|
2165
|
+
unread: null,
|
|
2166
|
+
closed: followUps.answers >= followUps.limits.maxAnswers
|
|
2167
|
+
}
|
|
1720
2168
|
}
|
|
1721
2169
|
});
|
|
1722
2170
|
},
|
|
@@ -1748,7 +2196,7 @@ function AvatarLiveView({
|
|
|
1748
2196
|
}
|
|
1749
2197
|
switch (result.kind) {
|
|
1750
2198
|
case "created":
|
|
1751
|
-
begin(result.session, context
|
|
2199
|
+
begin(result.session, context);
|
|
1752
2200
|
return;
|
|
1753
2201
|
case "unusable":
|
|
1754
2202
|
sendEnd({
|
|
@@ -1846,7 +2294,14 @@ function AvatarLiveView({
|
|
|
1846
2294
|
return;
|
|
1847
2295
|
}
|
|
1848
2296
|
if (turnstile !== null) go({ name: "creating" });
|
|
1849
|
-
const context = {
|
|
2297
|
+
const context = {
|
|
2298
|
+
module: loaded,
|
|
2299
|
+
offer,
|
|
2300
|
+
messageId,
|
|
2301
|
+
conversationToken,
|
|
2302
|
+
base: base2,
|
|
2303
|
+
headers: headers2
|
|
2304
|
+
};
|
|
1850
2305
|
const result = await requestCreate({
|
|
1851
2306
|
base: base2,
|
|
1852
2307
|
headers: headers2,
|
|
@@ -2151,6 +2606,9 @@ function AvatarLiveView({
|
|
|
2151
2606
|
attend();
|
|
2152
2607
|
}, PLAYOUT_TAIL_MS);
|
|
2153
2608
|
return;
|
|
2609
|
+
case "speaking":
|
|
2610
|
+
heardVoice(handle, event.speaking);
|
|
2611
|
+
return;
|
|
2154
2612
|
case "speaker-left":
|
|
2155
2613
|
finish(
|
|
2156
2614
|
wasLive ? "finished" : "failed",
|
|
@@ -2163,7 +2621,7 @@ function AvatarLiveView({
|
|
|
2163
2621
|
return;
|
|
2164
2622
|
}
|
|
2165
2623
|
},
|
|
2166
|
-
[attend, finish, goLive, inSight]
|
|
2624
|
+
[attend, finish, goLive, heardVoice, inSight]
|
|
2167
2625
|
);
|
|
2168
2626
|
react.useEffect(() => {
|
|
2169
2627
|
if (phase.name !== "pending") return;
|
|
@@ -2299,7 +2757,13 @@ function AvatarLiveView({
|
|
|
2299
2757
|
const textMode = any && (usedToday && (phase.name === "needs-answer" || phase.name === "consent" || phase.name === "declined" || phase.name === "ended") || phase.name === "refused" && phase.reason === "limits");
|
|
2300
2758
|
textBoxRef.current = textMode;
|
|
2301
2759
|
const askState = !any ? void 0 : textMode ? "text" : phase.name === "needs-answer" ? kind === "empty" ? "ready" : kind : phase.name;
|
|
2302
|
-
const
|
|
2760
|
+
const followUp = phase.name === "live" ? phase.session.followUp : null;
|
|
2761
|
+
const followUpAsks = followUpBox(
|
|
2762
|
+
phase,
|
|
2763
|
+
sessionRef.current?.followUps ?? null,
|
|
2764
|
+
token
|
|
2765
|
+
);
|
|
2766
|
+
const askReason = inFlight(conversation) || followUp !== null && followUp.pending !== null ? S.askBusy : conversation?.canSend ? null : S.askUnavailable;
|
|
2303
2767
|
const refreshMicOffer = react.useCallback(async () => {
|
|
2304
2768
|
const { live: live2, locale: locale2, base: base2, headers: headers2 } = latest.current;
|
|
2305
2769
|
if (base2 === null) return null;
|
|
@@ -2324,21 +2788,54 @@ function AvatarLiveView({
|
|
|
2324
2788
|
},
|
|
2325
2789
|
[askMaxLength, touchFirst]
|
|
2326
2790
|
);
|
|
2791
|
+
const onMicStart = react.useCallback(() => {
|
|
2792
|
+
const handle = sessionRef.current;
|
|
2793
|
+
const current = phaseRef.current;
|
|
2794
|
+
if (handle === null || handle.followUps === null || handle.done || current.name !== "live" || current.session.followUp?.speaking !== true) {
|
|
2795
|
+
return;
|
|
2796
|
+
}
|
|
2797
|
+
const run = handle.followUps;
|
|
2798
|
+
tell(handle, "interrupt");
|
|
2799
|
+
if (run.quiet !== null) clearTimeout(run.quiet);
|
|
2800
|
+
run.quiet = null;
|
|
2801
|
+
run.owed = false;
|
|
2802
|
+
handle.playedOut = true;
|
|
2803
|
+
showFollowUp(handle, { speaking: false });
|
|
2804
|
+
}, [showFollowUp, tell]);
|
|
2327
2805
|
const mic = useAvatarMic({
|
|
2328
2806
|
enabled: live.mic === true && micOffer !== null && !off && acceptsQuestion({
|
|
2329
2807
|
any,
|
|
2330
2808
|
phase: phase.name,
|
|
2331
2809
|
kind,
|
|
2332
2810
|
textMode,
|
|
2333
|
-
reason: askReason
|
|
2811
|
+
reason: askReason,
|
|
2812
|
+
followUp: followUpAsks
|
|
2334
2813
|
}),
|
|
2335
2814
|
locale,
|
|
2336
2815
|
base,
|
|
2337
2816
|
offer: micOffer,
|
|
2338
2817
|
refreshOffer: refreshMicOffer,
|
|
2339
|
-
onText: onMicText
|
|
2818
|
+
onText: onMicText,
|
|
2819
|
+
onStart: onMicStart
|
|
2340
2820
|
});
|
|
2341
2821
|
micCancelRef.current = mic.cancel;
|
|
2822
|
+
const micRecording = mic.recording;
|
|
2823
|
+
react.useEffect(() => {
|
|
2824
|
+
const handle = sessionRef.current;
|
|
2825
|
+
if (!micRecording || handle === null || handle.followUps === null || handle.done) {
|
|
2826
|
+
return;
|
|
2827
|
+
}
|
|
2828
|
+
tell(handle, "listening");
|
|
2829
|
+
const every = Math.min(
|
|
2830
|
+
LISTENING_MARK_MS,
|
|
2831
|
+
Math.max(1e3, handle.followUps.limits.idleSeconds * 1e3 / 3)
|
|
2832
|
+
);
|
|
2833
|
+
const timer = setInterval(() => tell(handle, "listening"), every);
|
|
2834
|
+
return () => {
|
|
2835
|
+
clearInterval(timer);
|
|
2836
|
+
if (sessionRef.current === handle) tell(handle, "listening");
|
|
2837
|
+
};
|
|
2838
|
+
}, [micRecording, tell]);
|
|
2342
2839
|
const notNow = react.useCallback(() => {
|
|
2343
2840
|
const current = phaseRef.current;
|
|
2344
2841
|
if ((current.name === "consent" || current.name === "needs-answer") && current.offer.questions === "any") {
|
|
@@ -2347,6 +2844,43 @@ function AvatarLiveView({
|
|
|
2347
2844
|
}
|
|
2348
2845
|
onContinueInChat();
|
|
2349
2846
|
}, [go, onContinueInChat]);
|
|
2847
|
+
const askFollowUp = react.useCallback(
|
|
2848
|
+
(handle, conversation2, question) => {
|
|
2849
|
+
const run = handle.followUps;
|
|
2850
|
+
const current = phaseRef.current;
|
|
2851
|
+
if (run === null || handle.done || current.name !== "live" || current.session.followUp?.pending !== null || !followUpBox(current, run, conversation2.getConversationToken())) {
|
|
2852
|
+
return;
|
|
2853
|
+
}
|
|
2854
|
+
sendingRef.current = true;
|
|
2855
|
+
run.seq += 1;
|
|
2856
|
+
const seq = run.seq;
|
|
2857
|
+
setDraft("");
|
|
2858
|
+
tell(handle, "asking");
|
|
2859
|
+
showFollowUp(handle, { pending: "asking", unread: null });
|
|
2860
|
+
void conversation2.send(question, [], { surface: "AVATAR" }).then((added) => {
|
|
2861
|
+
if (added === null) {
|
|
2862
|
+
setDraft((current2) => current2 === "" ? question : current2);
|
|
2863
|
+
}
|
|
2864
|
+
if (sessionRef.current !== handle || handle.done || run.seq !== seq) {
|
|
2865
|
+
return;
|
|
2866
|
+
}
|
|
2867
|
+
if (added === null || added.conversationToken !== run.token) {
|
|
2868
|
+
showFollowUp(handle, { pending: null });
|
|
2869
|
+
attend();
|
|
2870
|
+
return;
|
|
2871
|
+
}
|
|
2872
|
+
run.asked = {
|
|
2873
|
+
messageId: added.userMessageId,
|
|
2874
|
+
token: added.conversationToken,
|
|
2875
|
+
answerId: null
|
|
2876
|
+
};
|
|
2877
|
+
showFollowUp(handle, { pending: "asking" });
|
|
2878
|
+
}).finally(() => {
|
|
2879
|
+
sendingRef.current = false;
|
|
2880
|
+
});
|
|
2881
|
+
},
|
|
2882
|
+
[attend, showFollowUp, tell]
|
|
2883
|
+
);
|
|
2350
2884
|
const ask = react.useCallback(
|
|
2351
2885
|
(text) => {
|
|
2352
2886
|
const conversation2 = conversationRef.current;
|
|
@@ -2355,6 +2889,11 @@ function AvatarLiveView({
|
|
|
2355
2889
|
return;
|
|
2356
2890
|
}
|
|
2357
2891
|
const spoken = !textBoxRef.current;
|
|
2892
|
+
const handle = sessionRef.current;
|
|
2893
|
+
if (spoken && handle !== null && handle.followUps !== null) {
|
|
2894
|
+
askFollowUp(handle, conversation2, question);
|
|
2895
|
+
return;
|
|
2896
|
+
}
|
|
2358
2897
|
if (spoken && conversation2.messages.length > 0) return;
|
|
2359
2898
|
sendingRef.current = true;
|
|
2360
2899
|
setAskedHere(null);
|
|
@@ -2376,8 +2915,64 @@ function AvatarLiveView({
|
|
|
2376
2915
|
sendingRef.current = false;
|
|
2377
2916
|
});
|
|
2378
2917
|
},
|
|
2379
|
-
[setAskedHere]
|
|
2918
|
+
[askFollowUp, setAskedHere]
|
|
2380
2919
|
);
|
|
2920
|
+
react.useEffect(() => {
|
|
2921
|
+
const handle = sessionRef.current;
|
|
2922
|
+
const run = handle?.followUps ?? null;
|
|
2923
|
+
if (handle === null || run === null || handle.done || run.asked === null) {
|
|
2924
|
+
return;
|
|
2925
|
+
}
|
|
2926
|
+
const asked = run.asked;
|
|
2927
|
+
const chat = conversationRef.current;
|
|
2928
|
+
const messages = chat?.messages ?? [];
|
|
2929
|
+
const at = messages.findIndex((message) => message.id === asked.messageId);
|
|
2930
|
+
const question = at === -1 ? void 0 : messages[at];
|
|
2931
|
+
const answer = at === -1 ? void 0 : messages[at + 1];
|
|
2932
|
+
const drop = (unread2) => {
|
|
2933
|
+
run.asked = null;
|
|
2934
|
+
showFollowUp(handle, { pending: null, unread: unread2 });
|
|
2935
|
+
attend();
|
|
2936
|
+
};
|
|
2937
|
+
const unread = (id, reason) => {
|
|
2938
|
+
if (id === null || run.sent.has(id) || !isAvatarMessageId(id)) {
|
|
2939
|
+
drop(reason);
|
|
2940
|
+
return;
|
|
2941
|
+
}
|
|
2942
|
+
run.asked = null;
|
|
2943
|
+
readFollowUp(handle, id, reason);
|
|
2944
|
+
};
|
|
2945
|
+
if (question === void 0 || (chat?.getConversationToken() ?? null) !== run.token) {
|
|
2946
|
+
drop(null);
|
|
2947
|
+
return;
|
|
2948
|
+
}
|
|
2949
|
+
if (answer === void 0) {
|
|
2950
|
+
if (!inFlight(chat)) unread(asked.answerId, "answer_failed");
|
|
2951
|
+
return;
|
|
2952
|
+
}
|
|
2953
|
+
if (answer.status === "PENDING" || answer.status === "RUNNING") {
|
|
2954
|
+
if (answer.role === "ASSISTANT") asked.answerId = answer.id;
|
|
2955
|
+
return;
|
|
2956
|
+
}
|
|
2957
|
+
if (answer.role !== "ASSISTANT") {
|
|
2958
|
+
unread(asked.answerId, "answer_failed");
|
|
2959
|
+
return;
|
|
2960
|
+
}
|
|
2961
|
+
if (answer.status !== "COMPLETED" || answer.content.trim() === "") {
|
|
2962
|
+
unread(answer.id, "answer_failed");
|
|
2963
|
+
return;
|
|
2964
|
+
}
|
|
2965
|
+
if (run.sent.has(answer.id) || !isAvatarMessageId(answer.id)) {
|
|
2966
|
+
drop("not_eligible");
|
|
2967
|
+
return;
|
|
2968
|
+
}
|
|
2969
|
+
if (at + 2 !== messages.length || !readable(question, answer)) {
|
|
2970
|
+
unread(answer.id, "not_eligible");
|
|
2971
|
+
return;
|
|
2972
|
+
}
|
|
2973
|
+
run.asked = null;
|
|
2974
|
+
readFollowUp(handle, answer.id, null);
|
|
2975
|
+
});
|
|
2381
2976
|
const newChatForAvatar = react.useCallback(() => {
|
|
2382
2977
|
const conversation2 = conversationRef.current;
|
|
2383
2978
|
if (conversation2 === null) return;
|
|
@@ -2401,7 +2996,14 @@ function AvatarLiveView({
|
|
|
2401
2996
|
const focused = document.activeElement;
|
|
2402
2997
|
if (focused && focused !== document.body && focused.isConnected) return;
|
|
2403
2998
|
rootRef.current?.querySelector(PRIMARY)?.focus();
|
|
2404
|
-
}, [
|
|
2999
|
+
}, [
|
|
3000
|
+
active,
|
|
3001
|
+
phase.name,
|
|
3002
|
+
askState,
|
|
3003
|
+
shown.pendingAnswerId,
|
|
3004
|
+
blocked,
|
|
3005
|
+
followUpAsks
|
|
3006
|
+
]);
|
|
2405
3007
|
const onPlaying = goLive;
|
|
2406
3008
|
const enablePlayback = react.useCallback(() => {
|
|
2407
3009
|
void sessionRef.current?.joined?.enablePlayback().catch(() => void 0);
|
|
@@ -2801,13 +3403,32 @@ function AvatarLiveView({
|
|
|
2801
3403
|
actions2 = stopButton;
|
|
2802
3404
|
break;
|
|
2803
3405
|
case "pending":
|
|
2804
|
-
case "live":
|
|
2805
|
-
status = phase.name === "live" ? speakingLine(phase.session) : S.connecting;
|
|
3406
|
+
case "live": {
|
|
2806
3407
|
top2 = badge;
|
|
2807
3408
|
media2 = liveMedia(phase.name === "live");
|
|
2808
3409
|
body2 = liveBody(phase.session);
|
|
2809
3410
|
actions2 = liveActions(readInChatButton(false));
|
|
3411
|
+
if (followUp === null) {
|
|
3412
|
+
status = phase.name === "live" ? speakingLine(phase.session) : S.connecting;
|
|
3413
|
+
break;
|
|
3414
|
+
}
|
|
3415
|
+
status = followUp.speaking ? speakingLine(phase.session) : followUp.pending !== null ? (followUp.pending === "asking" ? activity : null) ?? S.gettingAnswer : followUp.unread !== null ? S[FOLLOW_UP_TEXT[followUp.unread]] : followUp.closed ? S.followUpLast : S.followUpIdle;
|
|
3416
|
+
if (!followUp.speaking && followUp.pending === null && followUp.unread !== null && !followUp.closed) {
|
|
3417
|
+
body2 = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3418
|
+
body2,
|
|
3419
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3420
|
+
"p",
|
|
3421
|
+
{
|
|
3422
|
+
"data-boff-explore": "avatar-live-idle",
|
|
3423
|
+
className: "text-xs text-muted-foreground",
|
|
3424
|
+
children: S.followUpIdle
|
|
3425
|
+
}
|
|
3426
|
+
)
|
|
3427
|
+
] });
|
|
3428
|
+
}
|
|
3429
|
+
if (followUpAsks) box = "spoken";
|
|
2810
3430
|
break;
|
|
3431
|
+
}
|
|
2811
3432
|
case "ended":
|
|
2812
3433
|
status = S[phase.how];
|
|
2813
3434
|
outcome2 = true;
|
|
@@ -2855,7 +3476,7 @@ function AvatarLiveView({
|
|
|
2855
3476
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: S.newChatNote })
|
|
2856
3477
|
] }) : null,
|
|
2857
3478
|
chips,
|
|
2858
|
-
box !== null && usedToday ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3479
|
+
box !== null && usedToday && phase.name !== "live" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2859
3480
|
"p",
|
|
2860
3481
|
{
|
|
2861
3482
|
"data-boff-explore": "avatar-live-used-today",
|