@oxvo/ai-live-assist 7.4.49 → 7.4.51

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.
@@ -39,7 +39,7 @@ export declare const resolveAiGuideProtocolVersion: (bootstrap: Pick<BootstrapCo
39
39
  export default class AiLiveAssist {
40
40
  private readonly app;
41
41
  private readonly options;
42
- readonly version = "7.4.49";
42
+ readonly version = "7.4.51";
43
43
  private readonly siteKey;
44
44
  private tabId;
45
45
  private readonly windowId;
@@ -6946,9 +6946,18 @@ class AiLiveAssist {
6946
6946
  !this.pendingLaunchMatches(this.lastLaunch, pendingLaunch)) {
6947
6947
  return;
6948
6948
  }
6949
- this.lastLaunch = null;
6950
6949
  this.clearPendingIntent();
6951
6950
  this.view?.showActiveElsewhere(false);
6951
+ if (messageKey === "visitorAlreadyActive") {
6952
+ // The explicit entry action has already staged the compact Voice/Text
6953
+ // surface. Keep that surface as a retryable, closable recovery card.
6954
+ // The generic unavailable presenter resets compact state while leaving
6955
+ // the open panel mounted, which produces an empty full-height shell.
6956
+ this.view?.showError(this.view.message(messageKey), true, false);
6957
+ this.state("error");
6958
+ return;
6959
+ }
6960
+ this.lastLaunch = null;
6952
6961
  this.view?.showLaunchUnavailable(this.view.message(messageKey));
6953
6962
  this.state("ready");
6954
6963
  }
@@ -13415,6 +13424,11 @@ class AiLiveAssist {
13415
13424
  return this.resume(resume);
13416
13425
  }
13417
13426
  if (this.lastLaunch) {
13427
+ if (this.visitorActiveTakeoverOnly) {
13428
+ // A deliberate retry gets one fresh bounded eligibility check. The
13429
+ // key still prevents accidental automatic replay loops.
13430
+ this.visitorActiveEligibilityRecheckKey = null;
13431
+ }
13418
13432
  return this.launch(this.lastLaunch.mode, this.lastLaunch.scopes, this.lastLaunch.idempotencyKey, { pendingLaunch: this.lastLaunch });
13419
13433
  }
13420
13434
  }
