@iblai/iblai-js 1.19.0 → 1.19.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.
@@ -0,0 +1,127 @@
1
+ import { Locator, Page } from '@playwright/test';
2
+ /**
3
+ * Chat-privacy Playwright bindings.
4
+ *
5
+ * Three surfaces, three groups of helpers:
6
+ *
7
+ * 1. **Header toggle** — the nav-bar `ChatPrivacyToggle` (`<HatGlasses />`
8
+ * icon-button that morphs into the "Private Mode" pill).
9
+ * 2. **User profile** — the "Private Mode" tab in `UserProfileModal`
10
+ * (three radio-card options: Normal / Anonymized / Disabled).
11
+ * 3. **Tenant admin** — the "Allow users to control chat privacy" row in
12
+ * the Advanced settings tab (`<Switch>`).
13
+ *
14
+ * All selectors target stable hooks — primarily `data-testid` plus the
15
+ * `data-state` / `data-source` / `aria-pressed` attributes the components
16
+ * already expose. Avoid asserting on visible copy directly (it changes);
17
+ * the label maps below are kept for readable assertions on the rare cases
18
+ * where copy IS the thing we want to lock.
19
+ */
20
+ export declare const CHAT_PRIVACY_LABELS: {
21
+ readonly toggle: {
22
+ /** Title of the mid-session confirmation dialog. */
23
+ readonly dialogTitle: "Enable Private Mode for this chat?";
24
+ /** Label on the dialog's confirm button (matches both idle + busy state via regex). */
25
+ readonly dialogConfirm: RegExp;
26
+ };
27
+ readonly profileTab: {
28
+ /** Tab name in the user profile modal sidebar. */
29
+ readonly tabName: "Private Mode";
30
+ /** Section heading inside the tab body. */
31
+ readonly sectionHeading: "Default Private Mode";
32
+ /** Label text on each radio card. Kept in case the test wants to assert
33
+ * copy explicitly — the click + read paths use the testid instead. */
34
+ readonly cards: {
35
+ readonly normal: "Normal";
36
+ readonly anonymized: "Anonymized";
37
+ readonly disabled: "Disabled";
38
+ };
39
+ };
40
+ readonly tenantToggle: {
41
+ /** Visible row label. */
42
+ readonly rowLabel: "Allow users to control chat privacy";
43
+ };
44
+ };
45
+ /** The four `effective.source` values the chat-privacy resolver returns. */
46
+ export type ChatPrivacySource = 'mentor' | 'tenant' | 'user' | 'session' | 'default';
47
+ /** The user-pickable modes on the profile tab. */
48
+ export type ChatPrivacyMode = 'normal' | 'anonymized' | 'disabled';
49
+ /** `data-state` on the header toggle. */
50
+ export type ChatPrivacyToggleState = 'on' | 'off';
51
+ /**
52
+ * Locator for the nav-bar Private-Mode toggle. The toggle is hidden below
53
+ * the `md` breakpoint — assertions in mobile contexts should use
54
+ * `expectChatPrivacyToggleVisible(scope, false)`.
55
+ */
56
+ export declare function getChatPrivacyToggle(scope: Page | Locator): Locator;
57
+ export declare function expectChatPrivacyToggleVisible(scope: Page | Locator, visible: boolean): Promise<void>;
58
+ /**
59
+ * Assert the toggle is on / off via `data-state` (driven by the resolved
60
+ * effective mode + the local optimistic overlay).
61
+ */
62
+ export declare function expectChatPrivacyState(scope: Page | Locator, state: ChatPrivacyToggleState): Promise<void>;
63
+ /**
64
+ * Assert the resolved precedence-source via `data-source` (mentor / tenant
65
+ * / user / session / default). Useful for verifying the chat-privacy-
66
+ * effective endpoint resolved to the expected tier.
67
+ */
68
+ export declare function expectChatPrivacySource(scope: Page | Locator, source: ChatPrivacySource): Promise<void>;
69
+ /**
70
+ * Lock state is signalled via `aria-disabled` (the component uses
71
+ * `aria-disabled` instead of native `disabled` so the source-aware tooltip
72
+ * stays reachable on the locked pill).
73
+ */
74
+ export declare function expectChatPrivacyLocked(scope: Page | Locator, locked: boolean): Promise<void>;
75
+ /**
76
+ * Click the toggle. Covers two scenarios:
77
+ * - Off → on (with no user messages) → starts a fresh private session.
78
+ * - On (user-initiated, no user messages) → starts a fresh normal session.
79
+ * Use `confirmEnableChatPrivacyMidSession` for the mid-conversation flow.
80
+ */
81
+ export declare function clickChatPrivacyToggle(scope: Page | Locator): Promise<void>;
82
+ /** Locator for the AlertDialog content that the mid-session enable opens. */
83
+ export declare function getChatPrivacyConfirmDialog(scope: Page | Locator): Locator;
84
+ export declare function expectChatPrivacyConfirmDialogOpen(scope: Page | Locator, open: boolean): Promise<void>;
85
+ /**
86
+ * Mid-conversation enable flow:
87
+ * 1. Click the toggle (opens the confirm dialog).
88
+ * 2. Click the "Enable Private Mode" action.
89
+ * The session-level disable-chathistory POST is one-way per spec; the
90
+ * helper resolves once the dialog closes after a successful confirm.
91
+ */
92
+ export declare function confirmEnableChatPrivacyMidSession(scope: Page | Locator): Promise<void>;
93
+ /** Cancel the confirm dialog without enabling. */
94
+ export declare function cancelEnableChatPrivacyMidSession(scope: Page | Locator): Promise<void>;
95
+ /**
96
+ * Switch to the Private Mode tab inside the user profile modal. Assumes
97
+ * the modal is already open. The tab only appears when the tenant has
98
+ * `allow_user_chat_privacy_control` on, so a `false` return from
99
+ * `isPrivateModeTabVisible` is a meaningful state, not an error.
100
+ */
101
+ export declare function isPrivateModeTabVisible(page: Page): Promise<boolean>;
102
+ export declare function switchToPrivateModeTab(page: Page): Promise<void>;
103
+ /** Locator for one of the three radio cards. */
104
+ export declare function getPrivateModeCard(scope: Page | Locator, mode: ChatPrivacyMode): Locator;
105
+ /**
106
+ * Pick a mode by clicking its card. Resolves once the card flips into
107
+ * the selected state via `aria-pressed="true"` — i.e. the mutation came
108
+ * back and the local pending state caught up.
109
+ */
110
+ export declare function selectPrivateMode(scope: Page | Locator, mode: ChatPrivacyMode): Promise<void>;
111
+ /** Assert which mode is currently selected. Uses `aria-pressed` + `data-state`. */
112
+ export declare function expectPrivateModeSelected(scope: Page | Locator, mode: ChatPrivacyMode): Promise<void>;
113
+ /** Assert every card is rendered. Useful as a "tab loaded" sanity check. */
114
+ export declare function expectPrivateModeTabReady(scope: Page | Locator): Promise<void>;
115
+ /** Locator for the tenant-wide "Allow users to control chat privacy" switch. */
116
+ export declare function getTenantChatPrivacySwitch(scope: Page | Locator): Locator;
117
+ /** Locator for the surrounding row (useful for scoping label / spinner queries). */
118
+ export declare function getTenantChatPrivacyRow(scope: Page | Locator): Locator;
119
+ export declare function expectTenantChatPrivacyVisible(scope: Page | Locator, visible: boolean): Promise<void>;
120
+ /** Assert the tenant gate's current state via `aria-checked` on the Radix switch. */
121
+ export declare function expectTenantChatPrivacyEnabled(scope: Page | Locator, enabled: boolean): Promise<void>;
122
+ /**
123
+ * Flip the tenant gate to the desired state. Idempotent — does nothing if
124
+ * the switch is already in the target state. Backend rejects this for
125
+ * non-admins (403) and the component surfaces an error toast in that case.
126
+ */
127
+ export declare function setTenantChatPrivacyEnabled(scope: Page | Locator, enabled: boolean): Promise<void>;
@@ -21,8 +21,10 @@ export type { AgentPromptField, SkillFormValues } from './claw-sandbox-helpers';
21
21
  export { navigateToAuditLog, verifyAuditLogTableVisible, getAuditLogRowCount, verifyAuditLogEntryStructure, verifyAuditLogEmptyState, verifyAuditLogPermissionError, verifyAuditLogGenericError, verifyAuditLogLoading, waitForAuditLogDataLoaded, filterByAction, filterByActor, getAvailableActors, filterByDateRange, clearDateRangeFilter, getPaginationInfo, goToNextPage, goToPreviousPage, goToFirstPage, goToLastPage, goToPage, verifyCurrentPage, isOnLastPage, isOnFirstPage, navigateToAuditLogAndWaitForData, filterByActionAndVerify, filterByActorAndVerify, } from './audit-log-helpers';
