@oxvo/ai-live-assist 7.3.0 → 7.3.4

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.
@@ -5,7 +5,7 @@ export declare const confirmationReplyIntent: (value: string) => ConfirmationRep
5
5
  export default class AiLiveAssist {
6
6
  private readonly app;
7
7
  private readonly options;
8
- readonly version = "7.3.0";
8
+ readonly version = "7.3.4";
9
9
  private readonly siteKey;
10
10
  private tabId;
11
11
  private readonly windowId;
@@ -1671,7 +1671,7 @@ class AiLiveAssist {
1671
1671
  ? 'Microphone access was not granted. Allow microphone access to use voice assist.'
1672
1672
  : 'Microphone access was not granted. You can continue with text.';
1673
1673
  }
1674
- return 'AI Live Assist is temporarily unavailable.';
1674
+ return 'AI Guide is temporarily unavailable.';
1675
1675
  }
1676
1676
  retryable(error) {
1677
1677
  return error instanceof client_js_1.RuntimeClientError ? error.retryable : false;
package/cjs/client.js CHANGED
@@ -20,7 +20,7 @@ class RuntimeClient {
20
20
  this.clientVersion = clientVersion;
21
21
  const url = new URL(runtimeUrl, window.location.href);
22
22
  if (!['http:', 'https:'].includes(url.protocol)) {
23
- throw new Error('AI Live Assist runtime URL must use HTTP or HTTPS.');
23
+ throw new Error('AI Guide runtime URL must use HTTP or HTTPS.');
24
24
  }
25
25
  this.baseUrl = url.origin + url.pathname.replace(/\/$/, '');
26
26
  }
@@ -187,7 +187,7 @@ class RuntimeClient {
187
187
  }
188
188
  throw new RuntimeClientError(typeof error.code === 'string' ? error.code : 'RUNTIME_UNAVAILABLE', typeof error.message === 'string'
189
189
  ? error.message
190
- : 'AI Live Assist is temporarily unavailable.', error.retryable === true, typeof error.retryAfterMs === 'number' ? error.retryAfterMs : null, response.status);
190
+ : 'AI Guide is temporarily unavailable.', error.retryable === true, typeof error.retryAfterMs === 'number' ? error.retryAfterMs : null, response.status);
191
191
  }
192
192
  }
193
193
  exports.RuntimeClient = RuntimeClient;