package/cjs/context.d.ts CHANGED
@@ -60,6 +60,11 @@ export declare class PageContextCollector {
60
60
  private scheduleCapture;
61
61
  private scheduleMutationCapture;
62
62
  private scheduleViewportCapture;
63
+ private mutationNodeIsInsideTrustedGuide;
64
+ private mutationIsInsideTrustedGuide;
65
+ private mutationNodeTouchesSemanticContext;
66
+ private mutationNodeContainsSemanticContext;
67
+ private mutationNeedsImmediateCapture;
63
68
  private handleMutations;
64
69
  private captureAndPublishIfChanged;
65
70
  private semanticSignature;
package/cjs/context.js CHANGED
@@ -12,6 +12,7 @@ const CAPTURE_SETTLE_MS = 250;
12
12
  const MUTATION_CAPTURE_DEBOUNCE_MS = 350;
13
13
  const FRAMER_TOGGLE_ROOT_SELECTOR = '[data-framer-name="Toggle"]';
14
14
  const FRAMER_TOGGLE_CONTROL_SELECTOR = 'div[name="Toggle Switch"][data-highlight="true"][tabindex]';
15
+ const ACTIONABLE_MUTATION_SELECTOR = `a[href],button,input,select,textarea,summary,[role="button"],[role="link"],[role="tab"],[role="menuitem"],[role="option"],[role="checkbox"],[role="radio"],[role="switch"],[role="combobox"],[role="listbox"],[role="treeitem"],[role="slider"],[role="spinbutton"],[role="searchbox"],[role="textbox"],${FRAMER_TOGGLE_CONTROL_SELECTOR}`;
15
16
  const MAX_FRAMER_TOGGLE_LABEL = 120;
16
17
  const PRIVATE_SELECTOR = [
17
18
  '[data-oxvo-ai-live-assist-root]',
@@ -692,9 +693,48 @@ class PageContextCollector {
692
693
  this.scheduleViewportCapture = () => {
693
694
  this.scheduleMutationCapture();
694
695
  };
696
+ this.mutationNodeIsInsideTrustedGuide = (node) => Boolean(this.trustedGuideRoot &&
697
+ (node === this.trustedGuideRoot || this.trustedGuideRoot.contains(node)));
698
+ this.mutationIsInsideTrustedGuide = (record) => {
699
+ if (this.mutationNodeIsInsideTrustedGuide(record.target))
700
+ return true;
701
+ if (record.type !== 'childList')
702
+ return false;
703
+ const changedNodes = [...record.addedNodes, ...record.removedNodes];
704
+ return (changedNodes.length > 0 &&
705
+ changedNodes.every(this.mutationNodeIsInsideTrustedGuide));
706
+ };
707
+ this.mutationNodeTouchesSemanticContext = (node) => {
708
+ const element = node instanceof Element
709
+ ? node
710
+ : node.nodeType === Node.TEXT_NODE
711
+ ? node.parentElement
712
+ : null;
713
+ return Boolean(element &&
714
+ (element.matches(ACTIONABLE_MUTATION_SELECTOR) ||
715
+ element.closest(ACTIONABLE_MUTATION_SELECTOR)));
716
+ };
717
+ this.mutationNodeContainsSemanticContext = (node) => this.mutationNodeTouchesSemanticContext(node) ||
718
+ Boolean(node instanceof Element &&
719
+ node.querySelector(ACTIONABLE_MUTATION_SELECTOR));
720
+ this.mutationNeedsImmediateCapture = (record) => {
721
+ if (record.type === 'attributes') {
722
+ if (record.attributeName === 'class' || record.attributeName === 'style') {
723
+ return false;
724
+ }
725
+ return this.mutationNodeTouchesSemanticContext(record.target);
726
+ }
727
+ if (this.mutationNodeTouchesSemanticContext(record.target))
728
+ return true;
729
+ if (record.type !== 'childList')
730
+ return false;
731
+ return [...record.addedNodes, ...record.removedNodes].some(this.mutationNodeContainsSemanticContext);
732
+ };
695
733
  this.handleMutations = (records) => {
696
- const hasSemanticMutation = records.some((record) => record.type !== 'attributes' ||
697
- (record.attributeName !== 'class' && record.attributeName !== 'style'));
734
+ const relevantRecords = records.filter((record) => !this.mutationIsInsideTrustedGuide(record));
735
+ if (relevantRecords.length === 0)
736
+ return;
737
+ const hasSemanticMutation = relevantRecords.some(this.mutationNeedsImmediateCapture);
698
738
  if (hasSemanticMutation)
699
739
  this.scheduleCapture();
700
740
  else
package/cjs/index.js CHANGED
@@ -18,8 +18,8 @@ function aiLiveAssist(options = {}) {
18
18
  if (document.querySelector('[data-oxvo-ai-live-assist-root]')) {
19
19
  return undefined;
20
20
  }
21
- if (!app.checkRequiredVersion?.('7.4.49')) {
22
- console.warn('OXVO AI Guide requires @oxvo/browser version 7.4.49 or newer.');
21
+ if (!app.checkRequiredVersion?.('7.4.51')) {
22
+ console.warn('OXVO AI Guide requires @oxvo/browser version 7.4.51 or newer.');
23
23
  return undefined;
24
24
  }
25
25
  const instance = new AiLiveAssist_js_1.default(app, options);
package/cjs/preview.d.ts CHANGED
@@ -2,7 +2,7 @@ import type { AiGuidePreviewBridge, AiGuidePreviewController, AiGuidePreviewCont
2
2
  export declare const AI_GUIDE_PREVIEW_PARENT_SOURCE = "oxvo-ai-guide-settings-preview";
3
3
  export declare const AI_GUIDE_PREVIEW_WIDGET_SOURCE = "oxvo-ai-guide-widget-preview";
4
4
  export declare const AI_GUIDE_PREVIEW_PROTOCOL_VERSION = 1;
5
- export declare const AI_GUIDE_PREVIEW_RENDERER_VERSION = "1.0.48";
5
+ export declare const AI_GUIDE_PREVIEW_RENDERER_VERSION = "1.0.49";
6
6
  export declare const buildAiGuidePreviewBootstrap: (draft: unknown, assetOrigin?: string | null) => BootstrapConfig;
7
7
  export declare class AiGuidePreviewControllerImpl implements AiGuidePreviewController {
8
8
  private readonly port;
package/cjs/preview.js CHANGED
@@ -9,7 +9,7 @@ const voicePresenceUi_js_1 = require("./voicePresenceUi.js");
9
9
  exports.AI_GUIDE_PREVIEW_PARENT_SOURCE = "oxvo-ai-guide-settings-preview";
10
10
  exports.AI_GUIDE_PREVIEW_WIDGET_SOURCE = "oxvo-ai-guide-widget-preview";
11
11
  exports.AI_GUIDE_PREVIEW_PROTOCOL_VERSION = 1;
12
- exports.AI_GUIDE_PREVIEW_RENDERER_VERSION = "1.0.48";
12
+ exports.AI_GUIDE_PREVIEW_RENDERER_VERSION = "1.0.49";
13
13
  const DEFAULT_COMPANION = {
14
14
  enabled: true,
15
15
  title: "AI Guide",
package/cjs/ui.js CHANGED
@@ -6673,10 +6673,25 @@ class WidgetView {
6673
6673
  if (!element || !element.isConnected)
6674
6674
  return;
6675
6675
  const rect = element.getBoundingClientRect();
6676
- this.highlight.style.left = `${Math.max(0, rect.left - 4)}px`;
6677
- this.highlight.style.top = `${Math.max(0, rect.top - 4)}px`;
6678
- this.highlight.style.width = `${Math.max(8, rect.width + 8)}px`;
6679
- this.highlight.style.height = `${Math.max(8, rect.height + 8)}px`;
6676
+ const visual = window.visualViewport;
6677
+ const viewportLeft = visual?.offsetLeft ?? 0;
6678
+ const viewportTop = visual?.offsetTop ?? 0;
6679
+ const viewportRight = viewportLeft + (visual?.width ?? window.innerWidth);
6680
+ const viewportBottom = viewportTop + (visual?.height ?? window.innerHeight);
6681
+ if (rect.right <= viewportLeft ||
6682
+ rect.bottom <= viewportTop ||
6683
+ rect.left >= viewportRight ||
6684
+ rect.top >= viewportBottom) {
6685
+ return;
6686
+ }
6687
+ const left = Math.max(viewportLeft, rect.left - 4);
6688
+ const top = Math.max(viewportTop, rect.top - 4);
6689
+ const right = Math.min(viewportRight, rect.right + 4);
6690
+ const bottom = Math.min(viewportBottom, rect.bottom + 4);
6691
+ this.highlight.style.left = `${left}px`;
6692
+ this.highlight.style.top = `${top}px`;
6693
+ this.highlight.style.width = `${right - left}px`;
6694
+ this.highlight.style.height = `${bottom - top}px`;
6680
6695
  this.highlight.classList.add("visible");
6681
6696
  }
6682
6697
  safetySurfaceVisible() {
package/cjs/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "7.4.49";
1
+ export declare const VERSION = "7.4.51";
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.4.49';
4
+ exports.VERSION = '7.4.51';
@@ -39,7 +39,7 @@ export declare const resolveAiGuideProtocolVersion: (bootstrap: Pick<BootstrapCo
39
39
  export default class AiLiveAssist {
40
40
  private readonly app;
41
41
  private readonly options;
42
- readonly version = "7.4.49";
42
+ readonly version = "7.4.51";
43
43
  private readonly siteKey;
44
44
  private tabId;
45
45
  private readonly windowId;
@@ -6938,9 +6938,18 @@ export default class AiLiveAssist {
6938
6938
  !this.pendingLaunchMatches(this.lastLaunch, pendingLaunch)) {
6939
6939
  return;
6940
6940
  }
6941
- this.lastLaunch = null;
6942
6941
  this.clearPendingIntent();
6943
6942
  this.view?.showActiveElsewhere(false);
6943
+ if (messageKey === "visitorAlreadyActive") {
6944
+ // The explicit entry action has already staged the compact Voice/Text
6945
+ // surface. Keep that surface as a retryable, closable recovery card.
6946
+ // The generic unavailable presenter resets compact state while leaving
6947
+ // the open panel mounted, which produces an empty full-height shell.
6948
+ this.view?.showError(this.view.message(messageKey), true, false);
6949
+ this.state("error");
6950
+ return;
6951
+ }
6952
+ this.lastLaunch = null;
6944
6953
  this.view?.showLaunchUnavailable(this.view.message(messageKey));
6945
6954
  this.state("ready");
6946
6955
  }
@@ -13407,6 +13416,11 @@ export default class AiLiveAssist {
13407
13416
  return this.resume(resume);
13408
13417
  }
13409
13418
  if (this.lastLaunch) {
13419
+ if (this.visitorActiveTakeoverOnly) {
13420
+ // A deliberate retry gets one fresh bounded eligibility check. The
13421
+ // key still prevents accidental automatic replay loops.
13422
+ this.visitorActiveEligibilityRecheckKey = null;
13423
+ }
13410
13424
  return this.launch(this.lastLaunch.mode, this.lastLaunch.scopes, this.lastLaunch.idempotencyKey, { pendingLaunch: this.lastLaunch });
13411
13425
  }
13412
13426
  }
package/lib/context.d.ts CHANGED
@@ -60,6 +60,11 @@ export declare class PageContextCollector {
60
60
  private scheduleCapture;
61
61
  private scheduleMutationCapture;
62
62
  private scheduleViewportCapture;
63
+ private mutationNodeIsInsideTrustedGuide;
64
+ private mutationIsInsideTrustedGuide;
65
+ private mutationNodeTouchesSemanticContext;
66
+ private mutationNodeContainsSemanticContext;
67
+ private mutationNeedsImmediateCapture;
63
68
  private handleMutations;
64
69
  private captureAndPublishIfChanged;
65
70
  private semanticSignature;
package/lib/context.js CHANGED
@@ -9,6 +9,7 @@ const CAPTURE_SETTLE_MS = 250;
9
9
  const MUTATION_CAPTURE_DEBOUNCE_MS = 350;
10
10
  const FRAMER_TOGGLE_ROOT_SELECTOR = '[data-framer-name="Toggle"]';
11
11
  const FRAMER_TOGGLE_CONTROL_SELECTOR = 'div[name="Toggle Switch"][data-highlight="true"][tabindex]';
12
+ const ACTIONABLE_MUTATION_SELECTOR = `a[href],button,input,select,textarea,summary,[role="button"],[role="link"],[role="tab"],[role="menuitem"],[role="option"],[role="checkbox"],[role="radio"],[role="switch"],[role="combobox"],[role="listbox"],[role="treeitem"],[role="slider"],[role="spinbutton"],[role="searchbox"],[role="textbox"],${FRAMER_TOGGLE_CONTROL_SELECTOR}`;
12
13
  const MAX_FRAMER_TOGGLE_LABEL = 120;
13
14
  const PRIVATE_SELECTOR = [
14
15
  '[data-oxvo-ai-live-assist-root]',
@@ -688,9 +689,48 @@ export class PageContextCollector {
688
689
  this.scheduleViewportCapture = () => {
689
690
  this.scheduleMutationCapture();
690
691
  };
692
+ this.mutationNodeIsInsideTrustedGuide = (node) => Boolean(this.trustedGuideRoot &&
693
+ (node === this.trustedGuideRoot || this.trustedGuideRoot.contains(node)));
694
+ this.mutationIsInsideTrustedGuide = (record) => {
695
+ if (this.mutationNodeIsInsideTrustedGuide(record.target))
696
+ return true;
697
+ if (record.type !== 'childList')
698
+ return false;
699
+ const changedNodes = [...record.addedNodes, ...record.removedNodes];
700
+ return (changedNodes.length > 0 &&
701
+ changedNodes.every(this.mutationNodeIsInsideTrustedGuide));
702
+ };
703
+ this.mutationNodeTouchesSemanticContext = (node) => {
704
+ const element = node instanceof Element
705
+ ? node
706
+ : node.nodeType === Node.TEXT_NODE
707
+ ? node.parentElement
708
+ : null;
709
+ return Boolean(element &&
710
+ (element.matches(ACTIONABLE_MUTATION_SELECTOR) ||
711
+ element.closest(ACTIONABLE_MUTATION_SELECTOR)));
712
+ };
713
+ this.mutationNodeContainsSemanticContext = (node) => this.mutationNodeTouchesSemanticContext(node) ||
714
+ Boolean(node instanceof Element &&
715
+ node.querySelector(ACTIONABLE_MUTATION_SELECTOR));
716
+ this.mutationNeedsImmediateCapture = (record) => {
717
+ if (record.type === 'attributes') {
718
+ if (record.attributeName === 'class' || record.attributeName === 'style') {
719
+ return false;
720
+ }
721
+ return this.mutationNodeTouchesSemanticContext(record.target);
722
+ }
723
+ if (this.mutationNodeTouchesSemanticContext(record.target))
724
+ return true;
725
+ if (record.type !== 'childList')
726
+ return false;
727
+ return [...record.addedNodes, ...record.removedNodes].some(this.mutationNodeContainsSemanticContext);
728
+ };
691
729
  this.handleMutations = (records) => {
692
- const hasSemanticMutation = records.some((record) => record.type !== 'attributes' ||
693
- (record.attributeName !== 'class' && record.attributeName !== 'style'));
730
+ const relevantRecords = records.filter((record) => !this.mutationIsInsideTrustedGuide(record));
731
+ if (relevantRecords.length === 0)
732
+ return;
733
+ const hasSemanticMutation = relevantRecords.some(this.mutationNeedsImmediateCapture);
694
734
  if (hasSemanticMutation)
695
735
  this.scheduleCapture();
696
736
  else
package/lib/index.js CHANGED
@@ -12,8 +12,8 @@ export default function aiLiveAssist(options = {}) {
12
12
  if (document.querySelector('[data-oxvo-ai-live-assist-root]')) {
13
13
  return undefined;
14
14
  }
15
- if (!app.checkRequiredVersion?.('7.4.49')) {
16
- console.warn('OXVO AI Guide requires @oxvo/browser version 7.4.49 or newer.');
15
+ if (!app.checkRequiredVersion?.('7.4.51')) {
16
+ console.warn('OXVO AI Guide requires @oxvo/browser version 7.4.51 or newer.');
17
17
  return undefined;
18
18
  }
19
19
  const instance = new AiLiveAssist(app, options);
package/lib/preview.d.ts CHANGED
@@ -2,7 +2,7 @@ import type { AiGuidePreviewBridge, AiGuidePreviewController, AiGuidePreviewCont
2
2
  export declare const AI_GUIDE_PREVIEW_PARENT_SOURCE = "oxvo-ai-guide-settings-preview";
3
3
  export declare const AI_GUIDE_PREVIEW_WIDGET_SOURCE = "oxvo-ai-guide-widget-preview";
4
4
  export declare const AI_GUIDE_PREVIEW_PROTOCOL_VERSION = 1;
5
- export declare const AI_GUIDE_PREVIEW_RENDERER_VERSION = "1.0.48";
5
+ export declare const AI_GUIDE_PREVIEW_RENDERER_VERSION = "1.0.49";
6
6
  export declare const buildAiGuidePreviewBootstrap: (draft: unknown, assetOrigin?: string | null) => BootstrapConfig;
7
7
  export declare class AiGuidePreviewControllerImpl implements AiGuidePreviewController {
8
8
  private readonly port;
package/lib/preview.js CHANGED
@@ -6,7 +6,7 @@ import { VoicePresenceView } from "./voicePresenceUi.js";
6
6
  export const AI_GUIDE_PREVIEW_PARENT_SOURCE = "oxvo-ai-guide-settings-preview";
7
7
  export const AI_GUIDE_PREVIEW_WIDGET_SOURCE = "oxvo-ai-guide-widget-preview";
8
8
  export const AI_GUIDE_PREVIEW_PROTOCOL_VERSION = 1;
9
- export const AI_GUIDE_PREVIEW_RENDERER_VERSION = "1.0.48";
9
+ export const AI_GUIDE_PREVIEW_RENDERER_VERSION = "1.0.49";
10
10
  const DEFAULT_COMPANION = {
11
11
  enabled: true,
12
12
  title: "AI Guide",
package/lib/ui.js CHANGED
@@ -6670,10 +6670,25 @@ export class WidgetView {
6670
6670
  if (!element || !element.isConnected)
6671
6671
  return;
6672
6672
  const rect = element.getBoundingClientRect();
6673
- this.highlight.style.left = `${Math.max(0, rect.left - 4)}px`;
6674
- this.highlight.style.top = `${Math.max(0, rect.top - 4)}px`;
6675
- this.highlight.style.width = `${Math.max(8, rect.width + 8)}px`;
6676
- this.highlight.style.height = `${Math.max(8, rect.height + 8)}px`;
6673
+ const visual = window.visualViewport;
6674
+ const viewportLeft = visual?.offsetLeft ?? 0;
6675
+ const viewportTop = visual?.offsetTop ?? 0;
6676
+ const viewportRight = viewportLeft + (visual?.width ?? window.innerWidth);
6677
+ const viewportBottom = viewportTop + (visual?.height ?? window.innerHeight);
6678
+ if (rect.right <= viewportLeft ||
6679
+ rect.bottom <= viewportTop ||
6680
+ rect.left >= viewportRight ||
6681
+ rect.top >= viewportBottom) {
6682
+ return;
6683
+ }
6684
+ const left = Math.max(viewportLeft, rect.left - 4);
6685
+ const top = Math.max(viewportTop, rect.top - 4);
6686
+ const right = Math.min(viewportRight, rect.right + 4);
6687
+ const bottom = Math.min(viewportBottom, rect.bottom + 4);
6688
+ this.highlight.style.left = `${left}px`;
6689
+ this.highlight.style.top = `${top}px`;
6690
+ this.highlight.style.width = `${right - left}px`;
6691
+ this.highlight.style.height = `${bottom - top}px`;
6677
6692
  this.highlight.classList.add("visible");
6678
6693
  }
6679
6694
  safetySurfaceVisible() {
package/lib/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "7.4.49";
1
+ export declare const VERSION = "7.4.51";
package/lib/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = '7.4.49';
1
+ export const VERSION = '7.4.51';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@oxvo/ai-live-assist",
3
3
  "description": "Secure AI Guide browser plugin for OXVO Sessions.",
4
- "version": "7.4.49",
4
+ "version": "7.4.51",
5
5
  "keywords": [
6
6
  "ai-live-assist",
7
7
  "webrtc",
@@ -42,10 +42,10 @@
42
42
  "prepublishOnly": "bun run test && bun run build"
43
43
  },
44
44
  "peerDependencies": {
45
- "@oxvo/browser": ">=7.4.49"
45
+ "@oxvo/browser": ">=7.4.51"
46
46
  },
47
47
  "devDependencies": {
48
- "@oxvo/browser": "workspace:*",
48
+ "@oxvo/browser": "7.4.51",
49
49
  "@typescript-eslint/eslint-plugin": "^8.54.0",
50
50
  "@typescript-eslint/parser": "^8.54.0",
51
51
  "eslint": "^9.39.2",