22
22
  export { getCurrentTenantShowPaywall, creditBalanceTrigger, creditBalancePanel, creditBalancePlanBadge, expectCreditBalanceVisibilityForTenant, openCreditBalanceDropdown, waitForCreditBalanceLoaded, closeCreditBalanceDropdown, getCreditBalancePlanLabel, getCreditBalanceRemaining, expectCreditBalancePanelForFreePlan, expectCreditBalancePanelForTrialPlan, expectCreditBalancePanelForPremiumPlan, expectCreditBalanceForCurrentPlan, } from './credit-balance-helpers';
23
23
  export type { CreditBalancePlan } from './credit-balance-helpers';
24
- export { PRIVACY_LABELS, isPrivacyTabVisible, switchToPrivacyTab, getPrivacyRouterSwitch, setPrivacyRouterEnabled, expectPrivacyRouterEnabled, expectPrivacyFieldsHidden, expectPrivacyFieldsVisible, selectPrivacyAction, setBlockMessage, getEntityChip, setEntitySelected, expectEntitySelected, getOutputFilterSwitch, setOutputFilterEnabled, expectOutputFilterEnabled, } from './privacy-tab-helpers';
24
+ export { PRIVACY_LABELS, isPrivacyTabVisible, switchToPrivacyTab, expectPrivacyFieldsHidden, expectPrivacyFieldsVisible, selectPrivacyAction, setBlockMessage, getEntityChip, setEntitySelected, expectEntitySelected, getOutputFilterSwitch, setOutputFilterEnabled, expectOutputFilterEnabled, } from './privacy-tab-helpers';
25
25
  export type { PrivacyAction, PrivacyEntity } from './privacy-tab-helpers';
