@iblai/iblai-js 1.12.3 → 1.13.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.
@@ -3151,7 +3151,7 @@ const PRIVACY_LABELS = {
3151
3151
  * reach DOM that Radix renders outside the dialog subtree (popovers,
3152
3152
  * select options, etc.).
3153
3153
  */
3154
- function asPage$1(scope) {
3154
+ function asPage$3(scope) {
3155
3155
  return 'page' in scope ? scope.page() : scope;
3156
3156
  }
3157
3157
  /**
@@ -3235,7 +3235,7 @@ async function selectPrivacyAction(scope, action) {
3235
3235
  await trigger.click();
3236
3236
  // Radix Select renders options in a portal at the document root, so we
3237
3237
  // always look them up on the Page — never on the dialog Locator.
3238
- const option = asPage$1(scope).getByRole('option', {
3238
+ const option = asPage$3(scope).getByRole('option', {
3239
3239
  name: PRIVACY_LABELS.actionOptions[action],
3240
3240
  });
3241
3241
  await expect(option).toBeVisible({ timeout: 5000 });
@@ -3304,6 +3304,488 @@ async function expectOutputFilterEnabled(scope, enabled) {
3304
3304
  await expect(getOutputFilterSwitch(scope)).toHaveAttribute('aria-checked', String(enabled));
3305
3305
  }
3306
3306
 
3307
+ /**
3308
+ * Voice tab helpers — Playwright bindings for the `AgentVoiceTab` component
3309
+ * from `@iblai/web-containers`.
3310
+ *
3311
+ * The Voice tab contains two sub-tabs:
3312
+ * 1. "Voice" — provider + voice-picker, persisted via mentor settings
3313
+ * 2. "Call Configuration" — TTS / STT / LLM / video stack, persisted via the
3314
+ * `/call-configurations/` endpoint
3315
+ *
3316
+ * All UI strings mirror `AGENT_VOICE_TAB_LABELS` from
3317
+ * `@iblai/web-containers/next`. If a consumer renames a label via the
3318
+ * `labels` prop, override these constants per spec — don't edit this file.
3319
+ *
3320
+ * Selectors below use stable hooks only: `data-testid`, role + name, and
3321
+ * label-based queries. No CSS class selectors anywhere — the underlying
3322
+ * Tailwind classes are not part of the public contract.
3323
+ *
3324
+ * The helpers assume the Edit Mentor dialog is already open. Call
3325
+ * `switchToVoiceTab(page)` first; from then on every helper accepts either
3326
+ * the `Page` or the dialog `Locator`.
3327
+ */
3328
+ const VOICE_LABELS = {
3329
+ tabName: 'Voice',
3330
+ headerTitle: 'Voice',
3331
+ subTabs: {
3332
+ voice: 'Voice',
3333
+ callConfig: 'Voice call',
3334
+ },
3335
+ providers: {
3336
+ browser: 'Browser',
3337
+ openai: 'OpenAI',
3338
+ google: 'Google',
3339
+ },
3340
+ voicePickerLabel: {
3341
+ openai: 'OpenAI Voice',
3342
+ google: 'Google Voice',
3343
+ },
3344
+ saveVoiceButton: /save voice settings|saving…/i,
3345
+ // The "Save" button reads "Save" when no config exists yet and
3346
+ // "Save changes" once one does. Helpers match both via a regex.
3347
+ callConfigSaveCreate: 'Save',
3348
+ callConfigSaveUpdate: 'Save changes',
3349
+ callConfigResetButton: 'Reset',
3350
+ callConfigFields: {
3351
+ mode: 'Call style',
3352
+ language: 'Spoken language',
3353
+ llmProvider: 'AI provider',
3354
+ ttsProvider: 'Text-to-speech provider',
3355
+ sttProvider: 'Speech-to-text provider',
3356
+ useFunctionCalling: 'Look things up only when needed',
3357
+ enableVideo: 'Allow screen sharing on a call',
3358
+ },
3359
+ modeOptions: {
3360
+ realtime: 'Live conversation',
3361
+ inference: 'Step-by-step',
3362
+ },
3363
+ ttsOptions: {
3364
+ openai: 'OpenAI',
3365
+ google: 'Google',
3366
+ elevenlabs: 'ElevenLabs',
3367
+ },
3368
+ llmOptions: {
3369
+ openai: 'OpenAI',
3370
+ google: 'Google',
3371
+ anthropic: 'Anthropic',
3372
+ },
3373
+ };
3374
+ /**
3375
+ * Resolve a Page from either a Page or a Locator. Used when we need to
3376
+ * reach DOM that Radix renders outside the dialog subtree (popovers,
3377
+ * select options, etc.).
3378
+ */
3379
+ function asPage$2(scope) {
3380
+ return 'page' in scope ? scope.page() : scope;
3381
+ }
3382
+ // ─── Tab navigation ──────────────────────────────────────────────────────
3383
+ /**
3384
+ * Check whether the Voice tab is currently rendered in the Edit Mentor
3385
+ * dialog. Returns false instead of throwing so consumers can guard
3386
+ * conditionally rendered tabs.
3387
+ */
3388
+ async function isVoiceTabVisible(page) {
3389
+ const tab = page.getByRole('tab', { name: VOICE_LABELS.tabName, exact: true });
3390
+ try {
3391
+ await expect(tab).toBeVisible({ timeout: 5000 });
3392
+ return true;
3393
+ }
3394
+ catch (_a) {
3395
+ return false;
3396
+ }
3397
+ }
3398
+ /**
3399
+ * Switch to the Voice tab. Assumes the Edit Mentor dialog is open.
3400
+ */
3401
+ async function switchToVoiceTab(page) {
3402
+ const tab = page.getByRole('tab', { name: VOICE_LABELS.tabName, exact: true });
3403
+ await expect(tab).toBeVisible({ timeout: 10000 });
3404
+ await tab.click();
3405
+ // The segmented sub-tab control is unique to the Voice tab; its presence
3406
+ // confirms we landed on the right pane.
3407
+ await expect(page.getByTestId('voice-sub-tabs')).toBeVisible({ timeout: 10000 });
3408
+ logger.info('Switched to Voice tab');
3409
+ }
3410
+ /**
3411
+ * Switch between the Voice / Voice call sub-tabs.
3412
+ *
3413
+ * Note: "Screen share" used to live here as a third sub-tab but is now
3414
+ * a peer top-level tab in the edit-mentor modal. Use the helpers in
3415
+ * `screenshare-tab-helpers.ts` for it.
3416
+ */
3417
+ async function switchToVoiceSubTab(scope, sub) {
3418
+ const testId = sub === 'voice' ? 'voice-sub-tab-voice' : 'voice-sub-tab-call-config';
3419
+ const pill = scope.getByTestId(testId);
3420
+ await expect(pill).toBeVisible({ timeout: 10000 });
3421
+ await pill.click();
3422
+ await expect(pill).toHaveAttribute('aria-selected', 'true', { timeout: 10000 });
3423
+ logger.info(`Switched to "${VOICE_LABELS.subTabs[sub]}" sub-tab`);
3424
+ }
3425
+ // ─── Mentor voice section ────────────────────────────────────────────────
3426
+ /**
3427
+ * Locator for one of the three provider cards on the "Voice" sub-tab.
3428
+ */
3429
+ function getVoiceProviderCard(scope, provider) {
3430
+ return scope.getByTestId(`voice-provider-${provider}`);
3431
+ }
3432
+ /**
3433
+ * Click a provider card. The picker below changes shape based on the
3434
+ * selection (Browser → no picker; OpenAI / Google → searchable voice list).
3435
+ */
3436
+ async function selectVoiceProvider(scope, provider) {
3437
+ const card = getVoiceProviderCard(scope, provider);
3438
+ await expect(card).toBeVisible({ timeout: 10000 });
3439
+ await card.click();
3440
+ await expect(card).toHaveAttribute('aria-checked', 'true', { timeout: 10000 });
3441
+ logger.info(`Voice provider set to "${provider}"`);
3442
+ }
3443
+ async function expectVoiceProviderSelected(scope, provider) {
3444
+ await expect(getVoiceProviderCard(scope, provider)).toHaveAttribute('aria-checked', 'true');
3445
+ }
3446
+ /**
3447
+ * Open the modal voice picker for the active mentor-voice provider
3448
+ * (OpenAI or Google). The trigger renders the currently-picked voice on
3449
+ * the Voice sub-tab; clicking its "open" button opens a Dialog with
3450
+ * the searchable + paginated picker inside.
3451
+ *
3452
+ * The trigger root container exposes `data-testid="mentor-voice-trigger"`,
3453
+ * and the open button (inside it) is `mentor-voice-trigger-open`. The
3454
+ * inline play button is `mentor-voice-trigger-preview` — see
3455
+ * `previewMentorVoiceInline`.
3456
+ */
3457
+ async function openMentorVoicePicker(scope) {
3458
+ const openBtn = scope.getByTestId('mentor-voice-trigger-open');
3459
+ await expect(openBtn).toBeVisible({ timeout: 10000 });
3460
+ await openBtn.click();
3461
+ await expect(asPage$2(scope).getByTestId('voice-picker-modal')).toBeVisible({
3462
+ timeout: 10000,
3463
+ });
3464
+ }
3465
+ /**
3466
+ * Open the modal voice picker inside the Call Configuration sub-tab.
3467
+ * Only present when an LLM provider has been chosen.
3468
+ */
3469
+ async function openCallConfigVoicePicker(scope) {
3470
+ const openBtn = scope.getByTestId('call-config-voice-trigger-open');
3471
+ await expect(openBtn).toBeVisible({ timeout: 10000 });
3472
+ await openBtn.click();
3473
+ await expect(asPage$2(scope).getByTestId('voice-picker-modal')).toBeVisible({
3474
+ timeout: 10000,
3475
+ });
3476
+ }
3477
+ /**
3478
+ * Click the inline Play button on the mentor-voice trigger to preview
3479
+ * the currently-selected voice without opening the modal. Audio
3480
+ * playback isn't deterministically observable from Playwright, so this
3481
+ * just clicks and returns.
3482
+ */
3483
+ async function previewMentorVoiceInline(scope) {
3484
+ const previewBtn = scope.getByTestId('mentor-voice-trigger-preview');
3485
+ await expect(previewBtn).toBeVisible({ timeout: 10000 });
3486
+ await previewBtn.click();
3487
+ logger.info('Previewed mentor voice (inline)');
3488
+ }
3489
+ /**
3490
+ * Same as `previewMentorVoiceInline`, but for the Call Configuration
3491
+ * voice trigger.
3492
+ */
3493
+ async function previewCallConfigVoiceInline(scope) {
3494
+ const previewBtn = scope.getByTestId('call-config-voice-trigger-preview');
3495
+ await expect(previewBtn).toBeVisible({ timeout: 10000 });
3496
+ await previewBtn.click();
3497
+ logger.info('Previewed call-config voice (inline)');
3498
+ }
3499
+ /**
3500
+ * Assert that the trigger shows the given voice name (title-cased).
3501
+ * Useful for verifying that a previously-saved voice loads correctly.
3502
+ */
3503
+ async function expectMentorVoiceTriggerShows(scope, voiceName) {
3504
+ await expect(scope.getByTestId('mentor-voice-trigger')).toContainText(voiceName);
3505
+ }
3506
+ async function expectCallConfigVoiceTriggerShows(scope, voiceName) {
3507
+ await expect(scope.getByTestId('call-config-voice-trigger')).toContainText(voiceName);
3508
+ }
3509
+ /**
3510
+ * Search the voice picker. Typing is debounced ~300ms inside the
3511
+ * component, so callers usually want to assert on the eventual results
3512
+ * (see `expectVoiceVisible`). When the picker lives inside the modal,
3513
+ * scope to the dialog: `searchVoices(page.getByTestId('voice-picker-modal'), 'al')`.
3514
+ *
3515
+ * Targets the input via `data-testid="voice-picker-search"` — a stable
3516
+ * hook that doesn't depend on the (overridable) placeholder copy and
3517
+ * avoids ReDoS-shaped placeholder regexes.
3518
+ */
3519
+ async function searchVoices(scope, query) {
3520
+ const input = scope.getByTestId('voice-picker-search');
3521
+ await expect(input).toBeVisible({ timeout: 10000 });
3522
+ await input.fill(query);
3523
+ }
3524
+ /**
3525
+ * Locator for an individual voice row in the picker. Rows are
3526
+ * `role="radio"` with `aria-label` matching the voice name.
3527
+ */
3528
+ function getVoiceRow(scope, voiceName) {
3529
+ return scope.getByRole('radio', { name: voiceName, exact: true });
3530
+ }
3531
+ async function expectVoiceVisible(scope, voiceName) {
3532
+ await expect(getVoiceRow(scope, voiceName)).toBeVisible({ timeout: 10000 });
3533
+ }
3534
+ /**
3535
+ * Click a voice row to select it. Returns once the row reports
3536
+ * aria-checked=true.
3537
+ */
3538
+ async function selectVoice(scope, voiceName) {
3539
+ const row = getVoiceRow(scope, voiceName);
3540
+ await expect(row).toBeVisible({ timeout: 10000 });
3541
+ await row.click();
3542
+ await expect(row).toHaveAttribute('aria-checked', 'true', { timeout: 10000 });
3543
+ logger.info(`Selected voice "${voiceName}"`);
3544
+ }
3545
+ /**
3546
+ * Click the inline preview button next to a voice. Returns immediately —
3547
+ * audio playback is async and not deterministically observable from
3548
+ * Playwright.
3549
+ */
3550
+ async function previewVoice(scope, voiceName) {
3551
+ const previewBtn = scope.getByRole('button', { name: `Preview ${voiceName} voice` });
3552
+ await expect(previewBtn).toBeVisible({ timeout: 10000 });
3553
+ await previewBtn.click();
3554
+ }
3555
+ /**
3556
+ * Click the Save button on the Voice sub-tab. Asserts the button is
3557
+ * enabled first (a no-op on a pristine form would be a test bug).
3558
+ */
3559
+ async function saveVoiceSettings(scope) {
3560
+ const btn = scope.getByTestId('voice-save-button');
3561
+ await expect(btn).toBeVisible({ timeout: 10000 });
3562
+ await expect(btn).toBeEnabled({ timeout: 10000 });
3563
+ await btn.click();
3564
+ logger.info('Clicked Save voice settings');
3565
+ }
3566
+ // ─── Call configuration section ──────────────────────────────────────────
3567
+ /**
3568
+ * Locator for the call configuration form, useful as a scope root for
3569
+ * subsequent helpers.
3570
+ */
3571
+ function getCallConfigForm(scope) {
3572
+ return scope.getByTestId('call-config-form');
3573
+ }
3574
+ async function expectCallConfigVisible(scope) {
3575
+ await expect(getCallConfigForm(scope)).toBeVisible({ timeout: 10000 });
3576
+ }
3577
+ /**
3578
+ * Pick a call mode (realtime/inference). The TTS/STT selects below become
3579
+ * disabled in realtime mode — assert via `expectTtsSelectDisabled` if your
3580
+ * test cares.
3581
+ */
3582
+ async function selectCallMode(scope, mode) {
3583
+ const trigger = scope.getByRole('combobox', { name: VOICE_LABELS.callConfigFields.mode });
3584
+ await expect(trigger).toBeVisible({ timeout: 10000 });
3585
+ await trigger.click();
3586
+ const option = asPage$2(scope).getByRole('option', { name: VOICE_LABELS.modeOptions[mode] });
3587
+ await expect(option).toBeVisible({ timeout: 5000 });
3588
+ await option.click();
3589
+ await expect(trigger).toHaveText(new RegExp(VOICE_LABELS.modeOptions[mode]));
3590
+ logger.info(`Call mode set to "${mode}"`);
3591
+ }
3592
+ async function setCallLanguage(scope, code) {
3593
+ const input = scope.getByLabel(VOICE_LABELS.callConfigFields.language);
3594
+ await expect(input).toBeVisible({ timeout: 10000 });
3595
+ await input.fill(code);
3596
+ await expect(input).toHaveValue(code);
3597
+ }
3598
+ async function selectFromCombobox(scope, triggerName, optionName) {
3599
+ const trigger = scope.getByRole('combobox', { name: triggerName });
3600
+ await expect(trigger).toBeVisible({ timeout: 10000 });
3601
+ await trigger.click();
3602
+ const option = asPage$2(scope).getByRole('option', { name: optionName });
3603
+ await expect(option).toBeVisible({ timeout: 5000 });
3604
+ await option.click();
3605
+ }
3606
+ async function selectLlmProvider(scope, provider) {
3607
+ await selectFromCombobox(scope, VOICE_LABELS.callConfigFields.llmProvider, VOICE_LABELS.llmOptions[provider]);
3608
+ logger.info(`Call LLM provider set to "${provider}"`);
3609
+ }
3610
+ async function selectTtsProvider(scope, provider) {
3611
+ await selectFromCombobox(scope, VOICE_LABELS.callConfigFields.ttsProvider, VOICE_LABELS.ttsOptions[provider]);
3612
+ logger.info(`Call TTS provider set to "${provider}"`);
3613
+ }
3614
+ async function selectSttProvider(scope, provider) {
3615
+ await selectFromCombobox(scope, VOICE_LABELS.callConfigFields.sttProvider, VOICE_LABELS.ttsOptions[provider]);
3616
+ logger.info(`Call STT provider set to "${provider}"`);
3617
+ }
3618
+ async function expectTtsSelectDisabled(scope) {
3619
+ const trigger = scope.getByRole('combobox', {
3620
+ name: VOICE_LABELS.callConfigFields.ttsProvider,
3621
+ });
3622
+ await expect(trigger).toBeDisabled();
3623
+ }
3624
+ async function expectSttSelectDisabled(scope) {
3625
+ const trigger = scope.getByRole('combobox', {
3626
+ name: VOICE_LABELS.callConfigFields.sttProvider,
3627
+ });
3628
+ await expect(trigger).toBeDisabled();
3629
+ }
3630
+ /**
3631
+ * Generic toggle helper for the call-config switches (functionCalling /
3632
+ * enableVideo). The switch is `role="switch"`; its name has the
3633
+ * pattern `<label> enabled|disabled`.
3634
+ */
3635
+ async function toggleCallSwitch(scope, label, enabled) {
3636
+ const escapedLabel = label.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
3637
+ const toggle = scope.getByRole('switch', {
3638
+ name: new RegExp(`^${escapedLabel} (enabled|disabled)$`),
3639
+ });
3640
+ await expect(toggle).toBeVisible({ timeout: 10000 });
3641
+ const isChecked = (await toggle.getAttribute('aria-checked')) === 'true';
3642
+ if (isChecked !== enabled) {
3643
+ await toggle.click();
3644
+ await expect(toggle).toHaveAttribute('aria-checked', String(enabled), {
3645
+ timeout: 10000,
3646
+ });
3647
+ }
3648
+ }
3649
+ async function setUseFunctionCallingEnabled(scope, enabled) {
3650
+ await toggleCallSwitch(scope, VOICE_LABELS.callConfigFields.useFunctionCalling, enabled);
3651
+ logger.info(`Function calling for RAG set to ${enabled ? 'enabled' : 'disabled'}`);
3652
+ }
3653
+ async function setEnableVideo(scope, enabled) {
3654
+ await toggleCallSwitch(scope, VOICE_LABELS.callConfigFields.enableVideo, enabled);
3655
+ logger.info(`Call video set to ${enabled ? 'enabled' : 'disabled'}`);
3656
+ }
3657
+ // Note: screensharing prompts moved to a standalone top-level tab —
3658
+ // see `screenshare-tab-helpers.ts` for editor / save helpers.
3659
+ /**
3660
+ * Click the call-config Save button. The label switches between
3661
+ * "Create configuration" and "Save changes" depending on whether a config
3662
+ * exists; we match either.
3663
+ */
3664
+ async function saveCallConfig(scope) {
3665
+ const btn = scope.getByRole('button', {
3666
+ name: new RegExp(`${VOICE_LABELS.callConfigSaveCreate}|${VOICE_LABELS.callConfigSaveUpdate}`, 'i'),
3667
+ });
3668
+ await expect(btn).toBeVisible({ timeout: 10000 });
3669
+ await expect(btn).toBeEnabled({ timeout: 10000 });
3670
+ await btn.click();
3671
+ logger.info('Clicked Save call configuration');
3672
+ }
3673
+ async function resetCallConfig(scope) {
3674
+ const btn = scope.getByRole('button', { name: VOICE_LABELS.callConfigResetButton });
3675
+ await expect(btn).toBeVisible({ timeout: 10000 });
3676
+ await btn.click();
3677
+ }
3678
+
3679
+ /**
3680
+ * Screen share tab helpers — Playwright bindings for the standalone
3681
+ * `AgentScreenShareTab` component from `@iblai/web-containers`.
3682
+ *
3683
+ * The tab is a peer to Voice/LLM/Tools/etc in the edit-mentor modal.
3684
+ * It only edits the two screensharing prompts on the mentor's
3685
+ * CallConfiguration; the on/off toggle lives on the Settings tab.
3686
+ *
3687
+ * Selectors below use stable hooks only: `data-testid`, role + name,
3688
+ * and label-based queries. No CSS class selectors.
3689
+ */
3690
+ const SCREENSHARE_LABELS = {
3691
+ tabName: 'Screen Share',
3692
+ headerTitle: 'Screen Share',
3693
+ fields: {
3694
+ systemPrompt: 'Instructions during screen sharing',
3695
+ proactivePrompt: 'What the agent says when screen sharing starts',
3696
+ },
3697
+ saveButton: 'Save',
3698
+ };
3699
+ function asPage$1(scope) {
3700
+ return 'page' in scope ? scope.page() : scope;
3701
+ }
3702
+ /**
3703
+ * Returns false if the Screen share tab isn't currently rendered in
3704
+ * the Edit Mentor dialog (e.g. the host hasn't registered it, or it's
3705
+ * hidden by RBAC).
3706
+ */
3707
+ async function isScreenShareTabVisible(page) {
3708
+ const tab = page.getByRole('tab', { name: SCREENSHARE_LABELS.tabName, exact: true });
3709
+ try {
3710
+ await expect(tab).toBeVisible({ timeout: 5000 });
3711
+ return true;
3712
+ }
3713
+ catch (_a) {
3714
+ return false;
3715
+ }
3716
+ }
3717
+ /**
3718
+ * Switch to the Screen share top-level tab. Assumes the Edit Mentor
3719
+ * dialog is open.
3720
+ */
3721
+ async function switchToScreenShareTab(page) {
3722
+ const tab = page.getByRole('tab', { name: SCREENSHARE_LABELS.tabName, exact: true });
3723
+ await expect(tab).toBeVisible({ timeout: 10000 });
3724
+ await tab.click();
3725
+ // The tab body's testid confirms we landed on the right pane.
3726
+ await expect(page.getByTestId('screenshare-tab-body')).toBeVisible({ timeout: 10000 });
3727
+ logger.info('Switched to Screen share tab');
3728
+ }
3729
+ /**
3730
+ * Open the inline prompt editor for one of the two screensharing
3731
+ * prompts. Mirrors the Prompts tab pattern: each prompt is rendered as
3732
+ * a card with an "Edit <label>" button that pops the rich-text modal.
3733
+ */
3734
+ async function openScreenSharePromptEditor(scope, field) {
3735
+ const label = SCREENSHARE_LABELS.fields[field];
3736
+ const btn = scope.getByRole('button', { name: `Edit ${label}` });
3737
+ await expect(btn).toBeVisible({ timeout: 10000 });
3738
+ await btn.click();
3739
+ await expect(asPage$1(scope).getByText(`Edit ${label}`)).toBeVisible({ timeout: 10000 });
3740
+ logger.info(`Opened screen-share ${field} editor`);
3741
+ }
3742
+ /**
3743
+ * Replace the prompt text inside the rich-text modal and confirm. The
3744
+ * new value is written to local form state; call `saveScreenSharePrompts`
3745
+ * afterwards to persist.
3746
+ */
3747
+ async function setScreenSharePrompt(scope, field, text) {
3748
+ await openScreenSharePromptEditor(scope, field);
3749
+ const page = asPage$1(scope);
3750
+ const editor = page.getByRole('dialog').locator('[contenteditable="true"]').first();
3751
+ await expect(editor).toBeVisible({ timeout: 10000 });
3752
+ await editor.click();
3753
+ await page.keyboard.press('ControlOrMeta+A');
3754
+ await page.keyboard.press('Delete');
3755
+ await editor.pressSequentially(text);
3756
+ // The modal's Save button — scope to dialog so we don't grab the
3757
+ // outer tab's Save.
3758
+ await page.getByRole('dialog').getByRole('button', { name: /^save$/i }).click();
3759
+ await expect(page.getByRole('dialog')).toBeHidden({ timeout: 10000 });
3760
+ logger.info(`Set screen-share ${field}`);
3761
+ }
3762
+ /**
3763
+ * Click the tab's Save button to persist any pending prompt edits.
3764
+ * Asserts the button is enabled first — a no-op call on a pristine
3765
+ * tab would be a test bug.
3766
+ */
3767
+ async function saveScreenSharePrompts(scope) {
3768
+ const btn = scope.getByTestId('screenshare-save-button');
3769
+ await expect(btn).toBeVisible({ timeout: 10000 });
3770
+ await expect(btn).toBeEnabled({ timeout: 10000 });
3771
+ await btn.click();
3772
+ logger.info('Clicked Save on Screen share tab');
3773
+ }
3774
+ /**
3775
+ * Assert whether the amber off-state hint is shown. The hint renders
3776
+ * when `enable_video` is false on the underlying CallConfiguration
3777
+ * (or when no call_configuration exists yet).
3778
+ */
3779
+ async function expectScreenShareDisabledHint(scope, visible) {
3780
+ const hint = scope.getByTestId('screenshare-disabled-hint');
3781
+ if (visible) {
3782
+ await expect(hint).toBeVisible({ timeout: 10000 });
3783
+ }
3784
+ else {
3785
+ await expect(hint).toBeHidden();
3786
+ }
3787
+ }
3788
+
3307
3789
  const DEFAULT_TIMEOUT = 10000;
3308
3790
  /** Locator for the Plan section card on the BillingTab. */
3309
3791
  function billingPlanSection(page) {
@@ -3921,5 +4403,5 @@ function createPlaywrightConfig(options) {
3921
4403
  });
3922
4404
  }
3923
4405
 
3924
- export { AuthFlowBuilder, CustomReporter, MailsacClient, PRIVACY_LABELS, TASKS_LABELS, addMemory, archiveFirstMemory, archiveMemoryByContent, billingAutoRechargeSection, billingCreditsSection, billingPlanSection, buildReportUrl, canChatWithEmbedMentor, cancelDeleteInstance, cancelDeleteSkill, cancelDisconnectInstance, cancelNewInstanceDialog, checkAdminStatus, clearDateRangeFilter, clearInstanceSearch, clickBackHome, clickBillingAddCredits, clickBillingManageBilling, clickBillingManageUsage, clickBillingUpgrade, clickDownloadAgain, clickManualDownloadLink, closeCreditBalanceDropdown, closeWithEsc, connectToInstance, createAuthSetup, createEnvConfig, createInstance, createPlaywrightConfig, createSkill, creditBalancePanel, creditBalancePlanBadge, creditBalanceTrigger, deleteFirstMemory, deleteInstance, deleteMemoryByContent, deleteSkill, deleteTask, disableSkill, disconnectInstance, editAgentPrompt, editInstance, editSkill, enableSkill, expectBillingAutoRechargeSection, expectBillingCreditsSection, expectBillingPlanSection, expectBillingTabForCurrentPlan, expectBillingTabForFreePlan, expectBillingTabForPremiumPlan, expectBillingTabForTrialPlan, expectCompletedTasks, expectCreditBalanceForCurrentPlan, expectCreditBalancePanelForFreePlan, expectCreditBalancePanelForPremiumPlan, expectCreditBalancePanelForTrialPlan, expectCreditBalanceVisibilityForTenant, expectEntitySelected, expectFailedTasks, expectLogDetailsStatus, expectLogsForTask, expectNoAccessibilityViolations, expectNoAccessibilityViolationsOnDialogs, expectNoLogsForSelectedTask, expectOutputFilterEnabled, expectPrivacyFieldsHidden, expectPrivacyFieldsVisible, expectPrivacyRouterEnabled, expectScheduleStartTimeInPastError, expectTaskInList, expectTaskNotInList, expectTaskStatus, expectTasksEmpty, expectTotalTasks, filterByAction, filterByActionAndVerify, filterByActor, filterByActorAndVerify, filterByDateRange, generateBrowserSetupProjects, generateProjectConfig, getAuditLogRowCount, getAvailableActors, getBillingAutoRechargeStatus, getBillingPlanLabel, getBrowserKey, getCreditBalancePlanLabel, getCreditBalanceRemaining, getCurrentModel, getCurrentTenantShowPaywall, getEntityChip, getInstanceHealthLabel, getInstanceRowCount, getInstanceStatusLabel, getMemoryCount, getMentorIdFromUrl, getOutputFilterSwitch, getPaginationInfo, getPrivacyRouterSwitch, getScheduleTaskButton, getSearchInput, getSkillRowCount, getTaskRow, goToFirstPage, goToLastPage, goToNextPage, goToPage, goToPreviousPage, inviteUserTest, isFirefox, isJSON, isMemoryTabVisible, isOnFirstPage, isOnLastPage, isPrivacyTabVisible, isSandboxTabVisible, isSkillEnabled, isTasksTabVisible, logger, loginWithEmailAndPassword, loginWithMicrosoftIdp, navigateToAccountComponent, navigateToAuditLog, navigateToAuditLogAndWaitForData, navigateToDataReports, navigateToReportDownload, openAddMemoryDialog, openAgentPromptEditModal, openCreditBalanceDropdown, openEditInstanceDialog, openEditSkillDialog, openFirstLogDetails, openInstanceActionsMenu, openLLMProviderPicker, openNewInstanceDialog, openNewSkillDialog, openScheduleTaskDialog, openSkillActionsMenu, parseReportUrlParams, pushConfiguration, reliableClick, reliableFill, retry, runConnectedInstanceChecks, runInstanceChecks, safeWaitForURL, scheduleTask, searchInstances, searchTasks, selectDateFromCalendar, selectLLMModel, selectPrivacyAction, selectTaskInList, setBlockMessage, setEntitySelected, setOutputFilterEnabled, setPrivacyRouterEnabled, setupSandboxInstance, shouldAddNewRowWhenClickingAddRowButton, shouldAllowEditingCellValuesInCSVEditor, shouldCancelCombiningReports, shouldCloseCSVEditorWhenClickingCloseButton, shouldCloseCSVEditorWithoutSavingWhenClickingCancel, shouldCombineRecommendationReports, shouldDirectlyDownloadChatHistoryReportWithoutCSVEditor, shouldDisableOtherDownloadButtonsWhileGeneratingReport, shouldDisplayCSVInEditableTableFormat, shouldDisplayReportCards, shouldHaveCombinedReportDataTestIds, shouldOpenCSVEditorDialog, shouldOpenCSVEditorForUserMetadataReport, shouldSaveEditedCSVAndTriggerDownload, shouldShowCombiningReportsDialog, shouldVerifyCSVEditorDialogAccessibility, signUpWithEmailAndPassword, switchToMemoryTab, switchToPrivacyTab, switchToSandboxTab, switchToSkillsTab, switchToTasksTab, teardownSandboxInstance, test, toggleAutoPush, toggleMemorySwitch, toggleSkill, verifyAgentConfigPromptsVisible, verifyAuditLogEmptyState, verifyAuditLogEntryStructure, verifyAuditLogGenericError, verifyAuditLogLoading, verifyAuditLogPermissionError, verifyAuditLogTableVisible, verifyConnectDisabledForUnhealthy, verifyConnectedInstanceCard, verifyCurrentPage, verifyDonePhase, verifyDownloadingPhase, verifyErrorPhase, verifyInstanceTableEmpty, verifyInstanceTableVisible, verifyMemoryExists, verifyMemoryNotExists, verifyMemoryTabMemoriesList, verifyMemoryTabSettings, verifyPreparingPhase, verifySkillVisible, verifySkillsEmptyState, verifySkillsTabVisible, waitForAuditLogDataLoaded, waitForBillingTabReady, waitForCreditBalanceLoaded, waitForDialogReady, waitForElementStable, waitForPageLoad, waitForPageReady, waitForReportDownload };
4406
+ export { AuthFlowBuilder, CustomReporter, MailsacClient, PRIVACY_LABELS, SCREENSHARE_LABELS, TASKS_LABELS, VOICE_LABELS, addMemory, archiveFirstMemory, archiveMemoryByContent, billingAutoRechargeSection, billingCreditsSection, billingPlanSection, buildReportUrl, canChatWithEmbedMentor, cancelDeleteInstance, cancelDeleteSkill, cancelDisconnectInstance, cancelNewInstanceDialog, checkAdminStatus, clearDateRangeFilter, clearInstanceSearch, clickBackHome, clickBillingAddCredits, clickBillingManageBilling, clickBillingManageUsage, clickBillingUpgrade, clickDownloadAgain, clickManualDownloadLink, closeCreditBalanceDropdown, closeWithEsc, connectToInstance, createAuthSetup, createEnvConfig, createInstance, createPlaywrightConfig, createSkill, creditBalancePanel, creditBalancePlanBadge, creditBalanceTrigger, deleteFirstMemory, deleteInstance, deleteMemoryByContent, deleteSkill, deleteTask, disableSkill, disconnectInstance, editAgentPrompt, editInstance, editSkill, enableSkill, expectBillingAutoRechargeSection, expectBillingCreditsSection, expectBillingPlanSection, expectBillingTabForCurrentPlan, expectBillingTabForFreePlan, expectBillingTabForPremiumPlan, expectBillingTabForTrialPlan, expectCallConfigVisible, expectCallConfigVoiceTriggerShows, expectCompletedTasks, expectCreditBalanceForCurrentPlan, expectCreditBalancePanelForFreePlan, expectCreditBalancePanelForPremiumPlan, expectCreditBalancePanelForTrialPlan, expectCreditBalanceVisibilityForTenant, expectEntitySelected, expectFailedTasks, expectLogDetailsStatus, expectLogsForTask, expectMentorVoiceTriggerShows, expectNoAccessibilityViolations, expectNoAccessibilityViolationsOnDialogs, expectNoLogsForSelectedTask, expectOutputFilterEnabled, expectPrivacyFieldsHidden, expectPrivacyFieldsVisible, expectPrivacyRouterEnabled, expectScheduleStartTimeInPastError, expectScreenShareDisabledHint, expectSttSelectDisabled, expectTaskInList, expectTaskNotInList, expectTaskStatus, expectTasksEmpty, expectTotalTasks, expectTtsSelectDisabled, expectVoiceProviderSelected, expectVoiceVisible, filterByAction, filterByActionAndVerify, filterByActor, filterByActorAndVerify, filterByDateRange, generateBrowserSetupProjects, generateProjectConfig, getAuditLogRowCount, getAvailableActors, getBillingAutoRechargeStatus, getBillingPlanLabel, getBrowserKey, getCallConfigForm, getCreditBalancePlanLabel, getCreditBalanceRemaining, getCurrentModel, getCurrentTenantShowPaywall, getEntityChip, getInstanceHealthLabel, getInstanceRowCount, getInstanceStatusLabel, getMemoryCount, getMentorIdFromUrl, getOutputFilterSwitch, getPaginationInfo, getPrivacyRouterSwitch, getScheduleTaskButton, getSearchInput, getSkillRowCount, getTaskRow, getVoiceProviderCard, getVoiceRow, goToFirstPage, goToLastPage, goToNextPage, goToPage, goToPreviousPage, inviteUserTest, isFirefox, isJSON, isMemoryTabVisible, isOnFirstPage, isOnLastPage, isPrivacyTabVisible, isSandboxTabVisible, isScreenShareTabVisible, isSkillEnabled, isTasksTabVisible, isVoiceTabVisible, logger, loginWithEmailAndPassword, loginWithMicrosoftIdp, navigateToAccountComponent, navigateToAuditLog, navigateToAuditLogAndWaitForData, navigateToDataReports, navigateToReportDownload, openAddMemoryDialog, openAgentPromptEditModal, openCallConfigVoicePicker, openCreditBalanceDropdown, openEditInstanceDialog, openEditSkillDialog, openFirstLogDetails, openInstanceActionsMenu, openLLMProviderPicker, openMentorVoicePicker, openNewInstanceDialog, openNewSkillDialog, openScheduleTaskDialog, openScreenSharePromptEditor, openSkillActionsMenu, parseReportUrlParams, previewCallConfigVoiceInline, previewMentorVoiceInline, previewVoice, pushConfiguration, reliableClick, reliableFill, resetCallConfig, retry, runConnectedInstanceChecks, runInstanceChecks, safeWaitForURL, saveCallConfig, saveScreenSharePrompts, saveVoiceSettings, scheduleTask, searchInstances, searchTasks, searchVoices, selectCallMode, selectDateFromCalendar, selectLLMModel, selectLlmProvider, selectPrivacyAction, selectSttProvider, selectTaskInList, selectTtsProvider, selectVoice, selectVoiceProvider, setBlockMessage, setCallLanguage, setEnableVideo, setEntitySelected, setOutputFilterEnabled, setPrivacyRouterEnabled, setScreenSharePrompt, setUseFunctionCallingEnabled, setupSandboxInstance, shouldAddNewRowWhenClickingAddRowButton, shouldAllowEditingCellValuesInCSVEditor, shouldCancelCombiningReports, shouldCloseCSVEditorWhenClickingCloseButton, shouldCloseCSVEditorWithoutSavingWhenClickingCancel, shouldCombineRecommendationReports, shouldDirectlyDownloadChatHistoryReportWithoutCSVEditor, shouldDisableOtherDownloadButtonsWhileGeneratingReport, shouldDisplayCSVInEditableTableFormat, shouldDisplayReportCards, shouldHaveCombinedReportDataTestIds, shouldOpenCSVEditorDialog, shouldOpenCSVEditorForUserMetadataReport, shouldSaveEditedCSVAndTriggerDownload, shouldShowCombiningReportsDialog, shouldVerifyCSVEditorDialogAccessibility, signUpWithEmailAndPassword, switchToMemoryTab, switchToPrivacyTab, switchToSandboxTab, switchToScreenShareTab, switchToSkillsTab, switchToTasksTab, switchToVoiceSubTab, switchToVoiceTab, teardownSandboxInstance, test, toggleAutoPush, toggleMemorySwitch, toggleSkill, verifyAgentConfigPromptsVisible, verifyAuditLogEmptyState, verifyAuditLogEntryStructure, verifyAuditLogGenericError, verifyAuditLogLoading, verifyAuditLogPermissionError, verifyAuditLogTableVisible, verifyConnectDisabledForUnhealthy, verifyConnectedInstanceCard, verifyCurrentPage, verifyDonePhase, verifyDownloadingPhase, verifyErrorPhase, verifyInstanceTableEmpty, verifyInstanceTableVisible, verifyMemoryExists, verifyMemoryNotExists, verifyMemoryTabMemoriesList, verifyMemoryTabSettings, verifyPreparingPhase, verifySkillVisible, verifySkillsEmptyState, verifySkillsTabVisible, waitForAuditLogDataLoaded, waitForBillingTabReady, waitForCreditBalanceLoaded, waitForDialogReady, waitForElementStable, waitForPageLoad, waitForPageReady, waitForReportDownload };
3925
4407
  //# sourceMappingURL=index.esm.js.map