@appilots/web-sdk 0.2.0 → 0.3.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/README.md CHANGED
@@ -115,6 +115,25 @@ messages when reading older replies.
115
115
  they never send automatically. Markdown supports headings, lists, bold, italic,
116
116
  inline/fenced code and HTTP(S) links. HTML is rendered as text.
117
117
 
118
+ ### Voice dictation (opt-in)
119
+
120
+ ```tsx
121
+ <AppilotsChatFab locale="pt-BR" speech="browser" />
122
+ ```
123
+
124
+ `speech="browser"` adds a mic button that uses the Web Speech API. The button
125
+ is hidden where the browser has no speech recognition (Firefox, many WebViews).
126
+ It is a toggle (`aria-pressed`), so it works with a keyboard or a switch device
127
+ and nobody has to hold it down. The recognised text fills the composer as the
128
+ user speaks, can be edited, and is never sent automatically. Escape discards the
129
+ dictation. A status region announces when listening starts and stops.
130
+
131
+ **In Chrome and Edge the audio is sent to the browser vendor's speech service**,
132
+ so it leaves the device. To use an engine you control, pass your own `SpeechHost`
133
+ instead (`speech={host}`). See the
134
+ [Voice Input guide](https://docs.appilots.com/docs/guides/voice-input) for the
135
+ contract and the LGPD notes.
136
+
118
137
  “Talk to a person” uses the existing escalation API: waiting/connected status,
119
138
  operator replies, and user messages all stay in the same transcript. The API
120
139
  and an operator in the dashboard must be available for a person to answer.
@@ -4511,18 +4511,27 @@ var WRITE_SUCCESS = {
4511
4511
  "a\\s+[e\xE9]t[e\xE9]\\s+(?:enregistr|cr[e\xE9]|ajout|sauvegard)[e\xE9]e?"
4512
4512
  ]
4513
4513
  };
4514
+ var SENTENCE_START = "(?:^|[.!?:;*\\n]\\s*)";
4514
4515
  var COMPLETION_CLAIM = {
4515
4516
  pt: [
4516
4517
  "j[a\xE1]\\s+(?:cadastrei|registrei|salvei|criei)",
4517
4518
  "conclu[i\xED]\\s+o\\s+cadastro",
4518
- "cadastro\\s+(?:foi\\s+)?(?:conclu[i\xED]do|realizado|feito)"
4519
+ "cadastro\\s+(?:foi\\s+)?(?:conclu[i\xED]do|realizado|feito)",
4520
+ `${SENTENCE_START}(?:eu\\s+)?(?:cadastrei|registrei|salvei|criei|adicionei|enviei)\\b`
4519
4521
  ],
4520
4522
  en: [
4521
4523
  "i(?:'|\u2019)?ve\\s+(?:registered|saved|created|added)",
4522
- "i\\s+have\\s+(?:registered|saved|created|added)"
4524
+ "i\\s+have\\s+(?:registered|saved|created|added)",
4525
+ `${SENTENCE_START}i\\s+(?:registered|saved|created|added|submitted)\\b`
4523
4526
  ],
4524
- es: ["ya\\s+(?:lo\\s+|la\\s+)?(?:registr[e\xE9]|guard[e\xE9]|cre[e\xE9])"],
4525
- fr: ["j(?:e\\s+l(?:'|\u2019)ai|(?:'|\u2019)ai)\\s+(?:enregistr|cr[e\xE9]|ajout|sauvegard)[e\xE9]"]
4527
+ es: [
4528
+ "ya\\s+(?:lo\\s+|la\\s+)?(?:registr[e\xE9]|guard[e\xE9]|cre[e\xE9])",
4529
+ `${SENTENCE_START}(?:ya\\s+)?he\\s+(?:registrado|guardado|creado|enviado|a[\xF1n]adido)\\b`
4530
+ ],
4531
+ fr: [
4532
+ "j(?:e\\s+l(?:'|\u2019)ai|(?:'|\u2019)ai)\\s+(?:enregistr|cr[e\xE9]|ajout|sauvegard)[e\xE9]",
4533
+ `${SENTENCE_START}c(?:'|\u2019)est\\s+(?:enregistr|cr[e\xE9]|ajout|sauvegard)[e\xE9]`
4534
+ ]
4526
4535
  };