26
+ export { CHAT_PRIVACY_LABELS, getChatPrivacyToggle, expectChatPrivacyToggleVisible, expectChatPrivacyState, expectChatPrivacySource, expectChatPrivacyLocked, clickChatPrivacyToggle, getChatPrivacyConfirmDialog, expectChatPrivacyConfirmDialogOpen, confirmEnableChatPrivacyMidSession, cancelEnableChatPrivacyMidSession, isPrivateModeTabVisible, switchToPrivateModeTab, getPrivateModeCard, selectPrivateMode, expectPrivateModeSelected, expectPrivateModeTabReady, getTenantChatPrivacySwitch, getTenantChatPrivacyRow, expectTenantChatPrivacyVisible, expectTenantChatPrivacyEnabled, setTenantChatPrivacyEnabled, } from './chat-privacy-helpers';
27
+ export type { ChatPrivacyMode, ChatPrivacySource, ChatPrivacyToggleState, } from './chat-privacy-helpers';
26
28
  export { VOICE_LABELS, isVoiceTabVisible, switchToVoiceTab, switchToVoiceSubTab, getVoiceProviderCard, selectVoiceProvider, expectVoiceProviderSelected, openMentorVoicePicker, openCallConfigVoicePicker, previewMentorVoiceInline, previewCallConfigVoiceInline, expectMentorVoiceTriggerShows, expectCallConfigVoiceTriggerShows, searchVoices, getVoiceRow, expectVoiceVisible, selectVoice, previewVoice, saveVoiceSettings, getCallConfigForm, expectCallConfigVisible, selectCallMode, setCallLanguage, selectLlmProvider, selectTtsProvider, selectSttProvider, expectTtsSelectDisabled, expectSttSelectDisabled, setUseFunctionCallingEnabled, setEnableVideo, saveCallConfig, resetCallConfig, } from './voice-tab-helpers';
27
29
  export type { VoiceProvider, CallMode, TtsProvider, SttProvider, LlmProvider, } from './voice-tab-helpers';
28
30
  export { SCREENSHARE_LABELS, isScreenShareTabVisible, switchToScreenShareTab, openScreenSharePromptEditor, setScreenSharePrompt, saveScreenSharePrompts, expectScreenShareDisabledHint, } from './screenshare-tab-helpers';