package/cjs/context.d.ts CHANGED
@@ -17,6 +17,7 @@ export declare class PageContextCollector {
17
17
  private running;
18
18
  private mutationObserver;
19
19
  private captureTimer;
20
+ private mutationCaptureTimer;
20
21
  private targetIds;
21
22
  private textIds;
22
23
  private optionIds;
@@ -28,6 +29,7 @@ export declare class PageContextCollector {
28
29
  private pendingCapture;
29
30
  private deferredNavigation;
30
31
  private recentSelection;
32
+ private latestSemanticSignature;
31
33
  constructor(callbacks: ContextCallbacks, selectorRegionRules?: SelectorRegionRule[], policyVersion?: number);
32
34
  get currentRevision(): number;
33
35
  get currentPageId(): string;
@@ -36,6 +38,7 @@ export declare class PageContextCollector {
36
38
  stop(): void;
37
39
  captureAndPublish(): Promise<PageContextSnapshot>;
38
40
  capture(): PageContextSnapshot;
41
+ private captureSnapshot;
39
42
  captureVisualContext(): VisualContextCapture | null;
40
43
  execute(grant: ActionGrant): Promise<ActionExecutionResult>;
41
44
  completeActionExecution(actionId: string): Promise<string | null>;
@@ -47,6 +50,11 @@ export declare class PageContextCollector {
47
50
  private actionApplied;
48
51
  private clippedGeometry;
49
52
  private scheduleCapture;
53
+ private scheduleMutationCapture;
54
+ private scheduleViewportCapture;
55
+ private handleMutations;
56
+ private captureAndPublishIfChanged;
57
+ private semanticSignature;
50
58
  private handleSelectionChange;
51
59
  private handleControlStateChange;
52
60
  private readSafeSelection;
package/cjs/context.js CHANGED
@@ -8,6 +8,8 @@ const MAX_TOTAL_TEXT = 24000;
8
8
  const MAX_OPTIONS = 100;
9
9
  const MAX_SELECTED_TEXT = 2000;
10
10
  const SELECTION_RETENTION_MS = 120000;
11
+ const CAPTURE_SETTLE_MS = 250;
12
+ const MUTATION_CAPTURE_DEBOUNCE_MS = 350;
11
13
  const PRIVATE_SELECTOR = [
12
14
  '[data-oxvo-ai-live-assist-root]',
13
15
  '[data-oxvo-private]',
@@ -66,6 +68,25 @@ const isCovered = (element) => {
66
68
  return Boolean(top && top !== element && !element.contains(top) && !top.contains(element));
67
69
  };
68
70
  const safePageUrl = () => `${window.location.origin}${window.location.pathname}`;
71
+ const safeDestinationPath = (element) => {
72
+ if (!(element instanceof HTMLAnchorElement))
73
+ return null;
74
+ try {
75
+ const destination = new URL(element.href, window.location.href);
76
+ if (destination.origin !== window.location.origin ||
77
+ destination.search ||
78
+ destination.hash ||
79
+ !destination.pathname.startsWith('/') ||
80
+ destination.pathname.startsWith('//') ||
81
+ destination.pathname.length > 1024) {
82
+ return null;
83
+ }
84
+ return destination.pathname;
85
+ }
86
+ catch {
87
+ return null;
88
+ }
89
+ };
69
90
  const sensitivityFor = (element, name) => {
70
91
  const input = element instanceof HTMLInputElement ? element : null;
71
92
  const type = input?.type.toLowerCase() ?? '';
@@ -235,6 +256,7 @@ class PageContextCollector {
235
256
  this.running = false;
236
257
  this.mutationObserver = null;
237
258
  this.captureTimer = null;
259
+ this.mutationCaptureTimer = null;
238
260
  this.targetIds = new WeakMap();
239
261
  this.textIds = new WeakMap();
240
262
  this.optionIds = new WeakMap();
@@ -246,6 +268,7 @@ class PageContextCollector {
246
268
  this.pendingCapture = false;
247
269
  this.deferredNavigation = null;
248
270
  this.recentSelection = null;
271
+ this.latestSemanticSignature = null;
249
272
  this.scheduleCapture = () => {
250
273
  if (!this.running)
251
274
  return;
@@ -253,20 +276,53 @@ class PageContextCollector {
253
276
  this.pendingCapture = true;
254
277
  return;
255
278
  }
279
+ if (this.mutationCaptureTimer)
280
+ clearTimeout(this.mutationCaptureTimer);
281
+ this.mutationCaptureTimer = null;
256
282
  if (this.captureTimer)
257
283
  return;
258
284
  this.captureTimer = setTimeout(() => {
259
285
  this.captureTimer = null;
260
- void this.captureAndPublish();
261
- }, 250);
286
+ void this.captureAndPublishIfChanged();
287
+ }, CAPTURE_SETTLE_MS);
288
+ };
289
+ this.scheduleMutationCapture = () => {
290
+ if (!this.running)
291
+ return;
292
+ if (this.executingActionId) {
293
+ this.pendingCapture = true;
294
+ return;
295
+ }
296
+ if (this.captureTimer)
297
+ return;
298
+ if (this.mutationCaptureTimer)
299
+ clearTimeout(this.mutationCaptureTimer);
300
+ this.mutationCaptureTimer = setTimeout(() => {
301
+ this.mutationCaptureTimer = null;
302
+ void this.captureAndPublishIfChanged();
303
+ }, MUTATION_CAPTURE_DEBOUNCE_MS);
304
+ };
305
+ this.scheduleViewportCapture = () => {
306
+ this.scheduleMutationCapture();
307
+ };
308
+ this.handleMutations = (records) => {
309
+ const hasSemanticMutation = records.some((record) => record.type !== 'attributes' ||
310
+ (record.attributeName !== 'class' && record.attributeName !== 'style'));
311
+ if (hasSemanticMutation)
312
+ this.scheduleCapture();
313
+ else
314
+ this.scheduleMutationCapture();
262
315
  };
263
316
  this.handleSelectionChange = () => {
264
317
  const selection = this.readSafeSelection();
265
318
  if (selection)
266
319
  this.recentSelection = selection;
267
- this.scheduleCapture();
320
+ this.scheduleMutationCapture();
268
321
  };
269
- this.handleControlStateChange = () => {
322
+ this.handleControlStateChange = (event) => {
323
+ if (event.target instanceof Element && event.target.closest('[data-oxvo-ai-live-assist-root]')) {
324
+ return;
325
+ }
270
326
  this.scheduleCapture();
271
327
  };
272
328
  this.handleNavigation = () => {
@@ -321,7 +377,7 @@ class PageContextCollector {
321
377
  return;
322
378
  this.running = true;
323
379
  this.installNavigationHooks();
324
- this.mutationObserver = new MutationObserver(() => this.scheduleCapture());
380
+ this.mutationObserver = new MutationObserver(this.handleMutations);
325
381
  if (document.body) {
326
382
  this.mutationObserver.observe(document.body, {
327
383
  subtree: true,
@@ -335,6 +391,7 @@ class PageContextCollector {
335
391
  'aria-live',
336
392
  'class',
337
393
  'disabled',
394
+ 'href',
338
395
  'hidden',
339
396
  'inert',
340
397
  'role',
@@ -342,19 +399,20 @@ class PageContextCollector {
342
399
  ],
343
400
  });
344
401
  }
345
- window.addEventListener('resize', this.scheduleCapture, { passive: true });
346
- document.addEventListener('scroll', this.scheduleCapture, {
402
+ window.addEventListener('resize', this.scheduleViewportCapture, { passive: true });
403
+ document.addEventListener('scroll', this.scheduleViewportCapture, {
347
404
  capture: true,
348
405
  passive: true,
349
406
  });
350
- window.visualViewport?.addEventListener('resize', this.scheduleCapture, {
407
+ window.visualViewport?.addEventListener('resize', this.scheduleViewportCapture, {
351
408
  passive: true,
352
409
  });
353
- window.visualViewport?.addEventListener('scroll', this.scheduleCapture, {
410
+ window.visualViewport?.addEventListener('scroll', this.scheduleViewportCapture, {
354
411
  passive: true,
355
412
  });
356
413
  window.addEventListener('popstate', this.handleNavigation);
357
414
  document.addEventListener('selectionchange', this.handleSelectionChange);
415
+ document.addEventListener('click', this.handleControlStateChange, true);
358
416
  document.addEventListener('input', this.handleControlStateChange, true);
359
417
  document.addEventListener('change', this.handleControlStateChange, true);
360
418
  void this.captureAndPublish();
@@ -366,12 +424,16 @@ class PageContextCollector {
366
424
  if (this.captureTimer)
367
425
  clearTimeout(this.captureTimer);
368
426
  this.captureTimer = null;
369
- window.removeEventListener('resize', this.scheduleCapture);
370
- document.removeEventListener('scroll', this.scheduleCapture, true);
371
- window.visualViewport?.removeEventListener('resize', this.scheduleCapture);
372
- window.visualViewport?.removeEventListener('scroll', this.scheduleCapture);
427
+ if (this.mutationCaptureTimer)
428
+ clearTimeout(this.mutationCaptureTimer);
429
+ this.mutationCaptureTimer = null;
430
+ window.removeEventListener('resize', this.scheduleViewportCapture);
431
+ document.removeEventListener('scroll', this.scheduleViewportCapture, true);
432
+ window.visualViewport?.removeEventListener('resize', this.scheduleViewportCapture);
433
+ window.visualViewport?.removeEventListener('scroll', this.scheduleViewportCapture);
373
434
  window.removeEventListener('popstate', this.handleNavigation);
374
435
  document.removeEventListener('selectionchange', this.handleSelectionChange);
436
+ document.removeEventListener('click', this.handleControlStateChange, true);
375
437
  document.removeEventListener('input', this.handleControlStateChange, true);
376
438
  document.removeEventListener('change', this.handleControlStateChange, true);
377
439
  this.restoreNavigationHooks();
@@ -381,6 +443,7 @@ class PageContextCollector {
381
443
  this.pendingCapture = false;
382
444
  this.deferredNavigation = null;
383
445
  this.recentSelection = null;
446
+ this.latestSemanticSignature = null;
384
447
  this.callbacks.onHighlight(null, 'focus');
385
448
  }
386
449
  async captureAndPublish() {
@@ -390,8 +453,10 @@ class PageContextCollector {
390
453
  return snapshot;
391
454
  }
392
455
  capture() {
393
- this.revision += 1;
394
- const revision = this.revision;
456
+ return this.captureSnapshot(true);
457
+ }
458
+ captureSnapshot(force) {
459
+ const revision = this.revision + 1;
395
460
  const nextTargets = new Map();
396
461
  const targetValues = [];
397
462
  const elements = document.querySelectorAll('a[href],button,input,select,textarea,summary,form,[contenteditable="true"],[role],[tabindex]');
@@ -444,6 +509,7 @@ class PageContextCollector {
444
509
  const role = roleFor(element);
445
510
  const sensitivity = sensitivityFor(element, name);
446
511
  const visible = isVisible(element);
512
+ const destinationPath = safeDestinationPath(element);
447
513
  const target = {
448
514
  targetId,
449
515
  revision,
@@ -456,6 +522,7 @@ class PageContextCollector {
456
522
  ? { autocomplete: normalize(element.getAttribute('autocomplete') ?? '', 128) }
457
523
  : {}),
458
524
  ...(semanticRegion ? { semanticRegion } : {}),
525
+ ...(destinationPath ? { destinationPath } : {}),
459
526
  sensitivity,
460
527
  protectedRegion: sensitivity !== 'none' || matchedRuleDigests.length > 0,
461
528
  frameOrigin: 'same_origin',
@@ -478,7 +545,6 @@ class PageContextCollector {
478
545
  targetValues.push(target);
479
546
  nextTargets.set(targetId, { element, revision, options: optionMap, target });
480
547
  }
481
- this.targets = nextTargets;
482
548
  const visibleText = [];
483
549
  let totalText = 0;
484
550
  const walker = document.createTreeWalker(document.body ?? document.documentElement, NodeFilter.SHOW_TEXT);
@@ -518,7 +584,7 @@ class PageContextCollector {
518
584
  Date.now() - Date.parse(this.recentSelection.capturedAt) > SELECTION_RETENTION_MS) {
519
585
  this.recentSelection = null;
520
586
  }
521
- return {
587
+ const snapshot = {
522
588
  schemaVersion: 1,
523
589
  pageId: this.pageId,
524
590
  revision,
@@ -537,6 +603,13 @@ class PageContextCollector {
537
603
  alerts,
538
604
  truncated,
539
605
  };
606
+ const signature = this.semanticSignature(snapshot);
607
+ if (!force && signature === this.latestSemanticSignature)
608
+ return null;
609
+ this.revision = revision;
610
+ this.targets = nextTargets;
611
+ this.latestSemanticSignature = signature;
612
+ return snapshot;
540
613
  }
541
614
  captureVisualContext() {
542
615
  if (this.revision < 1 || this.targets.size === 0)
@@ -659,7 +732,7 @@ class PageContextCollector {
659
732
  element.focus({ preventScroll: false });
660
733
  break;
661
734
  case 'click':
662
- this.click(element, grant.actionId, grant.desiredChecked);
735
+ this.click(element, grant.actionId, grant.desiredChecked, handle.target.destinationPath);
663
736
  break;
664
737
  case 'type':
665
738
  this.type(element, grant.value ?? '');
@@ -737,7 +810,7 @@ class PageContextCollector {
737
810
  ? deferred.toUrl
738
811
  : null;
739
812
  }
740
- click(element, actionId, desiredChecked) {
813
+ click(element, actionId, desiredChecked, expectedDestinationPath) {
741
814
  if (!(element instanceof HTMLElement))
742
815
  throw new Error('Target is not clickable.');
743
816
  if (desiredChecked !== undefined) {
@@ -752,6 +825,10 @@ class PageContextCollector {
752
825
  }
753
826
  if (element instanceof HTMLAnchorElement) {
754
827
  const target = new URL(element.href, window.location.href);
828
+ if (expectedDestinationPath !== undefined &&
829
+ safeDestinationPath(element) !== expectedDestinationPath) {
830
+ throw new Error('The navigation destination changed before execution.');
831
+ }
755
832
  if (target.origin !== window.location.origin) {
756
833
  throw new Error('External navigation is blocked.');
757
834
  }
@@ -895,6 +972,26 @@ class PageContextCollector {
895
972
  height,
896
973
  };
897
974
  }
975
+ async captureAndPublishIfChanged() {
976
+ const snapshot = this.captureSnapshot(false);
977
+ if (snapshot && this.running)
978
+ await this.callbacks.onSnapshot(snapshot);
979
+ }
980
+ semanticSignature(snapshot) {
981
+ return JSON.stringify({
982
+ ...snapshot,
983
+ revision: 0,
984
+ ...(snapshot.selection
985
+ ? {
986
+ selection: {
987
+ text: snapshot.selection.text,
988
+ source: snapshot.selection.source,
989
+ },
990
+ }
991
+ : {}),
992
+ targets: snapshot.targets.map((target) => ({ ...target, revision: 0 })),
993
+ });
994
+ }
898
995
  readSafeSelection() {
899
996
  const selection = window.getSelection();
900
997
  if (!selection || selection.isCollapsed || selection.rangeCount !== 1)
package/cjs/control.js CHANGED
@@ -31,7 +31,7 @@ class ControlChannel {
31
31
  }
32
32
  setFencingToken(value) {
33
33
  if (!Number.isSafeInteger(value) || value < 1) {
34
- throw new Error('Invalid AI Live Assist fencing token.');
34
+ throw new Error('Invalid AI Guide fencing token.');
35
35
  }
36
36
  this.fencingToken = value;
37
37
  }
@@ -70,7 +70,7 @@ class ControlChannel {
70
70
  socket.addEventListener('error', () => {
71
71
  signal.removeEventListener('abort', abort);
72
72
  if (socket.readyState !== WebSocket.OPEN) {
73
- reject(new Error('The AI Live Assist control channel could not connect.'));
73
+ reject(new Error('The AI Guide control channel could not connect.'));
74
74
  }
75
75
  }, { once: true });
76
76
  socket.addEventListener('close', () => {
@@ -82,7 +82,7 @@ class ControlChannel {
82
82
  }
83
83
  send(type, payload) {
84
84
  if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
85
- throw new Error('The AI Live Assist control channel is not connected.');
85
+ throw new Error('The AI Guide control channel is not connected.');
86
86
  }
87
87
  const id = `msg_${crypto.randomUUID()}`;
88
88
  this.clientSequence += 1;
package/cjs/index.js CHANGED
@@ -19,7 +19,7 @@ function aiLiveAssist(options = {}) {
19
19
  return undefined;
20
20
  }
21
21
  if (!app.checkRequiredVersion?.('7.3.0')) {
22
- console.warn('OXVO AI Live Assist requires @oxvo/browser version 7.3.0 or newer.');
22
+ console.warn('OXVO AI Guide requires @oxvo/browser version 7.3.0 or newer.');
23
23
  return undefined;
24
24
  }
25
25
  const instance = new AiLiveAssist_js_1.default(app, options);
package/cjs/messages.js CHANGED
@@ -3,12 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.defaultMessages = void 0;
4
4
  exports.defaultMessages = {
5
5
  launcher: "Ask AI",
6
- introTitle: "AI Live Assist",
6
+ introTitle: "AI Guide",
7
7
  disclosure: "You are interacting with an AI assistant.",
8
8
  requiredConsent: "I understand that I am interacting with an AI assistant.",
9
- consentAgreement: "I agree to use AI Live Assist.",
9
+ consentAgreement: "I agree to use AI Guide.",
10
10
  legalDetails: "Terms and privacy",
11
- legalTitle: "AI Live Assist terms and privacy",
11
+ legalTitle: "AI Guide terms and privacy",
12
12
  legalClose: "Close details",
13
13
  termsOfService: "Terms of service",
14
14
  microphoneConsent: "Send microphone audio to the AI provider for this session.",
@@ -38,8 +38,8 @@ exports.defaultMessages = {
38
38
  close: "Close",
39
39
  minimize: "Minimize",
40
40
  maximize: "Expand conversation",
41
- activeCall: "AI Live Assist is still active",
42
- movePanel: "Move AI Live Assist",
41
+ activeCall: "AI Guide is still active",
42
+ movePanel: "Move AI Guide",
43
43
  mute: "Mute",
44
44
  unmute: "Unmute",
45
45
  interrupt: "Stop AI response",
@@ -66,19 +66,19 @@ exports.defaultMessages = {
66
66
  actionVerified: "Action completed and verified",
67
67
  actionFailed: "The action could not be verified",
68
68
  limitReached: "This session reached its configured limit.",
69
- ended: "This AI Live Assist session has ended.",
69
+ ended: "This AI Guide session has ended.",
70
70
  idleEnded: "This session ended after no visitor activity.",
71
71
  feedbackQuestion: "Did this resolve your issue?",
72
72
  resolved: "Resolved",
73
73
  partiallyResolved: "Partly resolved",
74
74
  notResolved: "Not resolved",
75
75
  submitFeedback: "Submit feedback",
76
- error: "AI Live Assist is temporarily unavailable.",
76
+ error: "AI Guide is temporarily unavailable.",
77
77
  retry: "Retry",
78
- activeElsewhere: "AI Live Assist is active in another tab.",
79
- visitorLimitReached: "The daily AI Live Assist limit has been reached for this visitor.",
80
- visitorCooldown: "Please wait before starting another AI Live Assist session.",
81
- visitorAlreadyActive: "An AI Live Assist session is already active for this visitor.",
78
+ activeElsewhere: "AI Guide is active in another tab.",
79
+ visitorLimitReached: "The daily AI Guide limit has been reached for this visitor.",
80
+ visitorCooldown: "Please wait before starting another AI Guide session.",
81
+ visitorAlreadyActive: "An AI Guide session is already active for this visitor.",
82
82
  safetyBoundary: "Payment details, passwords, and verification codes are always handled by you. You can pause actions or end this session at any time.",
83
83
  privacyPolicy: "Privacy policy",
84
84
  moreControls: "More controls",
package/cjs/types.d.ts CHANGED
@@ -170,6 +170,7 @@ export type SanitizedTarget = {
170
170
  inputType?: string;
171
171
  autocomplete?: string;
172
172
  semanticRegion?: string;
173
+ destinationPath?: string;
173
174
  sensitivity: Sensitivity;
174
175
  protectedRegion?: boolean;
175
176
  frameOrigin?: "same_origin" | "cross_origin";
package/cjs/ui.js CHANGED
@@ -532,18 +532,21 @@ const style = `
532
532
  .visual-request h3 { margin: 0 0 6px; font-size: 14px; line-height: 20px; }
533
533
  .visual-request p { margin: 4px 0; color: #4b5563; font-size: 12px; line-height: 17px; }
534
534
  .feedback { display: none; }
535
- .feedback.visible { display: grid; gap: 12px; margin-top: 10px; padding: 8px 0 4px; }
535
+ .feedback.visible { display: grid; gap: 10px; margin-top: 10px; padding: 8px 0 4px; }
536
536
  .attribution-bubble {
537
+ justify-self: center;
537
538
  width: max-content;
538
539
  max-width: 100%;
539
- padding: 8px 10px;
540
+ margin-top: 2px;
541
+ padding: 0;
540
542
  color: var(--assist-muted);
541
- background: var(--assist-surface-muted);
542
- border: 1px solid var(--assist-border);
543
- border-radius: 8px;
544
- box-shadow: 0 4px 14px rgba(15, 23, 42, .08);
545
- font-size: 12px;
546
- line-height: 18px;
543
+ background: transparent;
544
+ border: 0;
545
+ border-radius: 0;
546
+ box-shadow: none;
547
+ font-size: 11px;
548
+ line-height: 16px;
549
+ text-align: center;
547
550
  animation: attribution-in .18s ease-out both;
548
551
  }
549
552
  .attribution-bubble a {
@@ -1475,13 +1478,13 @@ class WidgetView {
1475
1478
  <div class="status-card" data-role="notice"></div>
1476
1479
  <button type="button" class="secondary" data-role="retry" hidden></button>
1477
1480
  <div class="feedback">
1478
- <div class="attribution-bubble" data-role="attribution" role="status" hidden>Powered by <a href="https://oxvo.com/" target="_blank" rel="noopener noreferrer">OXVO.com</a></div>
1479
1481
  <strong></strong>
1480
1482
  <div class="feedback-options">
1481
1483
  <button type="button" class="secondary" data-outcome="resolved"></button>
1482
1484
  <button type="button" class="secondary" data-outcome="partially_resolved"></button>
1483
1485
  <button type="button" class="secondary" data-outcome="not_resolved"></button>
1484
1486
  </div>
1487
+ <div class="attribution-bubble" data-role="attribution" role="status" hidden>Powered by <a href="https://oxvo.com/" target="_blank" rel="noopener noreferrer">OXVO.com</a></div>
1485
1488
  </div>
1486
1489
  </div>
1487
1490
  <footer class="controls">
@@ -2346,7 +2349,7 @@ class WidgetView {
2346
2349
  require(selector) {
2347
2350
  const element = this.shadow.querySelector(selector);
2348
2351
  if (!element)
2349
- throw new Error(`Missing AI Live Assist UI element: ${selector}`);
2352
+ throw new Error(`Missing AI Guide UI element: ${selector}`);
2350
2353
  return element;
2351
2354
  }
2352
2355
  }
package/cjs/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "7.3.0";
1
+ export declare const VERSION = "7.3.4";
package/cjs/version.js CHANGED
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.VERSION = void 0;
4
- exports.VERSION = '7.3.0';
4
+ exports.VERSION = '7.3.4';
@@ -5,7 +5,7 @@ export declare const confirmationReplyIntent: (value: string) => ConfirmationRep
5
5
  export default class AiLiveAssist {
6
6
  private readonly app;
7
7
  private readonly options;
8
- readonly version = "7.3.0";
8
+ readonly version = "7.3.4";
9
9
  private readonly siteKey;
10
10
  private tabId;
11
11
  private readonly windowId;
@@ -1667,7 +1667,7 @@ export default class AiLiveAssist {
1667
1667
  ? 'Microphone access was not granted. Allow microphone access to use voice assist.'
1668
1668
  : 'Microphone access was not granted. You can continue with text.';
1669
1669
  }
1670
- return 'AI Live Assist is temporarily unavailable.';
1670
+ return 'AI Guide is temporarily unavailable.';
1671
1671
  }
1672
1672
  retryable(error) {
1673
1673
  return error instanceof RuntimeClientError ? error.retryable : false;
package/lib/client.js CHANGED
@@ -15,7 +15,7 @@ export class RuntimeClient {
15
15
  this.clientVersion = clientVersion;
16
16
  const url = new URL(runtimeUrl, window.location.href);
17
17
  if (!['http:', 'https:'].includes(url.protocol)) {
18
- throw new Error('AI Live Assist runtime URL must use HTTP or HTTPS.');
18
+ throw new Error('AI Guide runtime URL must use HTTP or HTTPS.');
19
19
  }
20
20
  this.baseUrl = url.origin + url.pathname.replace(/\/$/, '');
21
21
  }
@@ -182,6 +182,6 @@ export class RuntimeClient {
182
182
  }
183
183
  throw new RuntimeClientError(typeof error.code === 'string' ? error.code : 'RUNTIME_UNAVAILABLE', typeof error.message === 'string'
184
184
  ? error.message
185
- : 'AI Live Assist is temporarily unavailable.', error.retryable === true, typeof error.retryAfterMs === 'number' ? error.retryAfterMs : null, response.status);
185
+ : 'AI Guide is temporarily unavailable.', error.retryable === true, typeof error.retryAfterMs === 'number' ? error.retryAfterMs : null, response.status);
186
186
  }
187
187
  }
package/lib/context.d.ts CHANGED
@@ -17,6 +17,7 @@ export declare class PageContextCollector {
17
17
  private running;
18
18
  private mutationObserver;
19
19
  private captureTimer;
20
+ private mutationCaptureTimer;
20
21
  private targetIds;
21
22
  private textIds;
22
23
  private optionIds;
@@ -28,6 +29,7 @@ export declare class PageContextCollector {
28
29
  private pendingCapture;
29
30
  private deferredNavigation;
30
31
  private recentSelection;
32
+ private latestSemanticSignature;
31
33
  constructor(callbacks: ContextCallbacks, selectorRegionRules?: SelectorRegionRule[], policyVersion?: number);
32
34
  get currentRevision(): number;
33
35
  get currentPageId(): string;
@@ -36,6 +38,7 @@ export declare class PageContextCollector {
36
38
  stop(): void;
37
39
  captureAndPublish(): Promise<PageContextSnapshot>;
38
40
  capture(): PageContextSnapshot;
41
+ private captureSnapshot;
39
42
  captureVisualContext(): VisualContextCapture | null;
40
43
  execute(grant: ActionGrant): Promise<ActionExecutionResult>;
41
44
  completeActionExecution(actionId: string): Promise<string | null>;
@@ -47,6 +50,11 @@ export declare class PageContextCollector {
47
50
  private actionApplied;
48
51
  private clippedGeometry;
49
52
  private scheduleCapture;
53
+ private scheduleMutationCapture;
54
+ private scheduleViewportCapture;
55
+ private handleMutations;
56
+ private captureAndPublishIfChanged;
57
+ private semanticSignature;
50
58
  private handleSelectionChange;
51
59
  private handleControlStateChange;
52
60
  private readSafeSelection;
package/lib/context.js CHANGED
@@ -5,6 +5,8 @@ const MAX_TOTAL_TEXT = 24000;
5
5
  const MAX_OPTIONS = 100;
6
6
  const MAX_SELECTED_TEXT = 2000;
7
7
  const SELECTION_RETENTION_MS = 120000;
8
+ const CAPTURE_SETTLE_MS = 250;
9
+ const MUTATION_CAPTURE_DEBOUNCE_MS = 350;
8
10
  const PRIVATE_SELECTOR = [
9
11
  '[data-oxvo-ai-live-assist-root]',
10
12
  '[data-oxvo-private]',
@@ -63,6 +65,25 @@ const isCovered = (element) => {
63
65
  return Boolean(top && top !== element && !element.contains(top) && !top.contains(element));
64
66
  };
65
67
  const safePageUrl = () => `${window.location.origin}${window.location.pathname}`;
68
+ const safeDestinationPath = (element) => {
69
+ if (!(element instanceof HTMLAnchorElement))
70
+ return null;
71
+ try {
72
+ const destination = new URL(element.href, window.location.href);
73
+ if (destination.origin !== window.location.origin ||
74
+ destination.search ||
75
+ destination.hash ||
76
+ !destination.pathname.startsWith('/') ||
77
+ destination.pathname.startsWith('//') ||
78
+ destination.pathname.length > 1024) {
79
+ return null;
80
+ }
81
+ return destination.pathname;
82
+ }
83
+ catch {
84
+ return null;
85
+ }
86
+ };
66
87
  const sensitivityFor = (element, name) => {
67
88
  const input = element instanceof HTMLInputElement ? element : null;
68
89
  const type = input?.type.toLowerCase() ?? '';
@@ -232,6 +253,7 @@ export class PageContextCollector {
232
253
  this.running = false;
233
254
  this.mutationObserver = null;
234
255
  this.captureTimer = null;
256
+ this.mutationCaptureTimer = null;
235
257
  this.targetIds = new WeakMap();
236
258
  this.textIds = new WeakMap();
237
259
  this.optionIds = new WeakMap();
@@ -243,6 +265,7 @@ export class PageContextCollector {
243
265
  this.pendingCapture = false;
244
266
  this.deferredNavigation = null;
245
267
  this.recentSelection = null;
268
+ this.latestSemanticSignature = null;
246
269
  this.scheduleCapture = () => {
247
270
  if (!this.running)
248
271
  return;
@@ -250,20 +273,53 @@ export class PageContextCollector {
250
273
  this.pendingCapture = true;
251
274
  return;
252
275
  }
276
+ if (this.mutationCaptureTimer)
277
+ clearTimeout(this.mutationCaptureTimer);
278
+ this.mutationCaptureTimer = null;
253
279
  if (this.captureTimer)
254
280
  return;
255
281
  this.captureTimer = setTimeout(() => {
256
282
  this.captureTimer = null;
257
- void this.captureAndPublish();
258
- }, 250);
283
+ void this.captureAndPublishIfChanged();
284
+ }, CAPTURE_SETTLE_MS);
285
+ };
286
+ this.scheduleMutationCapture = () => {
287
+ if (!this.running)
288
+ return;
289
+ if (this.executingActionId) {
290
+ this.pendingCapture = true;
291
+ return;
292
+ }
293
+ if (this.captureTimer)
294
+ return;
295
+ if (this.mutationCaptureTimer)
296
+ clearTimeout(this.mutationCaptureTimer);
297
+ this.mutationCaptureTimer = setTimeout(() => {
298
+ this.mutationCaptureTimer = null;
299
+ void this.captureAndPublishIfChanged();
300
+ }, MUTATION_CAPTURE_DEBOUNCE_MS);
301
+ };
302
+ this.scheduleViewportCapture = () => {
303
+ this.scheduleMutationCapture();
304
+ };
305
+ this.handleMutations = (records) => {
306
+ const hasSemanticMutation = records.some((record) => record.type !== 'attributes' ||
307
+ (record.attributeName !== 'class' && record.attributeName !== 'style'));
308
+ if (hasSemanticMutation)
309
+ this.scheduleCapture();
310
+ else
311
+ this.scheduleMutationCapture();
259
312
  };
260
313
  this.handleSelectionChange = () => {
261
314
  const selection = this.readSafeSelection();
262
315
  if (selection)
263
316
  this.recentSelection = selection;
264
- this.scheduleCapture();
317
+ this.scheduleMutationCapture();
265
318
  };
266
- this.handleControlStateChange = () => {
319
+ this.handleControlStateChange = (event) => {
320
+ if (event.target instanceof Element && event.target.closest('[data-oxvo-ai-live-assist-root]')) {
321
+ return;
322
+ }
267
323
  this.scheduleCapture();
268
324
  };
269
325
  this.handleNavigation = () => {
@@ -318,7 +374,7 @@ export class PageContextCollector {
318
374
  return;
319
375
  this.running = true;
320
376
  this.installNavigationHooks();
321
- this.mutationObserver = new MutationObserver(() => this.scheduleCapture());
377
+ this.mutationObserver = new MutationObserver(this.handleMutations);
322
378
  if (document.body) {
323
379
  this.mutationObserver.observe(document.body, {
324
380
  subtree: true,
@@ -332,6 +388,7 @@ export class PageContextCollector {
332
388
  'aria-live',
333
389
  'class',
334
390
  'disabled',
391
+ 'href',
335
392
  'hidden',
336
393
  'inert',
337
394
  'role',
@@ -339,19 +396,20 @@ export class PageContextCollector {
339
396
  ],
340
397
  });
341
398
  }
342
- window.addEventListener('resize', this.scheduleCapture, { passive: true });
343
- document.addEventListener('scroll', this.scheduleCapture, {
399
+ window.addEventListener('resize', this.scheduleViewportCapture, { passive: true });
400
+ document.addEventListener('scroll', this.scheduleViewportCapture, {
344
401
  capture: true,
345
402
  passive: true,
346
403
  });
347
- window.visualViewport?.addEventListener('resize', this.scheduleCapture, {
404
+ window.visualViewport?.addEventListener('resize', this.scheduleViewportCapture, {
348
405
  passive: true,
349
406
  });
350
- window.visualViewport?.addEventListener('scroll', this.scheduleCapture, {
407
+ window.visualViewport?.addEventListener('scroll', this.scheduleViewportCapture, {
351
408
  passive: true,
352
409
  });
353
410
  window.addEventListener('popstate', this.handleNavigation);
354
411
  document.addEventListener('selectionchange', this.handleSelectionChange);
412
+ document.addEventListener('click', this.handleControlStateChange, true);
355
413
  document.addEventListener('input', this.handleControlStateChange, true);
356
414
  document.addEventListener('change', this.handleControlStateChange, true);
357
415
  void this.captureAndPublish();
@@ -363,12 +421,16 @@ export class PageContextCollector {
363
421
  if (this.captureTimer)
364
422
  clearTimeout(this.captureTimer);
365
423
  this.captureTimer = null;
366
- window.removeEventListener('resize', this.scheduleCapture);
367
- document.removeEventListener('scroll', this.scheduleCapture, true);
368
- window.visualViewport?.removeEventListener('resize', this.scheduleCapture);
369
- window.visualViewport?.removeEventListener('scroll', this.scheduleCapture);
424
+ if (this.mutationCaptureTimer)
425
+ clearTimeout(this.mutationCaptureTimer);
426
+ this.mutationCaptureTimer = null;
427
+ window.removeEventListener('resize', this.scheduleViewportCapture);
428
+ document.removeEventListener('scroll', this.scheduleViewportCapture, true);
429
+ window.visualViewport?.removeEventListener('resize', this.scheduleViewportCapture);
430
+ window.visualViewport?.removeEventListener('scroll', this.scheduleViewportCapture);
370
431
  window.removeEventListener('popstate', this.handleNavigation);
371
432
  document.removeEventListener('selectionchange', this.handleSelectionChange);
433
+ document.removeEventListener('click', this.handleControlStateChange, true);
372
434
  document.removeEventListener('input', this.handleControlStateChange, true);
373
435
  document.removeEventListener('change', this.handleControlStateChange, true);
374
436
  this.restoreNavigationHooks();
@@ -378,6 +440,7 @@ export class PageContextCollector {
378
440
  this.pendingCapture = false;
379
441
  this.deferredNavigation = null;
380
442
  this.recentSelection = null;
443
+ this.latestSemanticSignature = null;
381
444
  this.callbacks.onHighlight(null, 'focus');
382
445
  }
383
446
  async captureAndPublish() {
@@ -387,8 +450,10 @@ export class PageContextCollector {
387
450
  return snapshot;
388
451
  }
389
452
  capture() {
390
- this.revision += 1;
391
- const revision = this.revision;
453
+ return this.captureSnapshot(true);
454
+ }
455
+ captureSnapshot(force) {
456
+ const revision = this.revision + 1;
392
457
  const nextTargets = new Map();
393
458
  const targetValues = [];
394
459
  const elements = document.querySelectorAll('a[href],button,input,select,textarea,summary,form,[contenteditable="true"],[role],[tabindex]');
@@ -441,6 +506,7 @@ export class PageContextCollector {
441
506
  const role = roleFor(element);
442
507
  const sensitivity = sensitivityFor(element, name);
443
508
  const visible = isVisible(element);
509
+ const destinationPath = safeDestinationPath(element);
444
510
  const target = {
445
511
  targetId,
446
512
  revision,
@@ -453,6 +519,7 @@ export class PageContextCollector {
453
519
  ? { autocomplete: normalize(element.getAttribute('autocomplete') ?? '', 128) }
454
520
  : {}),
455
521
  ...(semanticRegion ? { semanticRegion } : {}),
522
+ ...(destinationPath ? { destinationPath } : {}),
456
523
  sensitivity,
457
524
  protectedRegion: sensitivity !== 'none' || matchedRuleDigests.length > 0,
458
525
  frameOrigin: 'same_origin',
@@ -475,7 +542,6 @@ export class PageContextCollector {
475
542
  targetValues.push(target);
476
543
  nextTargets.set(targetId, { element, revision, options: optionMap, target });
477
544
  }
478
- this.targets = nextTargets;
479
545
  const visibleText = [];
480
546
  let totalText = 0;
481
547
  const walker = document.createTreeWalker(document.body ?? document.documentElement, NodeFilter.SHOW_TEXT);
@@ -515,7 +581,7 @@ export class PageContextCollector {
515
581
  Date.now() - Date.parse(this.recentSelection.capturedAt) > SELECTION_RETENTION_MS) {
516
582
  this.recentSelection = null;
517
583
  }
518
- return {
584
+ const snapshot = {
519
585
  schemaVersion: 1,
520
586
  pageId: this.pageId,
521
587
  revision,
@@ -534,6 +600,13 @@ export class PageContextCollector {
534
600
  alerts,
535
601
  truncated,
536
602
  };
603
+ const signature = this.semanticSignature(snapshot);
604
+ if (!force && signature === this.latestSemanticSignature)
605
+ return null;
606
+ this.revision = revision;
607
+ this.targets = nextTargets;
608
+ this.latestSemanticSignature = signature;
609
+ return snapshot;
537
610
  }
538
611
  captureVisualContext() {
539
612
  if (this.revision < 1 || this.targets.size === 0)
@@ -656,7 +729,7 @@ export class PageContextCollector {
656
729
  element.focus({ preventScroll: false });
657
730
  break;
658
731
  case 'click':
659
- this.click(element, grant.actionId, grant.desiredChecked);
732
+ this.click(element, grant.actionId, grant.desiredChecked, handle.target.destinationPath);
660
733
  break;
661
734
  case 'type':
662
735
  this.type(element, grant.value ?? '');
@@ -734,7 +807,7 @@ export class PageContextCollector {
734
807
  ? deferred.toUrl
735
808
  : null;
736
809
  }
737
- click(element, actionId, desiredChecked) {
810
+ click(element, actionId, desiredChecked, expectedDestinationPath) {
738
811
  if (!(element instanceof HTMLElement))
739
812
  throw new Error('Target is not clickable.');
740
813
  if (desiredChecked !== undefined) {
@@ -749,6 +822,10 @@ export class PageContextCollector {
749
822
  }
750
823
  if (element instanceof HTMLAnchorElement) {
751
824
  const target = new URL(element.href, window.location.href);
825
+ if (expectedDestinationPath !== undefined &&
826
+ safeDestinationPath(element) !== expectedDestinationPath) {
827
+ throw new Error('The navigation destination changed before execution.');
828
+ }
752
829
  if (target.origin !== window.location.origin) {
753
830
  throw new Error('External navigation is blocked.');
754
831
  }
@@ -892,6 +969,26 @@ export class PageContextCollector {
892
969
  height,
893
970
  };
894
971
  }
972
+ async captureAndPublishIfChanged() {
973
+ const snapshot = this.captureSnapshot(false);
974
+ if (snapshot && this.running)
975
+ await this.callbacks.onSnapshot(snapshot);
976
+ }
977
+ semanticSignature(snapshot) {
978
+ return JSON.stringify({
979
+ ...snapshot,
980
+ revision: 0,
981
+ ...(snapshot.selection
982
+ ? {
983
+ selection: {
984
+ text: snapshot.selection.text,
985
+ source: snapshot.selection.source,
986
+ },
987
+ }
988
+ : {}),
989
+ targets: snapshot.targets.map((target) => ({ ...target, revision: 0 })),
990
+ });
991
+ }
895
992
  readSafeSelection() {
896
993
  const selection = window.getSelection();
897
994
  if (!selection || selection.isCollapsed || selection.rangeCount !== 1)
package/lib/control.js CHANGED
@@ -28,7 +28,7 @@ export class ControlChannel {
28
28
  }
29
29
  setFencingToken(value) {
30
30
  if (!Number.isSafeInteger(value) || value < 1) {
31
- throw new Error('Invalid AI Live Assist fencing token.');
31
+ throw new Error('Invalid AI Guide fencing token.');
32
32
  }
33
33
  this.fencingToken = value;
34
34
  }
@@ -67,7 +67,7 @@ export class ControlChannel {
67
67
  socket.addEventListener('error', () => {
68
68
  signal.removeEventListener('abort', abort);
69
69
  if (socket.readyState !== WebSocket.OPEN) {
70
- reject(new Error('The AI Live Assist control channel could not connect.'));
70
+ reject(new Error('The AI Guide control channel could not connect.'));
71
71
  }
72
72
  }, { once: true });
73
73
  socket.addEventListener('close', () => {
@@ -79,7 +79,7 @@ export class ControlChannel {
79
79
  }
80
80
  send(type, payload) {
81
81
  if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
82
- throw new Error('The AI Live Assist control channel is not connected.');
82
+ throw new Error('The AI Guide control channel is not connected.');
83
83
  }
84
84
  const id = `msg_${crypto.randomUUID()}`;
85
85
  this.clientSequence += 1;
package/lib/index.js CHANGED
@@ -13,7 +13,7 @@ export default function aiLiveAssist(options = {}) {
13
13
  return undefined;
14
14
  }
15
15
  if (!app.checkRequiredVersion?.('7.3.0')) {
16
- console.warn('OXVO AI Live Assist requires @oxvo/browser version 7.3.0 or newer.');
16
+ console.warn('OXVO AI Guide requires @oxvo/browser version 7.3.0 or newer.');
17
17
  return undefined;
18
18
  }
19
19
  const instance = new AiLiveAssist(app, options);
package/lib/messages.js CHANGED
@@ -1,11 +1,11 @@
1
1
  export const defaultMessages = {
2
2
  launcher: "Ask AI",
3
- introTitle: "AI Live Assist",
3
+ introTitle: "AI Guide",
4
4
  disclosure: "You are interacting with an AI assistant.",
5
5
  requiredConsent: "I understand that I am interacting with an AI assistant.",
6
- consentAgreement: "I agree to use AI Live Assist.",
6
+ consentAgreement: "I agree to use AI Guide.",
7
7
  legalDetails: "Terms and privacy",
8
- legalTitle: "AI Live Assist terms and privacy",
8
+ legalTitle: "AI Guide terms and privacy",
9
9
  legalClose: "Close details",
10
10
  termsOfService: "Terms of service",
11
11
  microphoneConsent: "Send microphone audio to the AI provider for this session.",
@@ -35,8 +35,8 @@ export const defaultMessages = {
35
35
  close: "Close",
36
36
  minimize: "Minimize",
37
37
  maximize: "Expand conversation",
38
- activeCall: "AI Live Assist is still active",
39
- movePanel: "Move AI Live Assist",
38
+ activeCall: "AI Guide is still active",
39
+ movePanel: "Move AI Guide",
40
40
  mute: "Mute",
41
41
  unmute: "Unmute",
42
42
  interrupt: "Stop AI response",
@@ -63,19 +63,19 @@ export const defaultMessages = {
63
63
  actionVerified: "Action completed and verified",
64
64
  actionFailed: "The action could not be verified",
65
65
  limitReached: "This session reached its configured limit.",
66
- ended: "This AI Live Assist session has ended.",
66
+ ended: "This AI Guide session has ended.",
67
67
  idleEnded: "This session ended after no visitor activity.",
68
68
  feedbackQuestion: "Did this resolve your issue?",
69
69
  resolved: "Resolved",
70
70
  partiallyResolved: "Partly resolved",
71
71
  notResolved: "Not resolved",
72
72
  submitFeedback: "Submit feedback",
73
- error: "AI Live Assist is temporarily unavailable.",
73
+ error: "AI Guide is temporarily unavailable.",
74
74
  retry: "Retry",
75
- activeElsewhere: "AI Live Assist is active in another tab.",
76
- visitorLimitReached: "The daily AI Live Assist limit has been reached for this visitor.",
77
- visitorCooldown: "Please wait before starting another AI Live Assist session.",
78
- visitorAlreadyActive: "An AI Live Assist session is already active for this visitor.",
75
+ activeElsewhere: "AI Guide is active in another tab.",
76
+ visitorLimitReached: "The daily AI Guide limit has been reached for this visitor.",
77
+ visitorCooldown: "Please wait before starting another AI Guide session.",
78
+ visitorAlreadyActive: "An AI Guide session is already active for this visitor.",
79
79
  safetyBoundary: "Payment details, passwords, and verification codes are always handled by you. You can pause actions or end this session at any time.",
80
80
  privacyPolicy: "Privacy policy",
81
81
  moreControls: "More controls",
package/lib/types.d.ts CHANGED
@@ -170,6 +170,7 @@ export type SanitizedTarget = {
170
170
  inputType?: string;
171
171
  autocomplete?: string;
172
172
  semanticRegion?: string;
173
+ destinationPath?: string;
173
174
  sensitivity: Sensitivity;
174
175
  protectedRegion?: boolean;
175
176
  frameOrigin?: "same_origin" | "cross_origin";
package/lib/ui.js CHANGED
@@ -529,18 +529,21 @@ const style = `
529
529
  .visual-request h3 { margin: 0 0 6px; font-size: 14px; line-height: 20px; }
530
530
  .visual-request p { margin: 4px 0; color: #4b5563; font-size: 12px; line-height: 17px; }
531
531
  .feedback { display: none; }
532
- .feedback.visible { display: grid; gap: 12px; margin-top: 10px; padding: 8px 0 4px; }
532
+ .feedback.visible { display: grid; gap: 10px; margin-top: 10px; padding: 8px 0 4px; }
533
533
  .attribution-bubble {
534
+ justify-self: center;
534
535
  width: max-content;
535
536
  max-width: 100%;
536
- padding: 8px 10px;
537
+ margin-top: 2px;
538
+ padding: 0;
537
539
  color: var(--assist-muted);
538
- background: var(--assist-surface-muted);
539
- border: 1px solid var(--assist-border);
540
- border-radius: 8px;
541
- box-shadow: 0 4px 14px rgba(15, 23, 42, .08);
542
- font-size: 12px;
543
- line-height: 18px;
540
+ background: transparent;
541
+ border: 0;
542
+ border-radius: 0;
543
+ box-shadow: none;
544
+ font-size: 11px;
545
+ line-height: 16px;
546
+ text-align: center;
544
547
  animation: attribution-in .18s ease-out both;
545
548
  }
546
549
  .attribution-bubble a {
@@ -1472,13 +1475,13 @@ export class WidgetView {
1472
1475
  <div class="status-card" data-role="notice"></div>
1473
1476
  <button type="button" class="secondary" data-role="retry" hidden></button>
1474
1477
  <div class="feedback">
1475
- <div class="attribution-bubble" data-role="attribution" role="status" hidden>Powered by <a href="https://oxvo.com/" target="_blank" rel="noopener noreferrer">OXVO.com</a></div>
1476
1478
  <strong></strong>
1477
1479
  <div class="feedback-options">
1478
1480
  <button type="button" class="secondary" data-outcome="resolved"></button>
1479
1481
  <button type="button" class="secondary" data-outcome="partially_resolved"></button>
1480
1482
  <button type="button" class="secondary" data-outcome="not_resolved"></button>
1481
1483
  </div>
1484
+ <div class="attribution-bubble" data-role="attribution" role="status" hidden>Powered by <a href="https://oxvo.com/" target="_blank" rel="noopener noreferrer">OXVO.com</a></div>
1482
1485
  </div>
1483
1486
  </div>
1484
1487
  <footer class="controls">
@@ -2343,7 +2346,7 @@ export class WidgetView {
2343
2346
  require(selector) {
2344
2347
  const element = this.shadow.querySelector(selector);
2345
2348
  if (!element)
2346
- throw new Error(`Missing AI Live Assist UI element: ${selector}`);
2349
+ throw new Error(`Missing AI Guide UI element: ${selector}`);
2347
2350
  return element;
2348
2351
  }
2349
2352
  }
package/lib/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "7.3.0";
1
+ export declare const VERSION = "7.3.4";
package/lib/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = '7.3.0';
1
+ export const VERSION = '7.3.4';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@oxvo/ai-live-assist",
3
- "description": "Secure AI Live Assist browser plugin for OXVO Sessions.",
4
- "version": "7.3.0",
3
+ "description": "Secure AI Guide browser plugin for OXVO Sessions.",
4
+ "version": "7.3.4",
5
5
  "keywords": [
6
6
  "ai-live-assist",
7
7
  "webrtc",