4527
4536
  var SUBMIT_CONTROL = {
4528
4537
  pt: ["cadastrar", "registrar", "salvar", "enviar", "confirmar"],
@@ -4640,6 +4649,7 @@ var AppilotsClient = class {
4640
4649
  mcpVersion;
4641
4650
  introspectionReporter;
4642
4651
  user;
4652
+ plainLanguage;
4643
4653
  sessionId = null;
4644
4654
  mission = null;
4645
4655
  missionDispatchPaused = false;
@@ -4658,6 +4668,7 @@ var AppilotsClient = class {
4658
4668
  this.mcpVersion = options.mcpVersion;
4659
4669
  this.introspectionReporter = options.introspectionReporter;
4660
4670
  this.user = options.user;
4671
+ this.plainLanguage = options.plainLanguage;
4661
4672
  this.headers = {
4662
4673
  "Content-Type": "application/json",
4663
4674
  // Version telemetry: lets the server correlate behavior per SDK
@@ -4807,6 +4818,19 @@ var AppilotsClient = class {
4807
4818
  return {};
4808
4819
  }
4809
4820
  }
4821
+ /**
4822
+ * Per-session plain-language override — e.g. from the signed-in user's
4823
+ * accessibility profile. `undefined` hands the decision back to the
4824
+ * project setting. Applies from the next request on.
4825
+ */
4826
+ setPlainLanguage(value) {
4827
+ this.plainLanguage = value;
4828
+ }
4829
+ /** Every relay-bound context carries the reply-style preference. */
4830
+ withPreferences(context) {
4831
+ if (this.plainLanguage === void 0) return context;
4832
+ return { ...context ?? {}, plainLanguage: this.plainLanguage };
4833
+ }
4810
4834
  // ── Messages ──────────────────────────────────────────────────
4811
4835
  async sendMessage(content, context) {
4812
4836
  if (this.sessionStorage && !this.sessionId) await this.loadStoredSession();
@@ -4820,7 +4844,7 @@ var AppilotsClient = class {
4820
4844
  body: JSON.stringify({
4821
4845
  content,
4822
4846
  sessionId: this.sessionId,
4823
- context,
4847
+ context: this.withPreferences(context),
4824
4848
  // Optional MCP version bundled with this app build. Server
4825
4849
  // compares vs the active MCP doc to detect stale uploads.
4826
4850
  ...this.mcpVersion ? { mcpVersion: this.mcpVersion } : {},
@@ -5085,7 +5109,7 @@ var AppilotsClient = class {
5085
5109
  JSON.stringify({
5086
5110
  content,
5087
5111
  sessionId: this.sessionId,
5088
- context,
5112
+ context: this.withPreferences(context),
5089
5113
  ...this.mcpVersion ? { mcpVersion: this.mcpVersion } : {},
5090
5114
  ...this.introspectionFragment()
5091
5115
  })
@@ -5104,7 +5128,9 @@ var AppilotsClient = class {
5104
5128
  body: JSON.stringify({
5105
5129
  sessionId: this.sessionId,
5106
5130
  results,
5107
- context: this.mission ? { ...context, missionProtocol: 1, missionId: this.mission.scope.id } : context,
5131
+ context: this.withPreferences(
5132
+ this.mission ? { ...context, missionProtocol: 1, missionId: this.mission.scope.id } : context
5133
+ ),
5108
5134
  hop
5109
5135
  })
5110
5136
  },
@@ -5192,7 +5218,7 @@ var AppilotsClient = class {
5192
5218
  body: JSON.stringify({
5193
5219
  command,
5194
5220
  missionId: this.mission.scope.id,
5195
- context
5221
+ context: this.withPreferences(context)
5196
5222
  })
5197
5223
  });
5198
5224
  if (command === "resume") this.missionDispatchPaused = false;
@@ -5579,6 +5605,11 @@ var AppilotsClient = class {
5579
5605
  };
5580
5606
  }
5581
5607
  };
5608
+ function isSpeechHost(value) {
5609
+ if (!value || typeof value !== "object") return false;
5610
+ const candidate = value;
5611
+ return typeof candidate.start === "function" && typeof candidate.stop === "function" && typeof candidate.cancel === "function";
5612
+ }
5582
5613
  var DESTRUCTIVE_LABEL_RE = /\b(cancelar?\w*|cancel(?:ar|led|ation|amento)?|delete\w*|exclu\w*|delet\w*|remov\w*|remove|apag\w*|destroy\w*|unsubscribe\w*)\b/i;
5583
5614
  function looksDestructiveActionLabel(value) {
5584
5615
  if (typeof value !== "string" || !value.trim()) return false;
@@ -6720,6 +6751,10 @@ function actionHasCompletedConfirmGate(action, actions) {
6720
6751
  return payload.destructive === true || payload.severity === "danger";
6721
6752
  });
6722
6753
  }
6754
+ function confirmExpiresAt(confirm) {
6755
+ const scope = readPayload(confirm).missionApproval;
6756
+ return typeof scope?.expiresAt === "number" ? scope.expiresAt : void 0;
6757
+ }
6723
6758
  var ActionQueueMachine = class {
6724
6759
  deps;
6725
6760
  options;
@@ -7225,6 +7260,20 @@ var ActionQueueMachine = class {
7225
7260
  return this.state.actions.filter((a) => a.status === "completed" || a.status === "failed");
7226
7261
  }
7227
7262
  };
7263
+ var STILL_WORKING_FIRST_MS = 1e4;
7264
+ var STILL_WORKING_EVERY_MS = 2e4;
7265
+ function startStillWorkingTimer(onTick) {
7266
+ let elapsedMs = STILL_WORKING_FIRST_MS;
7267
+ let timer = setTimeout(function tick() {
7268
+ onTick(Math.round(elapsedMs / 1e3));
7269
+ elapsedMs += STILL_WORKING_EVERY_MS;
7270
+ timer = setTimeout(tick, STILL_WORKING_EVERY_MS);
7271
+ }, STILL_WORKING_FIRST_MS);
7272
+ return () => clearTimeout(timer);
7273
+ }
7274
+ function normalize3(s) {
7275
+ return s.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]/g, "");
7276
+ }
7228
7277
  function matchScore(needle, hay) {
7229
7278
  if (!needle || !hay) return 0;
7230
7279
  if (needle === hay) return 100;
@@ -7925,7 +7974,7 @@ function parseInlineMarkdown(text) {
7925
7974
  }
7926
7975
 
7927
7976
  // src/version.ts
7928
- var SDK_VERSION2 = "0.2.0";
7977
+ var SDK_VERSION2 = "0.3.0";
7929
7978
 
7930
7979
  // src/introspection/locators.ts
7931
7980
  var APPILOTS_ID_ATTR = "data-appilots-id";
@@ -9320,7 +9369,9 @@ function stripComponentPrefix(componentId) {
9320
9369
  /^(?:button|btn|switch|toggle|slider|input|field|link)[-_](.+)$/i
9321
9370
  );
9322
9371
  if (kebab?.[1]) return kebab[1];
9323
- const camel = componentId.match(/^(?:button|btn|switch|toggle|slider|input|field|link)([A-Z].*)$/);
9372
+ const camel = componentId.match(
9373
+ /^(?:button|btn|switch|toggle|slider|input|field|link)([A-Z].*)$/
9374
+ );
9324
9375
  if (camel?.[1]) return camel[1].charAt(0).toLowerCase() + camel[1].slice(1);
9325
9376
  return null;
9326
9377
  }
@@ -9348,7 +9399,8 @@ function matchesKind(el, kind) {
9348
9399
  return !["checkbox", "radio", "button", "submit", "reset", "image"].includes(type);
9349
9400
  }
9350
9401
  if (kind === "toggle") {
9351
- if (tag === "input" && (el.getAttribute("type") ?? "").toLowerCase() === "checkbox") return true;
9402
+ if (tag === "input" && (el.getAttribute("type") ?? "").toLowerCase() === "checkbox")
9403
+ return true;
9352
9404
  const role2 = (el.getAttribute("role") ?? "").toLowerCase();
9353
9405
  return role2 === "switch" || role2 === "checkbox";
9354
9406
  }
@@ -9367,7 +9419,9 @@ function findListItemElements(listIndex, options = {}) {
9367
9419
  const root = rootOf(options);
9368
9420
  if (typeof root.querySelectorAll !== "function") return void 0;
9369
9421
  const containers = Array.from(
9370
- root.querySelectorAll(`[${APPILOTS_LIST_ID_ATTR}], ul, ol, table, [role="list"], [role="listbox"], [role="grid"]`)
9422
+ root.querySelectorAll(
9423
+ `[${APPILOTS_LIST_ID_ATTR}], ul, ol, table, [role="list"], [role="listbox"], [role="grid"]`
9424
+ )
9371
9425
  ).filter((el) => !hidesSubtree(el));
9372
9426
  const container = containers[listIndex];
9373
9427
  if (!container) return void 0;
@@ -9462,6 +9516,14 @@ function resolveTargetElement(rawId, options = {}) {
9462
9516
  return { element: el, via: "exact-id" };
9463
9517
  }
9464
9518
  }
9519
+ const folded = normalize3(id);
9520
+ if (folded) {
9521
+ for (const el of candidates) {
9522
+ if (idCandidatesFor(el).some((candidate) => normalize3(candidate) === folded)) {
9523
+ return { element: el, via: "exact-id" };
9524
+ }
9525
+ }
9526
+ }
9465
9527
  const stripped = stripComponentPrefix(id);
9466
9528
  if (stripped) {
9467
9529
  const strippedLower = stripped.toLowerCase();
@@ -9476,9 +9538,10 @@ function resolveTargetElement(rawId, options = {}) {
9476
9538
  let best;
9477
9539
  let tied = false;
9478
9540
  for (const el of candidates) {
9541
+ if (isSensitiveField(el, bestId(el), accessibleName(el))) continue;
9479
9542
  let score = 0;
9480
9543
  for (const candidate of idCandidatesFor(el)) {
9481
- score = Math.max(score, matchScore(id, candidate));
9544
+ score = Math.max(score, matchScore(folded, normalize3(candidate)));
9482
9545
  }
9483
9546
  if (score < MIN_FUZZY_SCORE) continue;
9484
9547
  if (!best || score > best.score) {
@@ -9508,7 +9571,9 @@ function isOccludedByModal(el, options = {}) {
9508
9571
  const root = rootOf(options);
9509
9572
  if (typeof root.querySelectorAll !== "function") return false;
9510
9573
  const modals = Array.from(
9511
- root.querySelectorAll('[aria-modal="true"], [role="dialog"], [role="alertdialog"], dialog[open]')
9574
+ root.querySelectorAll(
9575
+ '[aria-modal="true"], [role="dialog"], [role="alertdialog"], dialog[open]'
9576
+ )
9512
9577
  ).filter((modal) => !hidesSubtree(modal) && isVisible(modal));
9513
9578
  if (modals.length === 0) return false;
9514
9579
  return !modals.some((modal) => modal === el || modal.contains(el));
@@ -9517,7 +9582,9 @@ function modalCandidateLabels(options = {}, limit = 8) {
9517
9582
  const root = rootOf(options);
9518
9583
  if (typeof root.querySelectorAll !== "function") return [];
9519
9584
  const modal = Array.from(
9520
- root.querySelectorAll('[aria-modal="true"], [role="dialog"], [role="alertdialog"], dialog[open]')
9585
+ root.querySelectorAll(
9586
+ '[aria-modal="true"], [role="dialog"], [role="alertdialog"], dialog[open]'
9587
+ )
9521
9588
  ).find((candidate) => !hidesSubtree(candidate));
9522
9589
  if (!modal) return [];
9523
9590
  const out = [];
@@ -9552,6 +9619,21 @@ function readVisibleValidation(context) {
9552
9619
  const snapshot = captureSnapshot({ root: rootElement(context) });
9553
9620
  return snapshot.texts.find((text) => FORM_VALIDATION_TEXT_RE2.test(text));
9554
9621
  }
9622
+ var SUBMIT_SETTLE_MAX_MS = 6e3;
9623
+ function readAlertTexts(context) {
9624
+ const root = rootElement(context) ?? (typeof document !== "undefined" ? document.body : void 0);
9625
+ if (!root) return [];
9626
+ return Array.from(root.querySelectorAll('[role="alert"], [aria-live="assertive"]')).filter((el) => !hidesSubtree(el)).map((el) => (el.textContent ?? "").trim()).filter(Boolean);
9627
+ }
9628
+ async function waitForSubmitSettle(submit, context) {
9629
+ const busy = () => submit.isConnected && (submit.disabled || submit.getAttribute("aria-disabled") === "true") || captureSnapshot({ root: rootElement(context) }).loading;
9630
+ const started = Date.now();
9631
+ while (busy()) {
9632
+ if (context.signal?.aborted || Date.now() - started >= SUBMIT_SETTLE_MAX_MS) return !busy();
9633
+ await sleep2(100);
9634
+ }
9635
+ return true;
9636
+ }
9555
9637
  function rootElement(context) {
9556
9638
  const root = context.root;
9557
9639
  if (!root) return void 0;
@@ -9757,6 +9839,18 @@ async function formFillHandler(payload, context, permissions) {
9757
9839
  setToggleValue(el, value2);
9758
9840
  continue;
9759
9841
  }
9842
+ if (el.tagName.toLowerCase() === "input" && el.type === "file") {
9843
+ failures.push({
9844
+ fieldId: field.fieldId,
9845
+ error: `Field "${field.fieldId}" is a file picker; only the user can attach a file`,
9846
+ diagnose: diagnose(context, "validation", {
9847
+ fieldId: field.fieldId,
9848
+ recoverable: false,
9849
+ requiresUserInput: true
9850
+ })
9851
+ });
9852
+ continue;
9853
+ }
9760
9854
  const value = field.value == null ? "" : String(field.value);
9761
9855
  const written = setControlValue(el, value);
9762
9856
  if (!written) {
@@ -9824,6 +9918,7 @@ async function formFillHandler(payload, context, permissions) {
9824
9918
  context
9825
9919
  );
9826
9920
  if (blocked) return { success: false, ...blocked };
9921
+ const alertsBefore = new Set(readAlertTexts(context));
9827
9922
  clickElement(submit);
9828
9923
  await sleep2(120);
9829
9924
  if (context.signal?.aborted) return { success: false, error: "Session ended" };
@@ -9836,6 +9931,30 @@ async function formFillHandler(payload, context, permissions) {
9836
9931
  effect: "partial"
9837
9932
  };
9838
9933
  }
9934
+ const settled = await waitForSubmitSettle(submit, context);
9935
+ if (context.signal?.aborted) return { success: false, error: "Session ended" };
9936
+ if (!settled) {
9937
+ return {
9938
+ success: false,
9939
+ error: `Submit still in progress after ${SUBMIT_SETTLE_MAX_MS / 1e3}s \u2014 outcome unknown, do not report it as saved`,
9940
+ diagnose: diagnose(context, "screen-timeout", { recoverable: false }),
9941
+ effect: "unknown"
9942
+ };
9943
+ }
9944
+ const lateValidation = readVisibleValidation(context);
9945
+ const rejection = lateValidation ?? readAlertTexts(context).find((text) => !alertsBefore.has(text));
9946
+ if (rejection) {
9947
+ return {
9948
+ success: false,
9949
+ error: `Submit was rejected: ${rejection}`,
9950
+ diagnose: lateValidation ? diagnose(context, "validation", { visibleMessage: rejection, recoverable: true }) : (
9951
+ // Server-side failure (5xx, expired session, timeout): retrying
9952
+ // blindly can double-submit or loop, so hand it to the user.
9953
+ diagnose(context, "unknown", { visibleMessage: rejection, recoverable: false })
9954
+ ),
9955
+ effect: "partial"
9956
+ };
9957
+ }
9839
9958
  return { success: true, effect: "completed" };
9840
9959
  }
9841
9960
  return { success: true, effect: "changed" };
@@ -10154,6 +10273,14 @@ async function withSuppressedConfirm(context, fn) {
10154
10273
  }
10155
10274
 
10156
10275
  // src/runtime/AppilotsWebRuntime.ts
10276
+ function browserLocale() {
10277
+ if (typeof navigator === "undefined") return void 0;
10278
+ for (const language of navigator.languages ?? [navigator.language]) {
10279
+ const locale = normalizeAppilotsLocale(language);
10280
+ if (locale) return locale;
10281
+ }
10282
+ return void 0;
10283
+ }
10157
10284
  var POST_ACTION_LOADING_MAX_MS = 6e3;
10158
10285
  var AppilotsWebRuntime = class {
10159
10286
  client;
@@ -10179,13 +10306,15 @@ var AppilotsWebRuntime = class {
10179
10306
  apiKey: options.apiKey,
10180
10307
  apiBaseUrl: options.apiBaseUrl,
10181
10308
  debug: options.debug,
10309
+ timeout: options.timeout,
10182
10310
  appVersion: options.appVersion,
10183
10311
  mcpVersion: options.mcpVersion,
10184
10312
  // This package's own version, so the server can tell a web
10185
10313
  // client apart from an RN one by more than the platform field
10186
10314
  // (issue #312).
10187
10315
  sdkVersion: SDK_VERSION2,
10188
- user: options.user
10316
+ user: options.user,
10317
+ plainLanguage: options.plainLanguage
10189
10318
  });
10190
10319
  this.navigation = new WebNavigationAdapter({
10191
10320
  routes: options.routes,
@@ -10251,6 +10380,8 @@ var AppilotsWebRuntime = class {
10251
10380
  buildContext(extras = {}) {
10252
10381
  if (extras.newMission === true) this.deltaTracker.reset();
10253
10382
  const context = { platform: "web" };
10383
+ const deviceLocale = browserLocale();
10384
+ if (deviceLocale) context.deviceLocale = deviceLocale;
10254
10385
  const screen = this.navigation.getCurrentScreen();
10255
10386
  if (screen) context.currentScreen = screen;
10256
10387
  const navigationState = this.navigation.getNavigationState();
@@ -10381,6 +10512,108 @@ var AppilotsWebRuntime = class {
10381
10512
  }
10382
10513
  };
10383
10514
 
10515
+ // src/speech/webSpeechHost.ts
10516
+ var MAX_SILENT_RESTARTS = 5;
10517
+ function recognitionCtor() {
10518
+ const g = globalThis;
10519
+ return g.SpeechRecognition ?? g.webkitSpeechRecognition ?? null;
10520
+ }
10521
+ function isWebSpeechSupported() {
10522
+ return recognitionCtor() !== null;
10523
+ }
10524
+ function createWebSpeechHost() {
10525
+ const Ctor = recognitionCtor();
10526
+ if (!Ctor) return null;
10527
+ let live = null;
10528
+ let committed = "";
10529
+ let current = "";
10530
+ let silentRestarts = 0;
10531
+ let pendingStop = null;
10532
+ const partialListeners = /* @__PURE__ */ new Set();
10533
+ const endListeners = /* @__PURE__ */ new Set();
10534
+ const text = () => `${committed} ${current}`.replace(/\s+/g, " ").trim();
10535
+ const ended = (error) => endListeners.forEach((l) => l(error));
10536
+ const open = (locale) => new Promise((resolve, reject) => {
10537
+ const r = new Ctor();
10538
+ live = r;
10539
+ r.lang = locale;
10540
+ r.continuous = true;
10541
+ r.interimResults = true;
10542
+ let started = false;
10543
+ let failure;
10544
+ r.onstart = () => {
10545
+ started = true;
10546
+ resolve();
10547
+ };
10548
+ r.onresult = ({ results }) => {
10549
+ if (live !== r) return;
10550
+ let next = "";
10551
+ for (let i = 0; i < results.length; i++) next += results[i]?.[0]?.transcript ?? "";
10552
+ current = next;
10553
+ silentRestarts = 0;
10554
+ const t = text();
10555
+ partialListeners.forEach((l) => l(t));
10556
+ };
10557
+ r.onerror = ({ error }) => {
10558
+ if (error !== "no-speech") failure = error;
10559
+ };
10560
+ r.onend = () => {
10561
+ if (live !== r) return;
10562
+ if (pendingStop) {
10563
+ const done = pendingStop;
10564
+ pendingStop = null;
10565
+ live = null;
10566
+ done(text());
10567
+ return;
10568
+ }
10569
+ live = null;
10570
+ if (!started) {
10571
+ reject(new Error(failure ?? "speech recognition did not start"));
10572
+ return;
10573
+ }
10574
+ if (failure) return ended(new Error(failure));
10575
+ if (silentRestarts >= MAX_SILENT_RESTARTS) return ended();
10576
+ silentRestarts++;
10577
+ committed = text();
10578
+ current = "";
10579
+ open(locale).catch(ended);
10580
+ };
10581
+ r.start();
10582
+ });
10583
+ return {
10584
+ start({ locale }) {
10585
+ live?.abort();
10586
+ committed = "";
10587
+ current = "";
10588
+ silentRestarts = 0;
10589
+ pendingStop = null;
10590
+ return open(locale);
10591
+ },
10592
+ stop() {
10593
+ const r = live;
10594
+ if (!r) return Promise.resolve(text());
10595
+ return new Promise((resolve) => {
10596
+ pendingStop = resolve;
10597
+ r.stop();
10598
+ });
10599
+ },
10600
+ async cancel() {
10601
+ const r = live;
10602
+ live = null;
10603
+ pendingStop = null;
10604
+ r?.abort();
10605
+ },
10606
+ onPartial(listener) {
10607
+ partialListeners.add(listener);
10608
+ return () => partialListeners.delete(listener);
10609
+ },
10610
+ onEnd(listener) {
10611
+ endListeners.add(listener);
10612
+ return () => endListeners.delete(listener);
10613
+ }
10614
+ };
10615
+ }
10616
+
10384
10617
  exports.APPILOTS_CHAT_ATTR = APPILOTS_CHAT_ATTR;
10385
10618
  exports.APPILOTS_ID_ATTR = APPILOTS_ID_ATTR;
10386
10619
  exports.APPILOTS_ITEM_COUNT_ATTR = APPILOTS_ITEM_COUNT_ATTR;
@@ -10398,11 +10631,14 @@ exports.RateLimitedError = RateLimitedError;
10398
10631
  exports.SDK_VERSION = SDK_VERSION2;
10399
10632
  exports.WebNavigationAdapter = WebNavigationAdapter;
10400
10633
  exports.accessibleName = accessibleName;
10634
+ exports.appilotsDebugWarn = appilotsDebugWarn;
10401
10635
  exports.bestId = bestId;
10402
10636
  exports.buildPath = buildPath;
10403
10637
  exports.candidateIds = candidateIds;
10404
10638
  exports.captureSnapshot = captureSnapshot;
10405
10639
  exports.clickElement = clickElement;
10640
+ exports.confirmExpiresAt = confirmExpiresAt;
10641
+ exports.createWebSpeechHost = createWebSpeechHost;
10406
10642
  exports.describeAction = describeAction;
10407
10643
  exports.elementRole = elementRole;
10408
10644
  exports.executeAction = executeAction;
@@ -10413,7 +10649,9 @@ exports.getRegisteredElement = getRegisteredElement;
10413
10649
  exports.hidesSubtree = hidesSubtree;
10414
10650
  exports.humanizeError = humanizeError;
10415
10651
  exports.isOccludedByModal = isOccludedByModal;
10652
+ exports.isSpeechHost = isSpeechHost;
10416
10653
  exports.isVisible = isVisible;
10654
+ exports.isWebSpeechSupported = isWebSpeechSupported;
10417
10655
  exports.longPressElement = longPressElement;
10418
10656
  exports.matchRoute = matchRoute;
10419
10657
  exports.modalCandidateLabels = modalCandidateLabels;
@@ -10436,6 +10674,7 @@ exports.scrollIntoView = scrollIntoView;
10436
10674
  exports.scrollToOffset = scrollToOffset;
10437
10675
  exports.setControlValue = setControlValue;
10438
10676
  exports.setToggleValue = setToggleValue;
10677
+ exports.startStillWorkingTimer = startStillWorkingTimer;
10439
10678
  exports.stripComponentPrefix = stripComponentPrefix;
10440
10679
  exports.waitForLoadingSettle = waitForLoadingSettle;
10441
10680
  exports.waitForScreenSettle = waitForScreenSettle;