@iblai/iblai-js 2.3.11 → 2.3.13

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.
@@ -1740,15 +1740,6 @@ async function switchToSandboxTab(page) {
1740
1740
  await expect(page.getByRole('heading', { name: 'Sandbox' }).or(page.getByText('Sandbox'))).toBeVisible({ timeout: 10000 });
1741
1741
  logger.info('Switched to Sandbox tab');
1742
1742
  }
1743
- /**
1744
- * Switch to the Skills tab inside the edit-mentor modal.
1745
- */
1746
- async function switchToSkillsTab(page) {
1747
- const tab = page.getByRole('tab', { name: 'Skills', exact: true });
1748
- await expect(tab).toBeVisible({ timeout: 10000 });
1749
- await tab.click();
1750
- logger.info('Switched to Skills tab');
1751
- }
1752
1743
  // ============================
1753
1744
  // Instance Table Helpers (not-connected state)
1754
1745
  // ============================
@@ -2248,96 +2239,251 @@ async function editAgentPrompt(page, field, content) {
2248
2239
  logger.info(`Edited agent prompt "${field}" with ${content.length} chars`);
2249
2240
  }
2250
2241
  // ============================
2251
- // Skills Tab Helpers
2242
+ // Combined Workflow Helpers
2243
+ // ============================
2244
+ /**
2245
+ * Full workflow: open Sandbox tab, create a new instance, connect the mentor to it.
2246
+ * Returns when the mentor is successfully connected.
2247
+ */
2248
+ async function setupSandboxInstance(page, instance) {
2249
+ await switchToSandboxTab(page);
2250
+ await createInstance(page, instance);
2251
+ await connectToInstance(page, instance.name);
2252
+ await verifyConnectedInstanceCard(page);
2253
+ logger.info(`Sandbox setup complete for "${instance.name}"`);
2254
+ }
2255
+ /**
2256
+ * Full workflow: open Sandbox tab, disconnect the current instance (if any),
2257
+ * then delete it from the table.
2258
+ */
2259
+ async function teardownSandboxInstance(page, instanceName) {
2260
+ await switchToSandboxTab(page);
2261
+ // If a connected instance is present, disconnect first
2262
+ const disconnectButton = page.getByRole('button', { name: /^Disconnect$/i });
2263
+ if (await disconnectButton.isVisible().catch(() => false)) {
2264
+ await disconnectInstance(page);
2265
+ }
2266
+ await deleteInstance(page, instanceName);
2267
+ logger.info(`Sandbox teardown complete for "${instanceName}"`);
2268
+ }
2269
+
2270
+ // Agent Skills helpers — the Skills section of the edit-agent dialog
2271
+ // ("Agent Skills" / "Available Skills" sub-tabs, skill CRUD with the
2272
+ // General/Resources dialog tabs, skill resources, and the chat `/` skill
2273
+ // picker). Kept separate from the Claw sandbox helpers, mirroring the
2274
+ // agent-skills / claw split in the data layer.
2275
+ //
2276
+ // Flake-resistance rules used throughout:
2277
+ // - Every dialog is captured ONCE via `page.getByRole('dialog', { name })`
2278
+ // (the accessible name comes from the dialog title only), and all fields
2279
+ // and buttons are resolved from that scoped locator. Dialogs stack here —
2280
+ // New/Edit Skill sits over the edit-agent dialog, resource dialogs sit
2281
+ // over Edit Skill — so unscoped text filters would match several dialogs.
2282
+ // - Rows are located via their test ids (`agent-skill-row`,
2283
+ // `available-skill-row`, `skill-resource-row`) filtered by visible name,
2284
+ // never via CSS classes.
2285
+ // - All waits are element-renders or toasts — never networkidle/timeouts.
2286
+ // - Page-level queries are used only for true portals: toasts, the dropdown
2287
+ // menu contents (they portal to document.body), and the Radix Select
2288
+ // listbox inside resource dialogs.
2289
+ // ============================
2290
+ // Navigation
2252
2291
  // ============================
2253
2292
  /**
2254
- * Verify the Skills tab content is loaded (either showing skill rows or the empty state).
2293
+ * Switch to the Skills tab inside the edit-agent dialog and wait for the
2294
+ * skills section to render.
2295
+ */
2296
+ async function switchToSkillsTab(page) {
2297
+ const tab = page.getByRole('tab', { name: 'Skills', exact: true });
2298
+ await expect(tab).toBeVisible({ timeout: 10000 });
2299
+ await tab.click();
2300
+ await expect(page.getByTestId('agent-skills-content')).toBeVisible({ timeout: 10000 });
2301
+ logger.info('Switched to Skills tab');
2302
+ }
2303
+ /** The Skills section container — scope for rows, sub-tabs, and pagination. */
2304
+ function skillsSection(page) {
2305
+ return page.getByTestId('agent-skills-content');
2306
+ }
2307
+ /** A row on the "Agent Skills" sub-tab, located by the skill's name. */
2308
+ function agentSkillRow(page, skillName) {
2309
+ return skillsSection(page)
2310
+ .getByTestId('agent-skill-row')
2311
+ .filter({ hasText: skillName });
2312
+ }
2313
+ /** A row on the "Available Skills" sub-tab, located by the skill's name. */
2314
+ function availableSkillRow(page, skillName) {
2315
+ return skillsSection(page)
2316
+ .getByTestId('available-skill-row')
2317
+ .filter({ hasText: skillName });
2318
+ }
2319
+ /**
2320
+ * Verify the Skills section is loaded: the section container and its
2321
+ * "Agent Skills" / "Available Skills" sub-tabs are visible.
2255
2322
  */
2256
2323
  async function verifySkillsTabVisible(page) {
2257
- // Either at least one skill toggle row is visible, or the empty message is shown
2258
- const hasSkills = await page
2259
- .getByRole('switch')
2324
+ const section = skillsSection(page);
2325
+ await expect(section).toBeVisible({ timeout: 10000 });
2326
+ await expect(page.getByTestId('agent-skills-tab-agent')).toBeVisible({ timeout: 10000 });
2327
+ await expect(page.getByTestId('agent-skills-tab-available')).toBeVisible({ timeout: 10000 });
2328
+ logger.info('Skills section is visible with Agent/Available sub-tabs');
2329
+ }
2330
+ /**
2331
+ * Switch to the "Agent Skills" sub-tab (the agent's own skills, with enable
2332
+ * toggles; the default sub-tab) and wait for its panel to render.
2333
+ */
2334
+ async function switchToAgentSkillsSubTab(page) {
2335
+ await page.getByTestId('agent-skills-tab-agent').click();
2336
+ await expect(skillsSection(page)
2337
+ .getByTestId('agent-skill-row')
2260
2338
  .first()
2261
- .isVisible()
2262
- .catch(() => false);
2263
- if (hasSkills) {
2264
- logger.info('Skills tab is visible with skills rendered');
2265
- return;
2266
- }
2339
+ .or(page.getByText(/No skills enabled for this agent yet/i))).toBeVisible({ timeout: 10000 });
2340
+ logger.info('Switched to Agent Skills sub-tab');
2341
+ }
2342
+ /**
2343
+ * Switch to the "Available Skills" sub-tab (the platform catalog, paged
2344
+ * server-side 10 per page) and wait for its panel to render past the
2345
+ * loading spinner.
2346
+ */
2347
+ async function switchToAvailableSkillsSubTab(page) {
2348
+ await page.getByTestId('agent-skills-tab-available').click();
2349
+ await expect(skillsSection(page)
2350
+ .getByTestId('available-skill-row')
2351
+ .first()
2352
+ .or(page.getByText(/No skills available for this platform/i))).toBeVisible({ timeout: 10000 });
2353
+ logger.info('Switched to Available Skills sub-tab');
2354
+ }
2355
+ /**
2356
+ * Go to a page of the currently visible skills list via the numbered
2357
+ * IblPagination buttons (the pagination renders nothing on a single page).
2358
+ */
2359
+ async function goToSkillsListPage(page, pageNumber) {
2360
+ await skillsSection(page)
2361
+ .getByRole('navigation')
2362
+ .getByText(String(pageNumber), { exact: true })
2363
+ .click();
2364
+ logger.info(`Went to skills list page ${pageNumber}`);
2365
+ }
2366
+ /**
2367
+ * Verify the "Available Skills" empty state ("No skills available for this
2368
+ * platform…") is shown.
2369
+ */
2370
+ async function verifySkillsEmptyState(page) {
2267
2371
  await expect(page.getByText(/No skills available for this platform/i)).toBeVisible({
2268
2372
  timeout: 10000,
2269
2373
  });
2270
- logger.info('Skills tab is visible with empty state');
2374
+ logger.info('Available Skills empty state is displayed');
2271
2375
  }