@@ -14,7 +14,6 @@ import { Locator, Page } from '@playwright/test';
14
14
  export declare const PRIVACY_LABELS: {
15
15
  readonly tabName: "Privacy";
16
16
  readonly headerTitle: "Privacy";
17
- readonly routerLabel: "Enable Privacy Router";
18
17
  readonly actionLabel: "When PII is detected";
19
18
  readonly blockMessageLabel: "Block Message";
20
19
  readonly entitiesLabel: "Entity Types";
@@ -51,20 +50,16 @@ export type PrivacyEntity = keyof typeof PRIVACY_LABELS.entityChips;
51
50
  export declare function isPrivacyTabVisible(page: Page): Promise<boolean>;
52
51
  /**
53
52
  * Switch to the Privacy tab. Assumes the Edit Mentor dialog is open.
53
+ *
54
+ * The Privacy Router on/off toggle lives on the Settings tab — this tab
55
+ * only shows dependent PII-handling fields when the router is enabled.
56
+ * Confirm landing via the tab body's testid rather than any inner label,
57
+ * since the field labels are conditional.
54
58
  */
55
59
  export declare function switchToPrivacyTab(page: Page): Promise<void>;
56
- /**
57
- * Locator for the master privacy-router switch.
58
- */
59
- export declare function getPrivacyRouterSwitch(scope: Page | Locator): Locator;
60
- /**
61
- * Click the master toggle until it matches the desired state.
62
- */
63
- export declare function setPrivacyRouterEnabled(scope: Page | Locator, enabled: boolean): Promise<void>;
64
- export declare function expectPrivacyRouterEnabled(scope: Page | Locator, enabled: boolean): Promise<void>;
65
60
  /**
66
61
  * Assert that all dependent privacy fields are hidden — the case when the
67
- * master toggle is off.
62
+ * Privacy Router is disabled (on the Settings tab).
68
63
  */
69
64
  export declare function expectPrivacyFieldsHidden(scope: Page | Locator): Promise<void>;
70
65
  /**
@@ -3120,7 +3120,6 @@ async function expectCreditBalanceForCurrentPlan(page, options) {
3120
3120
  const PRIVACY_LABELS = {
3121
3121
  tabName: 'Privacy',
3122
3122
  headerTitle: 'Privacy',
3123
- routerLabel: 'Enable Privacy Router',
3124
3123
  actionLabel: 'When PII is detected',
3125
3124
  blockMessageLabel: 'Block Message',
3126
3125
  entitiesLabel: 'Entity Types',
@@ -3152,7 +3151,7 @@ const PRIVACY_LABELS = {
3152
3151
  * reach DOM that Radix renders outside the dialog subtree (popovers,
3153
3152
  * select options, etc.).
3154
3153
  */
3155
- function asPage$3(scope) {
3154
+ function asPage$4(scope) {
3156
3155
  return 'page' in scope ? scope.page() : scope;
3157
3156
  }
3158
3157
  /**
@@ -3172,45 +3171,22 @@ async function isPrivacyTabVisible(page) {
3172
3171
  }
3173
3172
  /**
3174
3173
  * Switch to the Privacy tab. Assumes the Edit Mentor dialog is open.
3174
+ *
3175
+ * The Privacy Router on/off toggle lives on the Settings tab — this tab
3176
+ * only shows dependent PII-handling fields when the router is enabled.
3177
+ * Confirm landing via the tab body's testid rather than any inner label,
3178
+ * since the field labels are conditional.
3175
3179
  */
3176
3180
  async function switchToPrivacyTab(page) {
3177
3181
  const tab = page.getByRole('tab', { name: PRIVACY_LABELS.tabName, exact: true });
3178
3182
  await test$1.expect(tab).toBeVisible({ timeout: 10000 });
3179
3183
  await tab.click();
3180
- // The master toggle label is unique to the privacy tab its presence
3181
- // confirms we landed on the right pane.
3182
- await test$1.expect(page.getByText(PRIVACY_LABELS.routerLabel)).toBeVisible({
3183
- timeout: 10000,
3184
- });
3184
+ await test$1.expect(page.getByTestId('privacy-tab-body')).toBeVisible({ timeout: 10000 });
3185
3185
  logger.info('Switched to Privacy tab');
3186
3186
  }
3187
- /**
3188
- * Locator for the master privacy-router switch.
3189
- */
3190
- function getPrivacyRouterSwitch(scope) {
3191
- return scope.getByRole('switch', { name: /Privacy router (enabled|disabled)/ });
3192
- }
3193
- /**
3194
- * Click the master toggle until it matches the desired state.
3195
- */
3196
- async function setPrivacyRouterEnabled(scope, enabled) {
3197
- const toggle = getPrivacyRouterSwitch(scope);
3198
- await test$1.expect(toggle).toBeVisible({ timeout: 10000 });
3199
- const isChecked = (await toggle.getAttribute('aria-checked')) === 'true';
3200
- if (isChecked !== enabled) {
3201
- await toggle.click();
3202
- await test$1.expect(toggle).toHaveAttribute('aria-checked', String(enabled), {
3203
- timeout: 10000,
3204
- });
3205
- }
3206
- logger.info(`Privacy router set to ${enabled ? 'enabled' : 'disabled'}`);
3207
- }
3208
- async function expectPrivacyRouterEnabled(scope, enabled) {
3209
- await test$1.expect(getPrivacyRouterSwitch(scope)).toHaveAttribute('aria-checked', String(enabled));
3210
- }
3211
3187
  /**
3212
3188
  * Assert that all dependent privacy fields are hidden — the case when the
3213
- * master toggle is off.
3189
+ * Privacy Router is disabled (on the Settings tab).
3214
3190
  */
3215
3191
  async function expectPrivacyFieldsHidden(scope) {
3216
3192
  await test$1.expect(scope.getByText(PRIVACY_LABELS.actionLabel)).toBeHidden();
@@ -3236,7 +3212,7 @@ async function selectPrivacyAction(scope, action) {
3236
3212
  await trigger.click();
3237
3213
  // Radix Select renders options in a portal at the document root, so we
3238
3214
  // always look them up on the Page — never on the dialog Locator.
3239
- const option = asPage$3(scope).getByRole('option', {
3215
+ const option = asPage$4(scope).getByRole('option', {
3240
3216
  name: PRIVACY_LABELS.actionOptions[action],
3241
3217
  });
3242
3218
  await test$1.expect(option).toBeVisible({ timeout: 5000 });
@@ -3305,6 +3281,264 @@ async function expectOutputFilterEnabled(scope, enabled) {
3305
3281
  await test$1.expect(getOutputFilterSwitch(scope)).toHaveAttribute('aria-checked', String(enabled));
3306
3282
  }
3307
3283
 
3284
+ /**
3285
+ * Chat-privacy Playwright bindings.
3286
+ *
3287
+ * Three surfaces, three groups of helpers:
3288
+ *
3289
+ * 1. **Header toggle** — the nav-bar `ChatPrivacyToggle` (`<HatGlasses />`
3290
+ * icon-button that morphs into the "Private Mode" pill).
3291
+ * 2. **User profile** — the "Private Mode" tab in `UserProfileModal`
3292
+ * (three radio-card options: Normal / Anonymized / Disabled).
3293
+ * 3. **Tenant admin** — the "Allow users to control chat privacy" row in
3294
+ * the Advanced settings tab (`<Switch>`).
3295
+ *
3296
+ * All selectors target stable hooks — primarily `data-testid` plus the
3297
+ * `data-state` / `data-source` / `aria-pressed` attributes the components
3298
+ * already expose. Avoid asserting on visible copy directly (it changes);
3299
+ * the label maps below are kept for readable assertions on the rare cases
3300
+ * where copy IS the thing we want to lock.
3301
+ */
3302
+ const CHAT_PRIVACY_LABELS = {
3303
+ toggle: {
3304
+ /** Title of the mid-session confirmation dialog. */
3305
+ dialogTitle: 'Enable Private Mode for this chat?',
3306
+ /** Label on the dialog's confirm button (matches both idle + busy state via regex). */
3307
+ dialogConfirm: /Enable Private Mode|Enabling…/,
3308
+ },
3309
+ profileTab: {
3310
+ /** Tab name in the user profile modal sidebar. */
3311
+ tabName: 'Private Mode',
3312
+ /** Section heading inside the tab body. */
3313
+ sectionHeading: 'Default Private Mode',
3314
+ /** Label text on each radio card. Kept in case the test wants to assert
3315
+ * copy explicitly — the click + read paths use the testid instead. */
3316
+ cards: {
3317
+ normal: 'Normal',
3318
+ anonymized: 'Anonymized',
3319
+ disabled: 'Disabled',
3320
+ },
3321
+ },
3322
+ tenantToggle: {
3323
+ /** Visible row label. */
3324
+ rowLabel: 'Allow users to control chat privacy',
3325
+ },
3326
+ };
3327
+ function asPage$3(scope) {
3328
+ return 'page' in scope ? scope.page() : scope;
3329
+ }
3330
+ // ──────────────────────────────────────────────────────────────────────
3331
+ // 1. Header toggle (ChatPrivacyToggle)
3332
+ // ──────────────────────────────────────────────────────────────────────
3333
+ /**
3334
+ * Locator for the nav-bar Private-Mode toggle. The toggle is hidden below
3335
+ * the `md` breakpoint — assertions in mobile contexts should use
3336
+ * `expectChatPrivacyToggleVisible(scope, false)`.
3337
+ */
3338
+ function getChatPrivacyToggle(scope) {
3339
+ return scope.getByTestId('chat-privacy-toggle');
3340
+ }
3341
+ async function expectChatPrivacyToggleVisible(scope, visible) {
3342
+ if (visible) {
3343
+ await test$1.expect(getChatPrivacyToggle(scope)).toBeVisible({ timeout: 10000 });
3344
+ }
3345
+ else {
3346
+ await test$1.expect(getChatPrivacyToggle(scope)).toBeHidden();
3347
+ }
3348
+ }
3349
+ /**
3350
+ * Assert the toggle is on / off via `data-state` (driven by the resolved
3351
+ * effective mode + the local optimistic overlay).
3352
+ */
3353
+ async function expectChatPrivacyState(scope, state) {
3354
+ await test$1.expect(getChatPrivacyToggle(scope)).toHaveAttribute('data-state', state, {
3355
+ timeout: 10000,
3356
+ });
3357
+ }
3358
+ /**
3359
+ * Assert the resolved precedence-source via `data-source` (mentor / tenant
3360
+ * / user / session / default). Useful for verifying the chat-privacy-
3361
+ * effective endpoint resolved to the expected tier.
3362
+ */
3363
+ async function expectChatPrivacySource(scope, source) {
3364
+ await test$1.expect(getChatPrivacyToggle(scope)).toHaveAttribute('data-source', source, {
3365
+ timeout: 10000,
3366
+ });
3367
+ }
3368
+ /**
3369
+ * Lock state is signalled via `aria-disabled` (the component uses
3370
+ * `aria-disabled` instead of native `disabled` so the source-aware tooltip
3371
+ * stays reachable on the locked pill).
3372
+ */
3373
+ async function expectChatPrivacyLocked(scope, locked) {
3374
+ const toggle = getChatPrivacyToggle(scope);
3375
+ if (locked) {
3376
+ await test$1.expect(toggle).toHaveAttribute('aria-disabled', 'true', { timeout: 10000 });
3377
+ }
3378
+ else {
3379
+ // When unlocked, the attribute is either absent or explicitly "false".
3380
+ const value = await toggle.getAttribute('aria-disabled');
3381
+ test$1.expect(value === null || value === 'false').toBe(true);
3382
+ }
3383
+ }
3384
+ /**
3385
+ * Click the toggle. Covers two scenarios:
3386
+ * - Off → on (with no user messages) → starts a fresh private session.
3387
+ * - On (user-initiated, no user messages) → starts a fresh normal session.
3388
+ * Use `confirmEnableChatPrivacyMidSession` for the mid-conversation flow.
3389
+ */
3390
+ async function clickChatPrivacyToggle(scope) {
3391
+ const toggle = getChatPrivacyToggle(scope);
3392
+ await test$1.expect(toggle).toBeVisible({ timeout: 10000 });
3393
+ await toggle.click();
3394
+ logger.info('Clicked chat-privacy toggle');
3395
+ }
3396
+ // ── Mid-session confirm dialog ────────────────────────────────────────
3397
+ /** Locator for the AlertDialog content that the mid-session enable opens. */
3398
+ function getChatPrivacyConfirmDialog(scope) {
3399
+ return asPage$3(scope).getByTestId('chat-privacy-confirm-dialog');
3400
+ }
3401
+ async function expectChatPrivacyConfirmDialogOpen(scope, open) {
3402
+ if (open) {
3403
+ await test$1.expect(getChatPrivacyConfirmDialog(scope)).toBeVisible({ timeout: 10000 });
3404
+ }
3405
+ else {
3406
+ await test$1.expect(getChatPrivacyConfirmDialog(scope)).toBeHidden({ timeout: 10000 });
3407
+ }
3408
+ }
3409
+ /**
3410
+ * Mid-conversation enable flow:
3411
+ * 1. Click the toggle (opens the confirm dialog).
3412
+ * 2. Click the "Enable Private Mode" action.
3413
+ * The session-level disable-chathistory POST is one-way per spec; the
3414
+ * helper resolves once the dialog closes after a successful confirm.
3415
+ */
3416
+ async function confirmEnableChatPrivacyMidSession(scope) {
3417
+ await clickChatPrivacyToggle(scope);
3418
+ const page = asPage$3(scope);
3419
+ await expectChatPrivacyConfirmDialogOpen(page, true);
3420
+ const action = page.getByTestId('chat-privacy-confirm-action');
3421
+ await test$1.expect(action).toBeVisible({ timeout: 10000 });
3422
+ await test$1.expect(action).toBeEnabled({ timeout: 10000 });
3423
+ await action.click();
3424
+ await expectChatPrivacyConfirmDialogOpen(page, false);
3425
+ logger.info('Confirmed mid-session Private Mode enable');
3426
+ }
3427
+ /** Cancel the confirm dialog without enabling. */
3428
+ async function cancelEnableChatPrivacyMidSession(scope) {
3429
+ const page = asPage$3(scope);
3430
+ const cancel = page.getByTestId('chat-privacy-confirm-cancel');
3431
+ await test$1.expect(cancel).toBeVisible({ timeout: 10000 });
3432
+ await cancel.click();
3433
+ await expectChatPrivacyConfirmDialogOpen(page, false);
3434
+ logger.info('Cancelled mid-session Private Mode dialog');
3435
+ }
3436
+ // ──────────────────────────────────────────────────────────────────────
3437
+ // 2. User profile — Private Mode tab
3438
+ // ──────────────────────────────────────────────────────────────────────
3439
+ /**
3440
+ * Switch to the Private Mode tab inside the user profile modal. Assumes
3441
+ * the modal is already open. The tab only appears when the tenant has
3442
+ * `allow_user_chat_privacy_control` on, so a `false` return from
3443
+ * `isPrivateModeTabVisible` is a meaningful state, not an error.
3444
+ */
3445
+ async function isPrivateModeTabVisible(page) {
3446
+ const tab = page.getByRole('tab', {
3447
+ name: CHAT_PRIVACY_LABELS.profileTab.tabName,
3448
+ exact: true,
3449
+ });
3450
+ try {
3451
+ await test$1.expect(tab).toBeVisible({ timeout: 5000 });
3452
+ return true;
3453
+ }
3454
+ catch (_a) {
3455
+ return false;
3456
+ }
3457
+ }
3458
+ async function switchToPrivateModeTab(page) {
3459
+ const tab = page.getByRole('tab', {
3460
+ name: CHAT_PRIVACY_LABELS.profileTab.tabName,
3461
+ exact: true,
3462
+ });
3463
+ await test$1.expect(tab).toBeVisible({ timeout: 10000 });
3464
+ await tab.click();
3465
+ await test$1.expect(page.getByTestId('chat-privacy-tab-body')).toBeVisible({ timeout: 10000 });
3466
+ logger.info('Switched to Private Mode tab');
3467
+ }
3468
+ /** Locator for one of the three radio cards. */
3469
+ function getPrivateModeCard(scope, mode) {
3470
+ return scope.getByTestId(`chat-privacy-mode-${mode}`);
3471
+ }
3472
+ /**
3473
+ * Pick a mode by clicking its card. Resolves once the card flips into
3474
+ * the selected state via `aria-pressed="true"` — i.e. the mutation came
3475
+ * back and the local pending state caught up.
3476
+ */
3477
+ async function selectPrivateMode(scope, mode) {
3478
+ const card = getPrivateModeCard(scope, mode);
3479
+ await test$1.expect(card).toBeVisible({ timeout: 10000 });
3480
+ await test$1.expect(card).toBeEnabled({ timeout: 10000 });
3481
+ await card.click();
3482
+ await test$1.expect(card).toHaveAttribute('aria-pressed', 'true', { timeout: 10000 });
3483
+ logger.info(`Selected Private Mode "${mode}"`);
3484
+ }
3485
+ /** Assert which mode is currently selected. Uses `aria-pressed` + `data-state`. */
3486
+ async function expectPrivateModeSelected(scope, mode) {
3487
+ await test$1.expect(getPrivateModeCard(scope, mode)).toHaveAttribute('aria-pressed', 'true', {
3488
+ timeout: 10000,
3489
+ });
3490
+ await test$1.expect(getPrivateModeCard(scope, mode)).toHaveAttribute('data-state', 'selected', {
3491
+ timeout: 10000,
3492
+ });
3493
+ }
3494
+ /** Assert every card is rendered. Useful as a "tab loaded" sanity check. */
3495
+ async function expectPrivateModeTabReady(scope) {
3496
+ await test$1.expect(scope.getByTestId('chat-privacy-tab-body')).toBeVisible({ timeout: 10000 });
3497
+ await test$1.expect(getPrivateModeCard(scope, 'normal')).toBeVisible();
3498
+ await test$1.expect(getPrivateModeCard(scope, 'anonymized')).toBeVisible();
3499
+ await test$1.expect(getPrivateModeCard(scope, 'disabled')).toBeVisible();
3500
+ }
3501
+ // ──────────────────────────────────────────────────────────────────────
3502
+ // 3. Tenant admin gate (ChatPrivacyContent in Advanced settings)
3503
+ // ──────────────────────────────────────────────────────────────────────
3504
+ /** Locator for the tenant-wide "Allow users to control chat privacy" switch. */
3505
+ function getTenantChatPrivacySwitch(scope) {
3506
+ return scope.getByTestId('tenant-chat-privacy-switch');
3507
+ }
3508
+ /** Locator for the surrounding row (useful for scoping label / spinner queries). */
3509
+ function getTenantChatPrivacyRow(scope) {
3510
+ return scope.getByTestId('tenant-chat-privacy-row');
3511
+ }
3512
+ async function expectTenantChatPrivacyVisible(scope, visible) {
3513
+ if (visible) {
3514
+ await test$1.expect(getTenantChatPrivacyRow(scope)).toBeVisible({ timeout: 10000 });
3515
+ }
3516
+ else {
3517
+ await test$1.expect(getTenantChatPrivacyRow(scope)).toBeHidden();
3518
+ }
3519
+ }
3520
+ /** Assert the tenant gate's current state via `aria-checked` on the Radix switch. */
3521
+ async function expectTenantChatPrivacyEnabled(scope, enabled) {
3522
+ await test$1.expect(getTenantChatPrivacySwitch(scope)).toHaveAttribute('aria-checked', String(enabled), { timeout: 10000 });
3523
+ }
3524
+ /**
3525
+ * Flip the tenant gate to the desired state. Idempotent — does nothing if
3526
+ * the switch is already in the target state. Backend rejects this for
3527
+ * non-admins (403) and the component surfaces an error toast in that case.
3528
+ */
3529
+ async function setTenantChatPrivacyEnabled(scope, enabled) {
3530
+ const sw = getTenantChatPrivacySwitch(scope);
3531
+ await test$1.expect(sw).toBeVisible({ timeout: 10000 });
3532
+ const current = (await sw.getAttribute('aria-checked')) === 'true';
3533
+ if (current === enabled) {
3534
+ logger.info(`Tenant chat-privacy already ${enabled ? 'enabled' : 'disabled'}`);
3535
+ return;
3536
+ }
3537
+ await sw.click();
3538
+ await test$1.expect(sw).toHaveAttribute('aria-checked', String(enabled), { timeout: 10000 });
3539
+ logger.info(`Tenant chat-privacy set to ${enabled ? 'enabled' : 'disabled'}`);
3540
+ }
3541
+
3308
3542
  /**
3309
3543
  * Voice tab helpers — Playwright bindings for the `AgentVoiceTab` component
3310
3544
  * from `@iblai/web-containers`.
@@ -4409,6 +4643,7 @@ Object.defineProperty(exports, "expect", {
4409
4643
  get: function () { return test$1.expect; }
4410
4644
  });
4411
4645
  exports.AuthFlowBuilder = AuthFlowBuilder;
4646
+ exports.CHAT_PRIVACY_LABELS = CHAT_PRIVACY_LABELS;
4412
4647
  exports.CustomReporter = CustomReporter;
4413
4648
  exports.MailsacClient = MailsacClient;
4414
4649
  exports.PRIVACY_LABELS = PRIVACY_LABELS;
@@ -4426,6 +4661,7 @@ exports.canChatWithEmbedMentor = canChatWithEmbedMentor;
4426
4661
  exports.cancelDeleteInstance = cancelDeleteInstance;
4427
4662
  exports.cancelDeleteSkill = cancelDeleteSkill;
4428
4663
  exports.cancelDisconnectInstance = cancelDisconnectInstance;
4664
+ exports.cancelEnableChatPrivacyMidSession = cancelEnableChatPrivacyMidSession;
4429
4665
  exports.cancelNewInstanceDialog = cancelNewInstanceDialog;
4430
4666
  exports.checkAdminStatus = checkAdminStatus;
4431
4667
  exports.clearDateRangeFilter = clearDateRangeFilter;
@@ -4435,10 +4671,12 @@ exports.clickBillingAddCredits = clickBillingAddCredits;
4435
4671
  exports.clickBillingManageBilling = clickBillingManageBilling;
4436
4672
  exports.clickBillingManageUsage = clickBillingManageUsage;
4437
4673
  exports.clickBillingUpgrade = clickBillingUpgrade;
4674
+ exports.clickChatPrivacyToggle = clickChatPrivacyToggle;
4438
4675
  exports.clickDownloadAgain = clickDownloadAgain;
4439
4676
  exports.clickManualDownloadLink = clickManualDownloadLink;
4440
4677
  exports.closeCreditBalanceDropdown = closeCreditBalanceDropdown;
4441
4678
  exports.closeWithEsc = closeWithEsc;
4679
+ exports.confirmEnableChatPrivacyMidSession = confirmEnableChatPrivacyMidSession;
4442
4680
  exports.connectToInstance = connectToInstance;
4443
4681
  exports.createAuthSetup = createAuthSetup;
4444
4682
  exports.createEnvConfig = createEnvConfig;
@@ -4468,6 +4706,11 @@ exports.expectBillingTabForPremiumPlan = expectBillingTabForPremiumPlan;
4468
4706
  exports.expectBillingTabForTrialPlan = expectBillingTabForTrialPlan;
4469
4707
  exports.expectCallConfigVisible = expectCallConfigVisible;
4470
4708
  exports.expectCallConfigVoiceTriggerShows = expectCallConfigVoiceTriggerShows;
4709
+ exports.expectChatPrivacyConfirmDialogOpen = expectChatPrivacyConfirmDialogOpen;
4710
+ exports.expectChatPrivacyLocked = expectChatPrivacyLocked;
4711
+ exports.expectChatPrivacySource = expectChatPrivacySource;
4712
+ exports.expectChatPrivacyState = expectChatPrivacyState;
4713
+ exports.expectChatPrivacyToggleVisible = expectChatPrivacyToggleVisible;
4471
4714
  exports.expectCompletedTasks = expectCompletedTasks;
4472
4715
  exports.expectCreditBalanceForCurrentPlan = expectCreditBalanceForCurrentPlan;
4473
4716
  exports.expectCreditBalancePanelForFreePlan = expectCreditBalancePanelForFreePlan;
@@ -4485,7 +4728,8 @@ exports.expectNoLogsForSelectedTask = expectNoLogsForSelectedTask;
4485
4728
  exports.expectOutputFilterEnabled = expectOutputFilterEnabled;
4486
4729
  exports.expectPrivacyFieldsHidden = expectPrivacyFieldsHidden;
4487
4730
  exports.expectPrivacyFieldsVisible = expectPrivacyFieldsVisible;
4488
- exports.expectPrivacyRouterEnabled = expectPrivacyRouterEnabled;
4731
+ exports.expectPrivateModeSelected = expectPrivateModeSelected;
4732
+ exports.expectPrivateModeTabReady = expectPrivateModeTabReady;
4489
4733
  exports.expectScheduleStartTimeInPastError = expectScheduleStartTimeInPastError;
4490
4734
  exports.expectScreenShareDisabledHint = expectScreenShareDisabledHint;
4491
4735
  exports.expectSttSelectDisabled = expectSttSelectDisabled;
@@ -4493,6 +4737,8 @@ exports.expectTaskInList = expectTaskInList;
4493
4737
  exports.expectTaskNotInList = expectTaskNotInList;
4494
4738
  exports.expectTaskStatus = expectTaskStatus;
4495
4739
  exports.expectTasksEmpty = expectTasksEmpty;
4740
+ exports.expectTenantChatPrivacyEnabled = expectTenantChatPrivacyEnabled;
4741
+ exports.expectTenantChatPrivacyVisible = expectTenantChatPrivacyVisible;
4496
4742
  exports.expectTotalTasks = expectTotalTasks;
4497
4743
  exports.expectTtsSelectDisabled = expectTtsSelectDisabled;
4498
4744
  exports.expectVoiceProviderSelected = expectVoiceProviderSelected;
@@ -4510,6 +4756,8 @@ exports.getBillingAutoRechargeStatus = getBillingAutoRechargeStatus;
4510
4756
  exports.getBillingPlanLabel = getBillingPlanLabel;
4511
4757
  exports.getBrowserKey = getBrowserKey;
4512
4758
  exports.getCallConfigForm = getCallConfigForm;
4759
+ exports.getChatPrivacyConfirmDialog = getChatPrivacyConfirmDialog;
4760
+ exports.getChatPrivacyToggle = getChatPrivacyToggle;
4513
4761
  exports.getCreditBalancePlanLabel = getCreditBalancePlanLabel;
4514
4762
  exports.getCreditBalanceRemaining = getCreditBalanceRemaining;
4515
4763
  exports.getCurrentModel = getCurrentModel;
@@ -4522,11 +4770,13 @@ exports.getMemoryCount = getMemoryCount;
4522
4770
  exports.getMentorIdFromUrl = getMentorIdFromUrl;
4523
4771
  exports.getOutputFilterSwitch = getOutputFilterSwitch;
4524
4772
  exports.getPaginationInfo = getPaginationInfo;
4525
- exports.getPrivacyRouterSwitch = getPrivacyRouterSwitch;
4773
+ exports.getPrivateModeCard = getPrivateModeCard;
4526
4774
  exports.getScheduleTaskButton = getScheduleTaskButton;
4527
4775
  exports.getSearchInput = getSearchInput;
4528
4776
  exports.getSkillRowCount = getSkillRowCount;
4529
4777
  exports.getTaskRow = getTaskRow;
4778
+ exports.getTenantChatPrivacyRow = getTenantChatPrivacyRow;
4779
+ exports.getTenantChatPrivacySwitch = getTenantChatPrivacySwitch;
4530
4780
  exports.getVoiceProviderCard = getVoiceProviderCard;
4531
4781
  exports.getVoiceRow = getVoiceRow;
4532
4782
  exports.goToFirstPage = goToFirstPage;
@@ -4541,6 +4791,7 @@ exports.isMemoryTabVisible = isMemoryTabVisible;
4541
4791
  exports.isOnFirstPage = isOnFirstPage;
4542
4792
  exports.isOnLastPage = isOnLastPage;
4543
4793
  exports.isPrivacyTabVisible = isPrivacyTabVisible;
4794
+ exports.isPrivateModeTabVisible = isPrivateModeTabVisible;
4544
4795
  exports.isSandboxTabVisible = isSandboxTabVisible;
4545
4796
  exports.isScreenShareTabVisible = isScreenShareTabVisible;
4546
4797
  exports.isSkillEnabled = isSkillEnabled;
@@ -4593,6 +4844,7 @@ exports.selectDateFromCalendar = selectDateFromCalendar;
4593
4844
  exports.selectLLMModel = selectLLMModel;
4594
4845
  exports.selectLlmProvider = selectLlmProvider;
4595
4846
  exports.selectPrivacyAction = selectPrivacyAction;
4847
+ exports.selectPrivateMode = selectPrivateMode;
4596
4848
  exports.selectSttProvider = selectSttProvider;
4597
4849
  exports.selectTaskInList = selectTaskInList;
4598
4850
  exports.selectTtsProvider = selectTtsProvider;
@@ -4603,8 +4855,8 @@ exports.setCallLanguage = setCallLanguage;
4603
4855
  exports.setEnableVideo = setEnableVideo;
4604
4856
  exports.setEntitySelected = setEntitySelected;
4605
4857
  exports.setOutputFilterEnabled = setOutputFilterEnabled;
4606
- exports.setPrivacyRouterEnabled = setPrivacyRouterEnabled;
4607
4858
  exports.setScreenSharePrompt = setScreenSharePrompt;
4859
+ exports.setTenantChatPrivacyEnabled = setTenantChatPrivacyEnabled;
4608
4860
  exports.setUseFunctionCallingEnabled = setUseFunctionCallingEnabled;
4609
4861
  exports.setupSandboxInstance = setupSandboxInstance;
4610
4862
  exports.shouldAddNewRowWhenClickingAddRowButton = shouldAddNewRowWhenClickingAddRowButton;
@@ -4626,6 +4878,7 @@ exports.shouldVerifyCSVEditorDialogAccessibility = shouldVerifyCSVEditorDialogAc
4626
4878
  exports.signUpWithEmailAndPassword = signUpWithEmailAndPassword;
4627
4879
  exports.switchToMemoryTab = switchToMemoryTab;
4628
4880
  exports.switchToPrivacyTab = switchToPrivacyTab;
4881
+ exports.switchToPrivateModeTab = switchToPrivateModeTab;
4629
4882
  exports.switchToSandboxTab = switchToSandboxTab;
4630
4883
  exports.switchToScreenShareTab = switchToScreenShareTab;
4631
4884
  exports.switchToSkillsTab = switchToSkillsTab;