2272
2376
  /**
2273
- * Verify that the "No skills available" empty state is shown.
2377
+ * Verify the "Agent Skills" empty state ("No skills enabled for this agent
2378
+ * yet…") is shown.
2274
2379
  */
2275
- async function verifySkillsEmptyState(page) {
2276
- await expect(page.getByText(/No skills available for this platform/i)).toBeVisible({
2380
+ async function verifyAgentSkillsEmptyState(page) {
2381
+ await expect(page.getByText(/No skills enabled for this agent yet/i)).toBeVisible({
2277
2382
  timeout: 10000,
2278
2383
  });
2279
- logger.info('Skills empty state is displayed');
2384
+ logger.info('Agent Skills empty state is displayed');
2280
2385
  }
2281
2386
  /**
2282
- * Count the number of skill rows rendered in the Skills tab.
2283
- * Each row represents an available (enabled) platform skill.
2387
+ * Count the skill rows on the "Agent Skills" sub-tab (max 10 per page).
2284
2388
  */
2285
2389
  async function getSkillRowCount(page) {
2286
- const rows = page.locator('div.rounded-lg.border.p-6').filter({ has: page.getByRole('switch') });
2287
- const count = await rows.count();
2288
- logger.info(`${count} skill row(s) displayed`);
2390
+ const count = await skillsSection(page).getByTestId('agent-skill-row').count();
2391
+ logger.info(`${count} agent skill row(s) displayed`);
2289
2392
  return count;
2290
2393
  }
2291
2394
  /**
2292
- * Verify that a specific skill row is visible in the Skills tab.
2395
+ * Verify that a specific skill row is visible on the "Agent Skills" sub-tab.
2293
2396
  */
2294
2397
  async function verifySkillVisible(page, skillName) {
2295
- const row = page
2296
- .locator('div.rounded-lg.border.p-6')
2297
- .filter({ hasText: skillName })
2298
- .filter({ has: page.getByRole('switch') });
2299
- await expect(row).toBeVisible({ timeout: 10000 });
2398
+ await expect(agentSkillRow(page, skillName)).toBeVisible({ timeout: 10000 });
2300
2399
  logger.info(`Skill "${skillName}" is visible`);
2301
2400
  }
2401
+ // ============================
2402
+ // Attach / Detach / Toggle
2403
+ // ============================
2404
+ /**
2405
+ * Attach a platform skill to the agent from the "Available Skills" sub-tab.
2406
+ * Waits for the toast and for the row to flip to its "Added" chip.
2407
+ */
2408
+ async function addSkillToAgent(page, skillName) {
2409
+ const row = availableSkillRow(page, skillName);
2410
+ await row.getByRole('button', { name: `Add ${skillName} to this agent` }).click();
2411
+ await expect(page.getByText(`${skillName} added`, { exact: false })).toBeVisible({
2412
+ timeout: 10000,
2413
+ });
2414
+ await expect(row.getByText('Added', { exact: true })).toBeVisible({ timeout: 10000 });
2415
+ logger.info(`Added skill "${skillName}" to the agent`);
2416
+ }
2302
2417
  /**
2303
- * Check if a specific skill is currently enabled for the mentor.
2418
+ * Verify a catalog row shows the "Added" chip (already on the agent, or
2419
+ * shadowed by a same-slug private skill) and offers no Add button.
2420
+ */
2421
+ async function verifySkillAdded(page, skillName) {
2422
+ const row = availableSkillRow(page, skillName);
2423
+ await expect(row.getByText('Added', { exact: true })).toBeVisible({ timeout: 10000 });
2424
+ await expect(row.getByRole('button', { name: `Add ${skillName} to this agent` })).toBeHidden();
2425
+ logger.info(`Skill "${skillName}" shows as Added`);
2426
+ }
2427
+ /**
2428
+ * Open the three-dots actions menu for a skill row. The menu content portals
2429
+ * to document.body, so the returned locator is page-level by design.
2430
+ */
2431
+ async function openSkillActionsMenu(page, skillName) {
2432
+ const actionsButton = page.getByRole('button', { name: `${skillName} actions` });
2433
+ await expect(actionsButton).toBeVisible({ timeout: 10000 });
2434
+ await actionsButton.click();
2435
+ const menu = page.getByRole('menu');
2436
+ await expect(menu).toBeVisible({ timeout: 5000 });
2437
+ return menu;
2438
+ }
2439
+ /**
2440
+ * Detach a skill from the agent via its row menu ("Remove from Agent") on the
2441
+ * "Agent Skills" sub-tab. Waits for the toast and for the row to disappear.
2442
+ */
2443
+ async function removeSkillFromAgent(page, skillName) {
2444
+ const menu = await openSkillActionsMenu(page, skillName);
2445
+ await menu.getByRole('menuitem', { name: /Remove from Agent/i }).click();
2446
+ await expect(page.getByText(`${skillName} removed`, { exact: false })).toBeVisible({
2447
+ timeout: 10000,
2448
+ });
2449
+ logger.info(`Removed skill "${skillName}" from the agent`);
2450
+ }
2451
+ /**
2452
+ * Check if a specific skill is currently enabled (its row Switch state) on
2453
+ * the "Agent Skills" sub-tab.
2304
2454
  */
2305
2455
  async function isSkillEnabled(page, skillName) {
2306
- const switchEl = page.getByLabel(new RegExp(`^${skillName} (enabled|disabled)$`));
2456
+ const switchEl = agentSkillRow(page, skillName).getByRole('switch');
2307
2457
  await expect(switchEl).toBeVisible({ timeout: 10000 });
2308
2458
  const enabled = await switchEl.isChecked();
2309
2459
  logger.info(`Skill "${skillName}" is ${enabled ? 'enabled' : 'disabled'}`);
2310
2460
  return enabled;
2311
2461
  }
2312
2462
  /**
2313
- * Enable a skill for the current mentor by toggling it on.
2314
- * No-op if already enabled. Waits for the success toast.
2463
+ * Enable an attached skill by flipping its Switch on (PATCHes the assignment;
2464
+ * attaching itself is `addSkillToAgent`). No-op if already enabled.
2315
2465
  */
2316
2466
  async function enableSkill(page, skillName) {
2317
- const disabledSwitch = page.getByLabel(`${skillName} disabled`);
2318
- const isDisabled = await disabledSwitch.isVisible().catch(() => false);
2319
- if (!isDisabled) {
2467
+ if (await isSkillEnabled(page, skillName)) {
2320
2468
  logger.info(`Skill "${skillName}" already enabled`);
2321
2469
  return;
2322
2470
  }
2323
- await disabledSwitch.click();
2471
+ await agentSkillRow(page, skillName).getByRole('switch').click();
2324
2472
  await expect(page.getByText(`${skillName} enabled`, { exact: false })).toBeVisible({
2325
2473
  timeout: 10000,
2326
2474
  });
2327
2475
  logger.info(`Enabled skill "${skillName}"`);
2328
2476
  }
2329
2477
  /**
2330
- * Disable a skill for the current mentor by toggling it off.
2331
- * No-op if already disabled. Waits for the success toast.
2478
+ * Disable an attached skill by flipping its Switch off, without detaching it
2479
+ * (detaching is `removeSkillFromAgent`). No-op if already disabled.
2332
2480
  */
2333
2481
  async function disableSkill(page, skillName) {
2334
- const enabledSwitch = page.getByLabel(`${skillName} enabled`);
2335
- const isEnabled = await enabledSwitch.isVisible().catch(() => false);
2336
- if (!isEnabled) {
2482
+ if (!(await isSkillEnabled(page, skillName))) {
2337
2483
  logger.info(`Skill "${skillName}" already disabled`);
2338
2484
  return;
2339
2485
  }
2340
- await enabledSwitch.click();
2486
+ await agentSkillRow(page, skillName).getByRole('switch').click();
2341
2487
  await expect(page.getByText(`${skillName} disabled`, { exact: false })).toBeVisible({
2342
2488
  timeout: 10000,
2343
2489
  });
@@ -2357,188 +2503,291 @@ async function toggleSkill(page, skillName) {
2357
2503
  return true;
2358
2504
  }
2359
2505
  /**
2360
- * Open the "New Skill" dialog by clicking the New Skill button in the Skills tab.
2361
- * Returns the dialog locator.
2506
+ * Open the "New Skill" dialog from the Skills section. Returns the dialog
2507
+ * locator — resolve every field from it (the dialog stacks over the
2508
+ * edit-agent dialog, so page-level field queries would be ambiguous).
2362
2509
  */
2363
2510
  async function openNewSkillDialog(page) {
2364
- const newButton = page.getByRole('button', { name: /^New Skill$/i });
2365
- await expect(newButton).toBeVisible({ timeout: 10000 });
2366
- await newButton.click();
2367
- const dialog = page.getByRole('dialog').filter({ hasText: 'New Skill' });
2511
+ await skillsSection(page).getByRole('button', { name: /^New Skill$/ }).click();
2512
+ // Accessible name comes from the dialog title only — never matches the
2513
+ // "New Skill" button text inside the edit-agent dialog behind it.
2514
+ const dialog = page.getByRole('dialog', { name: 'New Skill' });
2368
2515
  return waitForDialogReady(page, dialog);
2369
2516
  }
2370
2517
  /**
2371
- * Type content into the RichTextEditor Instruction field.
2372
- * The editor renders as a contenteditable element.
2373
- *
2374
- * `dialog` scopes the lookups to the open skill modal so the
2375
- * contenteditable `.first()` and the "Instruction" label don't match
2376
- * elements on the modal behind it. The keyboard is page-global.
2518
+ * Type content into the RichTextEditor Instruction field (a contenteditable
2519
+ * region). `dialog` scopes the editor lookup to the open skill modal.
2377
2520
  */
2378
2521
  async function fillInstructionEditor(dialog, content) {
2379
2522
  const page = dialog.page();
2380
- // RichTextEditor uses a contenteditable region
2381
2523
  const editor = dialog.locator('[contenteditable="true"]').first();
2382
- const hasEditor = await editor.isVisible().catch(() => false);
2383
- if (hasEditor) {
2384
- await editor.click();
2385
- await page.keyboard.press('ControlOrMeta+A');
2386
- await page.keyboard.press('Delete');
2387
- if (content) {
2388
- await page.keyboard.type(content);
2389
- }
2390
- return;
2524
+ await expect(editor).toBeVisible({ timeout: 5000 });
2525
+ await editor.click();
2526
+ await page.keyboard.press('ControlOrMeta+A');
2527
+ await page.keyboard.press('Delete');
2528
+ if (content) {
2529
+ await page.keyboard.type(content);
2391
2530
  }
2392
- // Fallback: a plain labelled input/textarea named "Instruction"
2393
- const textarea = dialog.getByLabel('Instruction');
2394
- await textarea.fill(content);
2395
2531
  }
2396
2532
  /**
2397
- * Fill the skill form fields (name, slug, description, version, instruction).
2398
- * The form is used by both the New and Edit dialogs. `dialog` scopes every
2399
- * field query to the open modal (see fillInstructionEditor).
2400
- * The Instruction field is a rich-text (markdown) editor.
2533
+ * Fill the skill form (used by both the New and Edit dialogs). Every field is
2534
+ * resolved from `dialog`; labels use exact matches so "Name" can never match
2535
+ * a "File Name" field in a stacked resource dialog.
2401
2536
  */
2402
2537
  async function fillSkillForm(dialog, values) {
2403
- await dialog.getByLabel('Name').fill(values.name);
2404
- await dialog.getByLabel('Slug').fill(values.slug);
2538
+ if (values.onlyThisAgent !== undefined) {
2539
+ const privateSwitch = dialog.getByRole('switch', { name: 'Only This Agent' });
2540
+ if ((await privateSwitch.isChecked()) !== values.onlyThisAgent) {
2541
+ await privateSwitch.click();
2542
+ }
2543
+ }
2544
+ await dialog.getByLabel('Name', { exact: true }).fill(values.name);
2545
+ await dialog.getByLabel('Slug', { exact: true }).fill(values.slug);
2405
2546
  if (values.version !== undefined) {
2406
- await dialog.getByLabel('Version').fill(values.version);
2547
+ await dialog.getByLabel('Version', { exact: true }).fill(values.version);
2548
+ }
2549
+ if (values.category !== undefined) {
2550
+ await dialog.getByLabel('Category', { exact: true }).fill(values.category);
2407
2551
  }
2408
2552
  if (values.description !== undefined) {
2409
- await dialog.getByLabel('Description').fill(values.description);
2553
+ await dialog.getByLabel('Description', { exact: true }).fill(values.description);
2410
2554
  }
2411
2555
  if (values.instruction !== undefined) {
2412
2556
  await fillInstructionEditor(dialog, values.instruction);
2413
2557
  }
2414
2558
  }
2415
2559
  /**
2416
- * Create a new platform-level skill via the Skills tab.
2560
+ * Create a new platform-level skill via the Skills section.
2417
2561
  */
2418
2562
  async function createSkill(page, values) {
2419
2563
  const dialog = await openNewSkillDialog(page);
2420
2564
  await fillSkillForm(dialog, values);
2421
- const createButton = dialog.getByRole('button', { name: /^Create$/i });
2565
+ const createButton = dialog.getByRole('button', { name: /^Create$/ });
2422
2566
  await expect(createButton).toBeEnabled({ timeout: 5000 });
2423
2567
  await createButton.click();
2424
2568
  await expect(page.getByText('Skill created', { exact: false })).toBeVisible({
2425
2569
  timeout: 10000,
2426
2570
  });
2571
+ await expect(dialog).toBeHidden({ timeout: 5000 });
2427
2572
  logger.info(`Created skill "${values.name}"`);
2428
2573
  }
2429
2574
  /**
2430
- * Open the Actions dropdown for a specific skill row.
2431
- * Returns the dropdown menu locator.
2432
- */
2433
- async function openSkillActionsMenu(page, skillName) {
2434
- const actionsButton = page.getByRole('button', { name: `${skillName} actions` });
2435
- await expect(actionsButton).toBeVisible({ timeout: 10000 });
2436
- await actionsButton.click();
2437
- const menu = page.getByRole('menu');
2438
- await expect(menu).toBeVisible({ timeout: 5000 });
2439
- return menu;
2440
- }
2441
- /**
2442
- * Open the Edit Skill dialog from the actions menu.
2443
- * Returns the dialog locator.
2575
+ * Open the Edit Skill dialog for a row (lands on the General sub-tab).
2576
+ * Returns the dialog locator — resolve fields and sub-tabs from it.
2444
2577
  */
2445
2578
  async function openEditSkillDialog(page, skillName) {
2446
- await openSkillActionsMenu(page, skillName);
2447
- const editItem = page.getByRole('menuitem', { name: /Edit/i });
2448
- await expect(editItem).toBeVisible({ timeout: 5000 });
2449
- await editItem.click();
2450
- const dialog = page.getByRole('dialog').filter({ hasText: 'Edit Skill' });
2579
+ const menu = await openSkillActionsMenu(page, skillName);
2580
+ await menu.getByRole('menuitem', { name: /^Edit$/ }).click();
2581
+ const dialog = page.getByRole('dialog', { name: 'Edit Skill' });
2451
2582
  return waitForDialogReady(page, dialog);
2452
2583
  }
2453
2584
  /**
2454
- * Edit an existing platform-level skill.
2455
- * @param skillName - The current name of the skill to edit
2456
- * @param updates - Fields to update on the skill
2585
+ * Edit an existing platform-level skill (General sub-tab fields).
2457
2586
  */
2458
2587
  async function editSkill(page, skillName, updates) {
2459
2588
  const dialog = await openEditSkillDialog(page, skillName);
2589
+ if (updates.onlyThisAgent !== undefined) {
2590
+ const privateSwitch = dialog.getByRole('switch', { name: 'Only This Agent' });
2591
+ if ((await privateSwitch.isChecked()) !== updates.onlyThisAgent) {
2592
+ await privateSwitch.click();
2593
+ }
2594
+ }
2460
2595
  if (updates.name !== undefined) {
2461
- await dialog.getByLabel('Name').fill(updates.name);
2596
+ await dialog.getByLabel('Name', { exact: true }).fill(updates.name);
2462
2597
  }
2463
2598
  if (updates.slug !== undefined) {
2464
- await dialog.getByLabel('Slug').fill(updates.slug);
2599
+ await dialog.getByLabel('Slug', { exact: true }).fill(updates.slug);
2465
2600
  }
2466
2601
  if (updates.version !== undefined) {
2467
- await dialog.getByLabel('Version').fill(updates.version);
2602
+ await dialog.getByLabel('Version', { exact: true }).fill(updates.version);
2603
+ }
2604
+ if (updates.category !== undefined) {
2605
+ await dialog.getByLabel('Category', { exact: true }).fill(updates.category);
2468
2606
  }
2469
2607
  if (updates.description !== undefined) {
2470
- await dialog.getByLabel('Description').fill(updates.description);
2608
+ await dialog.getByLabel('Description', { exact: true }).fill(updates.description);
2471
2609
  }
2472
2610
  if (updates.instruction !== undefined) {
2473
2611
  await fillInstructionEditor(dialog, updates.instruction);
2474
2612
  }
2475
- const saveButton = dialog.getByRole('button', { name: /^Save$/i });
2613
+ const saveButton = dialog.getByRole('button', { name: /^Save$/ });
2476
2614
  await expect(saveButton).toBeEnabled({ timeout: 5000 });
2477
2615
  await saveButton.click();
2478
2616
  await expect(page.getByText('Skill updated', { exact: false })).toBeVisible({
2479
2617
  timeout: 10000,
2480
2618
  });
2481
- logger.info(`Edited skill "${skillName}" with updates: ${JSON.stringify(updates)}`);
2619
+ await expect(dialog).toBeHidden({ timeout: 5000 });
2620
+ logger.info(`Edited skill "${skillName}"`);
2482
2621
  }
2483
2622
  /**
2484
- * Delete a platform-level skill, confirming the deletion dialog.
2623
+ * Delete a platform-level skill via its row menu, confirming the
2624
+ * "Delete Skill" dialog.
2485
2625
  */
2486
2626
  async function deleteSkill(page, skillName) {
2487
- await openSkillActionsMenu(page, skillName);
2488
- const deleteItem = page.getByRole('menuitem', { name: /Delete/i });
2489
- await expect(deleteItem).toBeVisible({ timeout: 5000 });
2490
- await deleteItem.click();
2491
- const dialog = page.getByRole('dialog').filter({ hasText: 'Delete Skill' });
2492
- await expect(dialog).toBeVisible({ timeout: 5000 });
2493
- const confirmButton = dialog.getByRole('button', { name: /^Delete$/i });
2494
- await expect(confirmButton).toBeVisible({ timeout: 5000 });
2495
- await confirmButton.click();
2627
+ const menu = await openSkillActionsMenu(page, skillName);
2628
+ await menu.getByRole('menuitem', { name: /^Delete$/ }).click();
2629
+ const confirm = page.getByRole('dialog', { name: 'Delete Skill' });
2630
+ await expect(confirm).toBeVisible({ timeout: 5000 });
2631
+ await confirm.getByRole('button', { name: /^Delete$/ }).click();
2496
2632
  await expect(page.getByText('Skill deleted', { exact: false })).toBeVisible({
2497
2633
  timeout: 10000,
2498
2634
  });
2635
+ await expect(confirm).toBeHidden({ timeout: 5000 });
2499
2636
  logger.info(`Deleted skill "${skillName}"`);
2500
2637
  }
2501
2638
  /**
2502
2639
  * Open the delete confirmation for a skill and cancel it (no deletion).
2503
2640
  */
2504
2641
  async function cancelDeleteSkill(page, skillName) {
2505
- await openSkillActionsMenu(page, skillName);
2506
- const deleteItem = page.getByRole('menuitem', { name: /Delete/i });
2507
- await deleteItem.click();
2508
- const dialog = page.getByRole('dialog').filter({ hasText: 'Delete Skill' });
2509
- await expect(dialog).toBeVisible({ timeout: 5000 });
2510
- const cancelButton = dialog.getByRole('button', { name: /Cancel/i });
2511
- await cancelButton.click();
2512
- await expect(dialog).not.toBeVisible({ timeout: 5000 });
2642
+ const menu = await openSkillActionsMenu(page, skillName);
2643
+ await menu.getByRole('menuitem', { name: /^Delete$/ }).click();
2644
+ const confirm = page.getByRole('dialog', { name: 'Delete Skill' });
2645
+ await expect(confirm).toBeVisible({ timeout: 5000 });
2646
+ await confirm.getByRole('button', { name: /^Cancel$/ }).click();
2647
+ await expect(confirm).toBeHidden({ timeout: 5000 });
2513
2648
  logger.info(`Cancelled delete of skill "${skillName}"`);
2514
2649
  }
2515
2650
  // ============================
2516
- // Combined Workflow Helpers
2651
+ // Skill Resources (Edit Skill → Resources sub-tab)
2517
2652
  // ============================
2518
2653
  /**
2519
- * Full workflow: open Sandbox tab, create a new instance, connect the mentor to it.
2520
- * Returns when the mentor is successfully connected.
2654
+ * Switch the open Edit Skill dialog to its Resources sub-tab. Pass the
2655
+ * dialog returned by `openEditSkillDialog`; returns the resources section
2656
+ * locator to scope row lookups.
2521
2657
  */
2522
- async function setupSandboxInstance(page, instance) {
2523
- await switchToSandboxTab(page);
2524
- await createInstance(page, instance);
2525
- await connectToInstance(page, instance.name);
2526
- await verifyConnectedInstanceCard(page);
2527
- logger.info(`Sandbox setup complete for "${instance.name}"`);
2658
+ async function switchToSkillResourcesSubTab(editSkillDialog) {
2659
+ await editSkillDialog.getByTestId('skill-dialog-tab-files').click();
2660
+ const section = editSkillDialog.getByTestId('skill-resources-section');
2661
+ await expect(section).toBeVisible({ timeout: 5000 });
2662
+ logger.info('Switched to the Resources sub-tab');
2663
+ return section;
2664
+ }
2665
+ /** A resource row inside the Resources sub-tab, located by filename. */
2666
+ function resourceRow(section, filename) {
2667
+ return section.getByTestId('skill-resource-row').filter({ hasText: filename });
2528
2668
  }
2529
2669
  /**
2530
- * Full workflow: open Sandbox tab, disconnect the current instance (if any),
2531
- * then delete it from the table.
2670
+ * Open the "New Resource" dialog from the resources section. Returns the
2671
+ * dialog locator (it stacks over the Edit Skill dialog).
2532
2672
  */
2533
- async function teardownSandboxInstance(page, instanceName) {
2534
- await switchToSandboxTab(page);
2535
- // If a connected instance is present, disconnect first
2536
- const disconnectButton = page.getByRole('button', { name: /^Disconnect$/i });
2537
- if (await disconnectButton.isVisible().catch(() => false)) {
2538
- await disconnectInstance(page);
2673
+ async function openNewResourceDialog(page, resourcesSection) {
2674
+ await resourcesSection.getByRole('button', { name: /^New Resource$/ }).click();
2675
+ const dialog = page.getByRole('dialog', { name: 'New Resource' });
2676
+ await expect(dialog).toBeVisible({ timeout: 5000 });
2677
+ return dialog;
2678
+ }
2679
+ /**
2680
+ * Add a text resource (reference/script) via the "New Resource" dialog.
2681
+ * The type Select's listbox portals inside the dialog panel, so options are
2682
+ * resolved from the dialog too.
2683
+ */
2684
+ async function addTextResource(page, resourcesSection, { filename, content, type = 'Reference' }) {
2685
+ const dialog = await openNewResourceDialog(page, resourcesSection);
2686
+ if (type !== 'Reference') {
2687
+ await dialog.getByRole('combobox').click();
2688
+ await dialog.getByRole('option', { name: type, exact: true }).click();
2539
2689
  }
2540
- await deleteInstance(page, instanceName);
2541
- logger.info(`Sandbox teardown complete for "${instanceName}"`);
2690
+ await dialog.getByLabel('File Name', { exact: true }).fill(filename);
2691
+ await dialog.getByLabel('Content', { exact: true }).fill(content);
2692
+ await dialog.getByRole('button', { name: /^Add Resource$/ }).click();
2693
+ await expect(page.getByText('Resource added', { exact: false })).toBeVisible({
2694
+ timeout: 10000,
2695
+ });
2696
+ await expect(dialog).toBeHidden({ timeout: 5000 });
2697
+ await expect(resourceRow(resourcesSection, filename)).toBeVisible({ timeout: 10000 });
2698
+ logger.info(`Added text resource "${filename}"`);
2699
+ }
2700
+ /**
2701
+ * Upload a binary asset resource via the "New Resource" dialog. `filePath`
2702
+ * is passed to Playwright's setInputFiles (absolute or test-relative).
2703
+ */
2704
+ async function uploadAssetResource(page, resourcesSection, filePath) {
2705
+ const dialog = await openNewResourceDialog(page, resourcesSection);
2706
+ await dialog.getByRole('combobox').click();
2707
+ await dialog.getByRole('option', { name: 'Asset', exact: true }).click();
2708
+ await dialog.getByTestId('resource-file-input').setInputFiles(filePath);
2709
+ await dialog.getByRole('button', { name: /^Upload$/ }).click();
2710
+ await expect(page.getByText('Resource uploaded', { exact: false })).toBeVisible({
2711
+ timeout: 30000,
2712
+ });
2713
+ await expect(dialog).toBeHidden({ timeout: 5000 });
2714
+ logger.info(`Uploaded asset resource from "${filePath}"`);
2715
+ }
2716
+ /**
2717
+ * Open a resource row's three-dots menu. The menu content portals to
2718
+ * document.body (above the dialog), so the returned locator is page-level.
2719
+ */
2720
+ async function openResourceActionsMenu(page, resourcesSection, filename) {
2721
+ await resourceRow(resourcesSection, filename)
2722
+ .getByRole('button', { name: `${filename} actions` })
2723
+ .click();
2724
+ const menu = page.getByRole('menu');
2725
+ await expect(menu).toBeVisible({ timeout: 5000 });
2726
+ return menu;
2727
+ }
2728
+ /**
2729
+ * Edit a text resource in place via its row menu ("Edit Resource" dialog).
2730
+ */
2731
+ async function editTextResource(page, resourcesSection, filename, updates) {
2732
+ const menu = await openResourceActionsMenu(page, resourcesSection, filename);
2733
+ await menu.getByRole('menuitem', { name: /^Edit$/ }).click();
2734
+ const dialog = page.getByRole('dialog', { name: 'Edit Resource' });
2735
+ await expect(dialog).toBeVisible({ timeout: 5000 });
2736
+ if (updates.filename !== undefined) {
2737
+ await dialog.getByLabel('File Name', { exact: true }).fill(updates.filename);
2738
+ }
2739
+ if (updates.content !== undefined) {
2740
+ await dialog.getByLabel('Content', { exact: true }).fill(updates.content);
2741
+ }
2742
+ await dialog.getByRole('button', { name: /^Save$/ }).click();
2743
+ await expect(page.getByText('Resource saved', { exact: false })).toBeVisible({
2744
+ timeout: 10000,
2745
+ });
2746
+ await expect(dialog).toBeHidden({ timeout: 5000 });
2747
+ logger.info(`Edited resource "${filename}"`);
2748
+ }
2749
+ /**
2750
+ * Delete a resource row via its menu, confirming the "Delete Resource"
2751
+ * dialog. Waits for the toast and for the row to disappear.
2752
+ */
2753
+ async function deleteResource(page, resourcesSection, filename) {
2754
+ const menu = await openResourceActionsMenu(page, resourcesSection, filename);
2755
+ await menu.getByRole('menuitem', { name: /^Delete$/ }).click();
2756
+ const confirm = page.getByRole('dialog', { name: 'Delete Resource' });
2757
+ await expect(confirm).toBeVisible({ timeout: 5000 });
2758
+ await confirm.getByRole('button', { name: /^Delete$/ }).click();
2759
+ await expect(page.getByText('Resource deleted', { exact: false })).toBeVisible({
2760
+ timeout: 10000,
2761
+ });
2762
+ await expect(confirm).toBeHidden({ timeout: 5000 });
2763
+ await expect(resourceRow(resourcesSection, filename)).toBeHidden({ timeout: 10000 });
2764
+ logger.info(`Deleted resource "${filename}"`);
2765
+ }
2766
+ // ============================
2767
+ // Chat `/` Skill Picker
2768
+ // ============================
2769
+ /**
2770
+ * Type a `/` query into the chat composer and wait for the skill picker
2771
+ * listbox to open (its lazy fetch may show a loading row first on cold
2772
+ * cache — waiting on the listbox element covers both paths).
2773
+ */
2774
+ async function openSlashSkillPicker(page, query = '/') {
2775
+ const composer = page.locator('#chat-input-textarea');
2776
+ await expect(composer).toBeVisible({ timeout: 10000 });
2777
+ await composer.click();
2778
+ await composer.fill(query);
2779
+ await expect(page.getByTestId('slash-skill-picker')).toBeVisible({ timeout: 15000 });
2780
+ logger.info(`Slash skill picker open for query "${query}"`);
2781
+ }
2782
+ /**
2783
+ * Pick a skill from the open `/` picker by its visible name. The picker
2784
+ * closes and the composer is left holding the inserted `/slug ` invocation.
2785
+ */
2786
+ async function selectSlashSkill(page, skillName) {
2787
+ const picker = page.getByTestId('slash-skill-picker');
2788
+ await picker.getByRole('option', { name: new RegExp(skillName, 'i') }).click();
2789
+ await expect(picker).toBeHidden({ timeout: 5000 });
2790
+ logger.info(`Selected slash skill "${skillName}"`);
2542
2791
  }
2543
2792
 
2544
2793
  // ============================
@@ -6929,5 +7178,5 @@ function createPlaywrightConfig(options) {
6929
7178
  });
6930
7179
  }
6931
7180
 
6932
- export { AuthFlowBuilder, CHAT_PRIVACY_LABELS, CustomReporter, EVALS_LABELS, GRADER_LABELS, LTI_LABELS, LTI_TEST_IDS, MailsacClient, PRIVACY_LABELS, SCREENSHARE_LABELS, SUPPORT_LABELS, TASKS_LABELS, VOICE_LABELS, addGraderCriterion, addManualScore, addMemory, addQaPairsManually, archiveFirstMemory, archiveMemoryByContent, billingAutoRechargeSection, billingCreditsSection, billingPlanSection, buildReportUrl, canChatWithEmbedMentor, cancelDeleteEvaluation, cancelDeleteInstance, cancelDeleteSkill, cancelDisconnectInstance, cancelEnableChatPrivacyMidSession, cancelKeyDelete, cancelLlmJudge, cancelNewInstanceDialog, cancelStartEvaluation, checkAdminStatus, checkRunStatus, clearDateRangeFilter, clearGradeResultOverride, clearInstanceSearch, clearUserFilter, clickBackHome, clickBillingAddCredits, clickBillingManageBilling, clickBillingManageUsage, clickBillingUpgrade, clickChatPrivacyToggle, clickDownloadAgain, clickManualDownloadLink, closeBenchmarkItemsDialog, closeCreditBalanceDropdown, closeEvaluationDetailDialog, closeKeyDetail, closeManageBenchmarksDialog, closeWithEsc, confirmEnableChatPrivacyMidSession, confirmKeyDelete, connectToInstance, copyEndpoint, createAuthSetup, createBenchmark, createEnvConfig, createInstance, createKey, createLink, createPlaywrightConfig, createSkill, createSupportTicketViaChatAndVerify, createTool, creditBalancePanel, creditBalancePlanBadge, creditBalanceTrigger, deleteEvaluation, deleteFirstMemory, deleteGraderCriterion, deleteInstance, deleteKey, deleteMemoryByContent, deleteQaItem, deleteSkill, deleteTask, disableSkill, disableSupport, disconnectInstance, editAgentPrompt, editGraderCriterion, editInstance, editLink, editSkill, editTool, enableSkill, enableSupport, expandReview, expandTrace, expectAllEndpointsVisible, expectBenchmarkListed, expectBillingAutoRechargeSection, expectBillingCreditsSection, expectBillingPlanSection, expectBillingTabForCurrentPlan, expectBillingTabForFreePlan, expectBillingTabForPremiumPlan, expectBillingTabForTrialPlan, expectCallConfigVisible, expectCallConfigVoiceTriggerShows, expectChatPrivacyConfirmDialogOpen, expectChatPrivacyLocked, expectChatPrivacySource, expectChatPrivacyState, expectChatPrivacyToggleVisible, expectCompletedTasks, expectCreditBalanceForCurrentPlan, expectCreditBalancePanelForFreePlan, expectCreditBalancePanelForPremiumPlan, expectCreditBalancePanelForTrialPlan, expectCreditBalanceVisibilityForTenant, expectDetailWaitingForTraces, expectEndpointUrl, expectEntitySelected, expectFailedTasks, expectGradeResultRow, expectGraderMisconfiguredWarning, expectGraderTotalPoints, expectKeyDeleteError, expectKeyInList, expectKeyNotInList, expectKeysEmpty, expectLastCriterionDeleteDisabled, expectLinkInList, expectLinkNotInList, expectLinkTargetUri, expectLinksEmpty, expectLogDetailsStatus, expectLogsForTask, expectLtiHeader, expectMentorVoiceTriggerShows, expectMessageInConversation, expectNoAccessibilityViolations, expectNoAccessibilityViolationsOnDialogs, expectNoBenchmarksNotice, expectNoLogsForSelectedTask, expectNoRepliesYet, expectNoTickets, expectOutputFilterEnabled, expectPrivacyFieldsHidden, expectPrivacyFieldsVisible, expectPrivateModeSelected, expectPrivateModeTabReady, expectQaItemListed, expectReviewInList, expectReviewStatus, expectRunInTable, expectRunNotInTable, expectRunStatus, expectRunsTableEmpty, expectScheduleStartTimeInPastError, expectScreenShareDisabledHint, expectSelectedBenchmark, expectSttSelectDisabled, expectTaskInList, expectTaskNotInList, expectTaskStatus, expectTasksEmpty, expectTenantChatPrivacyEnabled, expectTenantChatPrivacyVisible, expectTicketClosedNotice, expectTicketDescriptionContains, expectTicketInList, expectTicketStatusInList, expectToolInList, expectToolNotInList, expectToolsEmpty, expectTotalTasks, expectTraceScore, expectTtsSelectDisabled, expectVoiceProviderSelected, expectVoiceVisible, exportRunCsv, fillLinkName, fillToolForm, filterByAction, filterByActionAndVerify, filterByActor, filterByActorAndVerify, filterByDateRange, filterGradeResultsByEmail, filterTicketsByStatus, filterTicketsByUser, generateBrowserSetupProjects, generateProjectConfig, getAddItemsDialog, getAuditLogRowCount, getAvailableActors, getBenchmarkCombobox, getBenchmarkComboboxDropdown, getBenchmarkItemsDialog, getBillingAutoRechargeStatus, getBillingPlanLabel, getBrowserKey, getCallConfigForm, getChatInput, getChatPrivacyConfirmDialog, getChatPrivacyToggle, getCreateBenchmarkDialog, getCreateKeyButton, getCreateLinkButton, getCreateToolButton, getCreditBalancePlanLabel, getCreditBalanceRemaining, getCurrentModel, getCurrentTenantShowPaywall, getDeleteEvaluationDialog, getDeleteQaItemDialog, getEditAgentDialog, getEndpoint, getEndpointCopyButton, getEndpointUrl, getEntityChip, getEvaluationDetailDialog, getInstanceHealthLabel, getInstanceRowCount, getInstanceStatusLabel, getKeyActionsTrigger, getKeyCreateModal, getKeyCreateNameInput, getKeyDeleteModal, getKeyDetailModal, getKeyDetailNameInput, getKeyPublicJwkField, getKeyPublicKeyField, getKeyRow, getKeysEmptyState, getKeysSection, getLinkCopyTargetUriButton, getLinkEditButton, getLinkModal, getLinkNameInput, getLinkRow, getLinksEmptyState, getLinksSection, getLlmJudgeDialog, getLlmPickerDialog, getLtiSubTab, getManageBenchmarksButton, getManageBenchmarksDialog, getMemoryCount, getMentorIdFromUrl, getNewEvaluationButton, getOutputFilterSwitch, getPaginationInfo, getPrivateModeCard, getReplyComposer, getReviewRow, getRunRow, getScheduleTaskButton, getSearchInput, getSkillRowCount, getStartEvaluationDialog, getStatusFilter, getSupportInfoBox, getSupportTabTrigger, getSupportToggle, getTaskRow, getTenantChatPrivacyRow, getTenantChatPrivacySwitch, getTicketDescription, getTicketDetail, getTicketList, getTicketRow, getTicketRowByIndex, getToolEditButton, getToolKeySelect, getToolModal, getToolRow, getToolsEmptyState, getToolsSection, getTraceRow, getUserFilter, getVoiceProviderCard, getVoiceRow, goToFirstPage, goToLastPage, goToNextPage, goToPage, goToPreviousPage, graderTabBody, inviteUserTest, isEvaluationTabVisible, isFirefox, isGraderTabVisible, isGradingEnabled, isJSON, isLtiTabVisible, isMemoryTabVisible, isOnFirstPage, isOnLastPage, isPrivacyTabVisible, isPrivateModeTabVisible, isSandboxTabVisible, isScreenShareTabVisible, isSkillEnabled, isSupportEnabled, isSupportTabVisible, isTasksTabVisible, isVoiceTabVisible, logger, loginWithEmailAndPassword, loginWithMicrosoftIdp, navigateToAccountComponent, navigateToAuditLog, navigateToAuditLogAndWaitForData, navigateToDataReports, navigateToReportDownload, openAddItemsDialog, openAddMemoryDialog, openAgentPromptEditModal, openBenchmarkItems, openCallConfigVoicePicker, openCreateKeyModal, openCreateLinkModal, openCreateToolModal, openCreditBalanceDropdown, openEditInstanceDialog, openEditLinkModal, openEditSkillDialog, openEditToolModal, openFirstLogDetails, openInstanceActionsMenu, openKeyActionsMenu, openKeyDelete, openKeyDetail, openLLMProviderPicker, openManageBenchmarksDialog, openMentorVoicePicker, openNewInstanceDialog, openNewReviewForRun, openNewReviewFromDetail, openNewSkillDialog, openRunActionsMenu, openRunResults, openScheduleTaskDialog, openScreenSharePromptEditor, openSkillActionsMenu, openStartEvaluationDialog, openTicket, overrideGradeResult, parseReportUrlParams, previewCallConfigVoiceInline, previewMentorVoiceInline, previewVoice, pushConfiguration, readEndpointUrl, readKeyPublicJwk, readKeyPublicKey, refreshEvaluationDetail, refreshTickets, reliableClick, reliableFill, removeTraceScore, renameKey, replyToTicket, requestSupportTicketViaChat, resetCallConfig, retry, runConnectedInstanceChecks, runInstanceChecks, safeWaitForURL, saveCallConfig, saveGraderConfig, saveScreenSharePrompts, saveVoiceSettings, scheduleTask, searchBenchmarks, searchInstances, searchTasks, searchVoices, selectBenchmark, selectCallMode, selectDateFromCalendar, selectJudgeLlm, selectLLMModel, selectLlmProvider, selectPrivacyAction, selectPrivateMode, selectSttProvider, selectTaskInList, selectToolKeySetMode, selectToolSigningKey, selectTtsProvider, selectVoice, selectVoiceProvider, sendAgentChatMessage, setBlockMessage, setCallLanguage, setEnableVideo, setEntitySelected, setGradingEnabled, setOutputFilterEnabled, setScreenSharePrompt, setSupportEnabled, setTenantChatPrivacyEnabled, setTicketStatus, setUseFunctionCallingEnabled, setupSandboxInstance, shouldAddNewRowWhenClickingAddRowButton, shouldAllowEditingCellValuesInCSVEditor, shouldCancelCombiningReports, shouldCloseCSVEditorWhenClickingCloseButton, shouldCloseCSVEditorWithoutSavingWhenClickingCancel, shouldCombineRecommendationReports, shouldDirectlyDownloadChatHistoryReportWithoutCSVEditor, shouldDisableOtherDownloadButtonsWhileGeneratingReport, shouldDisplayCSVInEditableTableFormat, shouldDisplayReportCards, shouldHaveCombinedReportDataTestIds, shouldOpenCSVEditorDialog, shouldOpenCSVEditorForUserMetadataReport, shouldSaveEditedCSVAndTriggerDownload, shouldShowCombiningReportsDialog, shouldVerifyCSVEditorDialogAccessibility, signUpWithEmailAndPassword, startEvaluation, submitLinkModal, submitLlmJudge, submitToolModal, switchToAddItemsSubTab, switchToEvaluationTab, switchToGraderSubTab, switchToGraderTab, switchToLtiSubTab, switchToLtiTab, switchToMemoryTab, switchToPrivacyTab, switchToPrivateModeTab, switchToSandboxTab, switchToScreenShareTab, switchToSkillsTab, switchToSupportTab, switchToTasksTab, switchToVoiceSubTab, switchToVoiceTab, teardownSandboxInstance, test, toggleAutoPush, toggleMemorySwitch, toggleSkill, toolFields, uploadQaCsv, verifyAgentConfigPromptsVisible, verifyAuditLogEmptyState, verifyAuditLogEntryStructure, verifyAuditLogGenericError, verifyAuditLogLoading, verifyAuditLogPermissionError, verifyAuditLogTableVisible, verifyConnectDisabledForUnhealthy, verifyConnectedInstanceCard, verifyCurrentPage, verifyDonePhase, verifyDownloadingPhase, verifyErrorPhase, verifyInstanceTableEmpty, verifyInstanceTableVisible, verifyMemoryExists, verifyMemoryNotExists, verifyMemoryTabMemoriesList, verifyMemoryTabSettings, verifyPreparingPhase, verifySkillVisible, verifySkillsEmptyState, verifySkillsTabVisible, waitForAgentChatResponse, waitForAuditLogDataLoaded, waitForBillingTabReady, waitForCreditBalanceLoaded, waitForDialogReady, waitForElementStable, waitForPageLoad, waitForPageReady, waitForReportDownload, waitForTicketInList };
7181
+ export { AuthFlowBuilder, CHAT_PRIVACY_LABELS, CustomReporter, EVALS_LABELS, GRADER_LABELS, LTI_LABELS, LTI_TEST_IDS, MailsacClient, PRIVACY_LABELS, SCREENSHARE_LABELS, SUPPORT_LABELS, TASKS_LABELS, VOICE_LABELS, addGraderCriterion, addManualScore, addMemory, addQaPairsManually, addSkillToAgent, addTextResource, archiveFirstMemory, archiveMemoryByContent, billingAutoRechargeSection, billingCreditsSection, billingPlanSection, buildReportUrl, canChatWithEmbedMentor, cancelDeleteEvaluation, cancelDeleteInstance, cancelDeleteSkill, cancelDisconnectInstance, cancelEnableChatPrivacyMidSession, cancelKeyDelete, cancelLlmJudge, cancelNewInstanceDialog, cancelStartEvaluation, checkAdminStatus, checkRunStatus, clearDateRangeFilter, clearGradeResultOverride, clearInstanceSearch, clearUserFilter, clickBackHome, clickBillingAddCredits, clickBillingManageBilling, clickBillingManageUsage, clickBillingUpgrade, clickChatPrivacyToggle, clickDownloadAgain, clickManualDownloadLink, closeBenchmarkItemsDialog, closeCreditBalanceDropdown, closeEvaluationDetailDialog, closeKeyDetail, closeManageBenchmarksDialog, closeWithEsc, confirmEnableChatPrivacyMidSession, confirmKeyDelete, connectToInstance, copyEndpoint, createAuthSetup, createBenchmark, createEnvConfig, createInstance, createKey, createLink, createPlaywrightConfig, createSkill, createSupportTicketViaChatAndVerify, createTool, creditBalancePanel, creditBalancePlanBadge, creditBalanceTrigger, deleteEvaluation, deleteFirstMemory, deleteGraderCriterion, deleteInstance, deleteKey, deleteMemoryByContent, deleteQaItem, deleteResource, deleteSkill, deleteTask, disableSkill, disableSupport, disconnectInstance, editAgentPrompt, editGraderCriterion, editInstance, editLink, editSkill, editTextResource, editTool, enableSkill, enableSupport, expandReview, expandTrace, expectAllEndpointsVisible, expectBenchmarkListed, expectBillingAutoRechargeSection, expectBillingCreditsSection, expectBillingPlanSection, expectBillingTabForCurrentPlan, expectBillingTabForFreePlan, expectBillingTabForPremiumPlan, expectBillingTabForTrialPlan, expectCallConfigVisible, expectCallConfigVoiceTriggerShows, expectChatPrivacyConfirmDialogOpen, expectChatPrivacyLocked, expectChatPrivacySource, expectChatPrivacyState, expectChatPrivacyToggleVisible, expectCompletedTasks, expectCreditBalanceForCurrentPlan, expectCreditBalancePanelForFreePlan, expectCreditBalancePanelForPremiumPlan, expectCreditBalancePanelForTrialPlan, expectCreditBalanceVisibilityForTenant, expectDetailWaitingForTraces, expectEndpointUrl, expectEntitySelected, expectFailedTasks, expectGradeResultRow, expectGraderMisconfiguredWarning, expectGraderTotalPoints, expectKeyDeleteError, expectKeyInList, expectKeyNotInList, expectKeysEmpty, expectLastCriterionDeleteDisabled, expectLinkInList, expectLinkNotInList, expectLinkTargetUri, expectLinksEmpty, expectLogDetailsStatus, expectLogsForTask, expectLtiHeader, expectMentorVoiceTriggerShows, expectMessageInConversation, expectNoAccessibilityViolations, expectNoAccessibilityViolationsOnDialogs, expectNoBenchmarksNotice, expectNoLogsForSelectedTask, expectNoRepliesYet, expectNoTickets, expectOutputFilterEnabled, expectPrivacyFieldsHidden, expectPrivacyFieldsVisible, expectPrivateModeSelected, expectPrivateModeTabReady, expectQaItemListed, expectReviewInList, expectReviewStatus, expectRunInTable, expectRunNotInTable, expectRunStatus, expectRunsTableEmpty, expectScheduleStartTimeInPastError, expectScreenShareDisabledHint, expectSelectedBenchmark, expectSttSelectDisabled, expectTaskInList, expectTaskNotInList, expectTaskStatus, expectTasksEmpty, expectTenantChatPrivacyEnabled, expectTenantChatPrivacyVisible, expectTicketClosedNotice, expectTicketDescriptionContains, expectTicketInList, expectTicketStatusInList, expectToolInList, expectToolNotInList, expectToolsEmpty, expectTotalTasks, expectTraceScore, expectTtsSelectDisabled, expectVoiceProviderSelected, expectVoiceVisible, exportRunCsv, fillLinkName, fillToolForm, filterByAction, filterByActionAndVerify, filterByActor, filterByActorAndVerify, filterByDateRange, filterGradeResultsByEmail, filterTicketsByStatus, filterTicketsByUser, generateBrowserSetupProjects, generateProjectConfig, getAddItemsDialog, getAuditLogRowCount, getAvailableActors, getBenchmarkCombobox, getBenchmarkComboboxDropdown, getBenchmarkItemsDialog, getBillingAutoRechargeStatus, getBillingPlanLabel, getBrowserKey, getCallConfigForm, getChatInput, getChatPrivacyConfirmDialog, getChatPrivacyToggle, getCreateBenchmarkDialog, getCreateKeyButton, getCreateLinkButton, getCreateToolButton, getCreditBalancePlanLabel, getCreditBalanceRemaining, getCurrentModel, getCurrentTenantShowPaywall, getDeleteEvaluationDialog, getDeleteQaItemDialog, getEditAgentDialog, getEndpoint, getEndpointCopyButton, getEndpointUrl, getEntityChip, getEvaluationDetailDialog, getInstanceHealthLabel, getInstanceRowCount, getInstanceStatusLabel, getKeyActionsTrigger, getKeyCreateModal, getKeyCreateNameInput, getKeyDeleteModal, getKeyDetailModal, getKeyDetailNameInput, getKeyPublicJwkField, getKeyPublicKeyField, getKeyRow, getKeysEmptyState, getKeysSection, getLinkCopyTargetUriButton, getLinkEditButton, getLinkModal, getLinkNameInput, getLinkRow, getLinksEmptyState, getLinksSection, getLlmJudgeDialog, getLlmPickerDialog, getLtiSubTab, getManageBenchmarksButton, getManageBenchmarksDialog, getMemoryCount, getMentorIdFromUrl, getNewEvaluationButton, getOutputFilterSwitch, getPaginationInfo, getPrivateModeCard, getReplyComposer, getReviewRow, getRunRow, getScheduleTaskButton, getSearchInput, getSkillRowCount, getStartEvaluationDialog, getStatusFilter, getSupportInfoBox, getSupportTabTrigger, getSupportToggle, getTaskRow, getTenantChatPrivacyRow, getTenantChatPrivacySwitch, getTicketDescription, getTicketDetail, getTicketList, getTicketRow, getTicketRowByIndex, getToolEditButton, getToolKeySelect, getToolModal, getToolRow, getToolsEmptyState, getToolsSection, getTraceRow, getUserFilter, getVoiceProviderCard, getVoiceRow, goToFirstPage, goToLastPage, goToNextPage, goToPage, goToPreviousPage, goToSkillsListPage, graderTabBody, inviteUserTest, isEvaluationTabVisible, isFirefox, isGraderTabVisible, isGradingEnabled, isJSON, isLtiTabVisible, isMemoryTabVisible, isOnFirstPage, isOnLastPage, isPrivacyTabVisible, isPrivateModeTabVisible, isSandboxTabVisible, isScreenShareTabVisible, isSkillEnabled, isSupportEnabled, isSupportTabVisible, isTasksTabVisible, isVoiceTabVisible, logger, loginWithEmailAndPassword, loginWithMicrosoftIdp, navigateToAccountComponent, navigateToAuditLog, navigateToAuditLogAndWaitForData, navigateToDataReports, navigateToReportDownload, openAddItemsDialog, openAddMemoryDialog, openAgentPromptEditModal, openBenchmarkItems, openCallConfigVoicePicker, openCreateKeyModal, openCreateLinkModal, openCreateToolModal, openCreditBalanceDropdown, openEditInstanceDialog, openEditLinkModal, openEditSkillDialog, openEditToolModal, openFirstLogDetails, openInstanceActionsMenu, openKeyActionsMenu, openKeyDelete, openKeyDetail, openLLMProviderPicker, openManageBenchmarksDialog, openMentorVoicePicker, openNewInstanceDialog, openNewReviewForRun, openNewReviewFromDetail, openNewSkillDialog, openRunActionsMenu, openRunResults, openScheduleTaskDialog, openScreenSharePromptEditor, openSkillActionsMenu, openSlashSkillPicker, openStartEvaluationDialog, openTicket, overrideGradeResult, parseReportUrlParams, previewCallConfigVoiceInline, previewMentorVoiceInline, previewVoice, pushConfiguration, readEndpointUrl, readKeyPublicJwk, readKeyPublicKey, refreshEvaluationDetail, refreshTickets, reliableClick, reliableFill, removeSkillFromAgent, removeTraceScore, renameKey, replyToTicket, requestSupportTicketViaChat, resetCallConfig, retry, runConnectedInstanceChecks, runInstanceChecks, safeWaitForURL, saveCallConfig, saveGraderConfig, saveScreenSharePrompts, saveVoiceSettings, scheduleTask, searchBenchmarks, searchInstances, searchTasks, searchVoices, selectBenchmark, selectCallMode, selectDateFromCalendar, selectJudgeLlm, selectLLMModel, selectLlmProvider, selectPrivacyAction, selectPrivateMode, selectSlashSkill, selectSttProvider, selectTaskInList, selectToolKeySetMode, selectToolSigningKey, selectTtsProvider, selectVoice, selectVoiceProvider, sendAgentChatMessage, setBlockMessage, setCallLanguage, setEnableVideo, setEntitySelected, setGradingEnabled, setOutputFilterEnabled, setScreenSharePrompt, setSupportEnabled, setTenantChatPrivacyEnabled, setTicketStatus, setUseFunctionCallingEnabled, setupSandboxInstance, shouldAddNewRowWhenClickingAddRowButton, shouldAllowEditingCellValuesInCSVEditor, shouldCancelCombiningReports, shouldCloseCSVEditorWhenClickingCloseButton, shouldCloseCSVEditorWithoutSavingWhenClickingCancel, shouldCombineRecommendationReports, shouldDirectlyDownloadChatHistoryReportWithoutCSVEditor, shouldDisableOtherDownloadButtonsWhileGeneratingReport, shouldDisplayCSVInEditableTableFormat, shouldDisplayReportCards, shouldHaveCombinedReportDataTestIds, shouldOpenCSVEditorDialog, shouldOpenCSVEditorForUserMetadataReport, shouldSaveEditedCSVAndTriggerDownload, shouldShowCombiningReportsDialog, shouldVerifyCSVEditorDialogAccessibility, signUpWithEmailAndPassword, startEvaluation, submitLinkModal, submitLlmJudge, submitToolModal, switchToAddItemsSubTab, switchToAgentSkillsSubTab, switchToAvailableSkillsSubTab, switchToEvaluationTab, switchToGraderSubTab, switchToGraderTab, switchToLtiSubTab, switchToLtiTab, switchToMemoryTab, switchToPrivacyTab, switchToPrivateModeTab, switchToSandboxTab, switchToScreenShareTab, switchToSkillResourcesSubTab, switchToSkillsTab, switchToSupportTab, switchToTasksTab, switchToVoiceSubTab, switchToVoiceTab, teardownSandboxInstance, test, toggleAutoPush, toggleMemorySwitch, toggleSkill, toolFields, uploadAssetResource, uploadQaCsv, verifyAgentConfigPromptsVisible, verifyAgentSkillsEmptyState, verifyAuditLogEmptyState, verifyAuditLogEntryStructure, verifyAuditLogGenericError, verifyAuditLogLoading, verifyAuditLogPermissionError, verifyAuditLogTableVisible, verifyConnectDisabledForUnhealthy, verifyConnectedInstanceCard, verifyCurrentPage, verifyDonePhase, verifyDownloadingPhase, verifyErrorPhase, verifyInstanceTableEmpty, verifyInstanceTableVisible, verifyMemoryExists, verifyMemoryNotExists, verifyMemoryTabMemoriesList, verifyMemoryTabSettings, verifyPreparingPhase, verifySkillAdded, verifySkillVisible, verifySkillsEmptyState, verifySkillsTabVisible, waitForAgentChatResponse, waitForAuditLogDataLoaded, waitForBillingTabReady, waitForCreditBalanceLoaded, waitForDialogReady, waitForElementStable, waitForPageLoad, waitForPageReady, waitForReportDownload, waitForTicketInList };
6933
7182
  //# sourceMappingURL=index.esm.js.map