@iblai/iblai-js 1.26.2 → 1.26.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3165,7 +3165,7 @@ const PRIVACY_LABELS = {
3165
3165
  * reach DOM that Radix renders outside the dialog subtree (popovers,
3166
3166
  * select options, etc.).
3167
3167
  */
3168
- function asPage$5(scope) {
3168
+ function asPage$6(scope) {
3169
3169
  return 'page' in scope ? scope.page() : scope;
3170
3170
  }
3171
3171
  /**
@@ -3226,7 +3226,7 @@ async function selectPrivacyAction(scope, action) {
3226
3226
  await trigger.click();
3227
3227
  // Radix Select renders options in a portal at the document root, so we
3228
3228
  // always look them up on the Page — never on the dialog Locator.
3229
- const option = asPage$5(scope).getByRole('option', {
3229
+ const option = asPage$6(scope).getByRole('option', {
3230
3230
  name: PRIVACY_LABELS.actionOptions[action],
3231
3231
  });
3232
3232
  await test$1.expect(option).toBeVisible({ timeout: 5000 });
@@ -3338,7 +3338,7 @@ const CHAT_PRIVACY_LABELS = {
3338
3338
  rowLabel: 'Allow users to control chat privacy',
3339
3339
  },
3340
3340
  };
3341
- function asPage$4(scope) {
3341
+ function asPage$5(scope) {
3342
3342
  return 'page' in scope ? scope.page() : scope;
3343
3343
  }
3344
3344
  // ──────────────────────────────────────────────────────────────────────
@@ -3410,7 +3410,7 @@ async function clickChatPrivacyToggle(scope) {
3410
3410
  // ── Mid-session confirm dialog ────────────────────────────────────────
3411
3411
  /** Locator for the AlertDialog content that the mid-session enable opens. */
3412
3412
  function getChatPrivacyConfirmDialog(scope) {
3413
- return asPage$4(scope).getByTestId('chat-privacy-confirm-dialog');
3413
+ return asPage$5(scope).getByTestId('chat-privacy-confirm-dialog');
3414
3414
  }
3415
3415
  async function expectChatPrivacyConfirmDialogOpen(scope, open) {
3416
3416
  if (open) {
@@ -3429,7 +3429,7 @@ async function expectChatPrivacyConfirmDialogOpen(scope, open) {
3429
3429
  */
3430
3430
  async function confirmEnableChatPrivacyMidSession(scope) {
3431
3431
  await clickChatPrivacyToggle(scope);
3432
- const page = asPage$4(scope);
3432
+ const page = asPage$5(scope);
3433
3433
  await expectChatPrivacyConfirmDialogOpen(page, true);
3434
3434
  const action = page.getByTestId('chat-privacy-confirm-action');
3435
3435
  await test$1.expect(action).toBeVisible({ timeout: 10000 });
@@ -3440,7 +3440,7 @@ async function confirmEnableChatPrivacyMidSession(scope) {
3440
3440
  }
3441
3441
  /** Cancel the confirm dialog without enabling. */
3442
3442
  async function cancelEnableChatPrivacyMidSession(scope) {
3443
- const page = asPage$4(scope);
3443
+ const page = asPage$5(scope);
3444
3444
  const cancel = page.getByTestId('chat-privacy-confirm-cancel');
3445
3445
  await test$1.expect(cancel).toBeVisible({ timeout: 10000 });
3446
3446
  await cancel.click();
@@ -3627,7 +3627,7 @@ const VOICE_LABELS = {
3627
3627
  * reach DOM that Radix renders outside the dialog subtree (popovers,
3628
3628
  * select options, etc.).
3629
3629
  */
3630
- function asPage$3(scope) {
3630
+ function asPage$4(scope) {
3631
3631
  return 'page' in scope ? scope.page() : scope;
3632
3632
  }
3633
3633
  // ─── Tab navigation ──────────────────────────────────────────────────────
@@ -3709,7 +3709,7 @@ async function openMentorVoicePicker(scope) {
3709
3709
  const openBtn = scope.getByTestId('mentor-voice-trigger-open');
3710
3710
  await test$1.expect(openBtn).toBeVisible({ timeout: 10000 });
3711
3711
  await openBtn.click();
3712
- await test$1.expect(asPage$3(scope).getByTestId('voice-picker-modal')).toBeVisible({
3712
+ await test$1.expect(asPage$4(scope).getByTestId('voice-picker-modal')).toBeVisible({
3713
3713
  timeout: 10000,
3714
3714
  });
3715
3715
  }
@@ -3721,7 +3721,7 @@ async function openCallConfigVoicePicker(scope) {
3721
3721
  const openBtn = scope.getByTestId('call-config-voice-trigger-open');
3722
3722
  await test$1.expect(openBtn).toBeVisible({ timeout: 10000 });
3723
3723
  await openBtn.click();
3724
- await test$1.expect(asPage$3(scope).getByTestId('voice-picker-modal')).toBeVisible({
3724
+ await test$1.expect(asPage$4(scope).getByTestId('voice-picker-modal')).toBeVisible({
3725
3725
  timeout: 10000,
3726
3726
  });
3727
3727
  }
@@ -3834,7 +3834,7 @@ async function selectCallMode(scope, mode) {
3834
3834
  const trigger = scope.getByRole('combobox', { name: VOICE_LABELS.callConfigFields.mode });
3835
3835
  await test$1.expect(trigger).toBeVisible({ timeout: 10000 });
3836
3836
  await trigger.click();
3837
- const option = asPage$3(scope).getByRole('option', { name: VOICE_LABELS.modeOptions[mode] });
3837
+ const option = asPage$4(scope).getByRole('option', { name: VOICE_LABELS.modeOptions[mode] });
3838
3838
  await test$1.expect(option).toBeVisible({ timeout: 5000 });
3839
3839
  await option.click();
3840
3840
  await test$1.expect(trigger).toHaveText(new RegExp(VOICE_LABELS.modeOptions[mode]));
@@ -3850,7 +3850,7 @@ async function selectFromCombobox(scope, triggerName, optionName) {
3850
3850
  const trigger = scope.getByRole('combobox', { name: triggerName });
3851
3851
  await test$1.expect(trigger).toBeVisible({ timeout: 10000 });
3852
3852
  await trigger.click();
3853
- const option = asPage$3(scope).getByRole('option', { name: optionName });
3853
+ const option = asPage$4(scope).getByRole('option', { name: optionName });
3854
3854
  await test$1.expect(option).toBeVisible({ timeout: 5000 });
3855
3855
  await option.click();
3856
3856
  }
@@ -3947,7 +3947,7 @@ const SCREENSHARE_LABELS = {
3947
3947
  },
3948
3948
  saveButton: 'Save',
3949
3949
  };
3950
- function asPage$2(scope) {
3950
+ function asPage$3(scope) {
3951
3951
  return 'page' in scope ? scope.page() : scope;
3952
3952
  }
3953
3953
  /**
@@ -3987,7 +3987,7 @@ async function openScreenSharePromptEditor(scope, field) {
3987
3987
  const btn = scope.getByRole('button', { name: `Edit ${label}` });
3988
3988
  await test$1.expect(btn).toBeVisible({ timeout: 10000 });
3989
3989
  await btn.click();
3990
- await test$1.expect(asPage$2(scope).getByText(`Edit ${label}`)).toBeVisible({ timeout: 10000 });
3990
+ await test$1.expect(asPage$3(scope).getByText(`Edit ${label}`)).toBeVisible({ timeout: 10000 });
3991
3991
  logger.info(`Opened screen-share ${field} editor`);
3992
3992
  }
3993
3993
  /**
@@ -3997,7 +3997,7 @@ async function openScreenSharePromptEditor(scope, field) {
3997
3997
  */
3998
3998
  async function setScreenSharePrompt(scope, field, text) {
3999
3999
  await openScreenSharePromptEditor(scope, field);
4000
- const page = asPage$2(scope);
4000
+ const page = asPage$3(scope);
4001
4001
  const editor = page.getByRole('dialog').locator('[contenteditable="true"]').first();
4002
4002
  await test$1.expect(editor).toBeVisible({ timeout: 10000 });
4003
4003
  await editor.click();
@@ -4006,7 +4006,10 @@ async function setScreenSharePrompt(scope, field, text) {
4006
4006
  await editor.pressSequentially(text);
4007
4007
  // The modal's Save button — scope to dialog so we don't grab the
4008
4008
  // outer tab's Save.
4009
- await page.getByRole('dialog').getByRole('button', { name: /^save$/i }).click();
4009
+ await page
4010
+ .getByRole('dialog')
4011
+ .getByRole('button', { name: /^save$/i })
4012
+ .click();
4010
4013
  await test$1.expect(page.getByRole('dialog')).toBeHidden({ timeout: 10000 });
4011
4014
  logger.info(`Set screen-share ${field}`);
4012
4015
  }
@@ -4325,7 +4328,7 @@ const TASKS_LABELS = {
4325
4328
  * reach DOM that Radix renders outside the dialog subtree (popovers,
4326
4329
  * select options, the schedule / delete / log-details dialogs, etc.).
4327
4330
  */
4328
- function asPage$1(scope) {
4331
+ function asPage$2(scope) {
4329
4332
  return 'page' in scope ? scope.page() : scope;
4330
4333
  }
4331
4334
  // ── Tab navigation ─────────────────────────────────────────────────────
@@ -4435,7 +4438,7 @@ async function searchTasks(scope, query) {
4435
4438
  * shares an accessible name with the dialog's submit button.
4436
4439
  */
4437
4440
  function getScheduleTaskDialog(scope) {
4438
- return asPage$1(scope).getByRole('dialog', { name: TASKS_LABELS.scheduleDialog.dialogName });
4441
+ return asPage$2(scope).getByRole('dialog', { name: TASKS_LABELS.scheduleDialog.dialogName });
4439
4442
  }
4440
4443
  /** Open the Schedule Task dialog and wait for it to be interactive. */
4441
4444
  async function openScheduleTaskDialog(scope) {
@@ -4454,7 +4457,7 @@ async function openScheduleTaskDialog(scope) {
4454
4457
  */
4455
4458
  async function scheduleTask(scope, opts) {
4456
4459
  await openScheduleTaskDialog(scope);
4457
- const page = asPage$1(scope);
4460
+ const page = asPage$2(scope);
4458
4461
  // Scope every query to the dialog: the toolbar "Schedule Task" button shares
4459
4462
  // an accessible name with the dialog's submit button, so an unscoped
4460
4463
  // getByRole('button', { name: 'Schedule Task' }) matches two elements.
@@ -4489,13 +4492,13 @@ async function scheduleTask(scope, opts) {
4489
4492
  }
4490
4493
  /** Asserts the in-dialog past-time error is currently shown. */
4491
4494
  async function expectScheduleStartTimeInPastError(scope) {
4492
- await test$1.expect(asPage$1(scope).getByText(TASKS_LABELS.scheduleDialog.startTimeInPast)).toBeVisible({
4495
+ await test$1.expect(asPage$2(scope).getByText(TASKS_LABELS.scheduleDialog.startTimeInPast)).toBeVisible({
4493
4496
  timeout: 5000,
4494
4497
  });
4495
4498
  }
4496
4499
  // ── Delete flow ────────────────────────────────────────────────────────
4497
4500
  function getDeleteTaskDialog(scope) {
4498
- return asPage$1(scope).locator(`[role="dialog"][aria-label="${TASKS_LABELS.deleteDialog.dialogName}"]`);
4501
+ return asPage$2(scope).locator(`[role="dialog"][aria-label="${TASKS_LABELS.deleteDialog.dialogName}"]`);
4499
4502
  }
4500
4503
  /**
4501
4504
  * Click the trash icon on a task row and confirm the delete in the
@@ -4516,7 +4519,7 @@ async function deleteTask(scope, taskName) {
4516
4519
  }
4517
4520
  // ── Logs panel + log details ───────────────────────────────────────────
4518
4521
  function getLogDetailsDialog(scope) {
4519
- return asPage$1(scope).locator(`[role="dialog"][aria-label="${TASKS_LABELS.logDetails.dialogName}"]`);
4522
+ return asPage$2(scope).locator(`[role="dialog"][aria-label="${TASKS_LABELS.logDetails.dialogName}"]`);
4520
4523
  }
4521
4524
  /**
4522
4525
  * Wait for the logs panel to finish loading and assert that the selected
@@ -4552,6 +4555,801 @@ async function expectLogDetailsStatus(scope, status) {
4552
4555
  });
4553
4556
  }
4554
4557
 
4558
+ /**
4559
+ * Evals tab helpers — Playwright bindings for the `AgentEvaluationTab`
4560
+ * component from `@iblai/web-containers` (edit-mentor modal > Evals tab),
4561
+ * including the embedded tenant Benchmarks management dialog.
4562
+ *
4563
+ * All UI strings below mirror `AGENT_EVALUATION_TAB_LABELS` from
4564
+ * `@iblai/web-containers/next` plus the `benchmarks*` i18n namespaces. If a
4565
+ * consumer renames a label via the `labels` prop, override these constants
4566
+ * per spec — don't edit this file.
4567
+ *
4568
+ * Every dialog spawned by this tab carries an explicit `aria-label` on its
4569
+ * `DialogContent` (see `dialogName` entries). Helpers ALWAYS resolve the
4570
+ * dialog once via `[role="dialog"][aria-label="…"]` — the attribute selector,
4571
+ * not the role accessible name, because a visible `DialogTitle` makes Radix's
4572
+ * `aria-labelledby` win the accessible-name computation — and then resolve
4573
+ * every in-dialog control from that scoped locator. This matters here more
4574
+ * than usual:
4575
+ * - the toolbar "New Evaluation" / "Manage benchmarks" buttons share their
4576
+ * text with the dialog titles they open;
4577
+ * - the LLM-judge dialog's title AND submit button both read "Evaluate";
4578
+ * - dialogs nest three deep (Manage benchmarks → benchmark items → add
4579
+ * Q&A), and the add-Q&A dialog has Manual / CSV Upload sub-tabs whose
4580
+ * footers each have their own Cancel button.
4581
+ *
4582
+ * The helpers assume the Edit Mentor dialog is already open. Call
4583
+ * `switchToEvaluationTab(page)` first; from then on, every helper accepts
4584
+ * either the `Page` or a scoping `Locator`.
4585
+ */
4586
+ const EVALS_LABELS = {
4587
+ tabName: 'Evals',
4588
+ header: {
4589
+ title: 'Evals',
4590
+ description: 'Run this agent against a benchmark and score the responses.',
4591
+ },
4592
+ benchmarkPicker: {
4593
+ placeholder: 'Select a benchmark',
4594
+ searchPlaceholder: 'Search benchmarks...',
4595
+ empty: 'No benchmarks found',
4596
+ /** `data-testid` on the combobox trigger button (its text is the selected benchmark, so it has no stable accessible name). */
4597
+ triggerTestId: 'benchmark-combobox',
4598
+ /** `data-testid` on the dropdown panel (rendered inline, not in a portal). */
4599
+ dropdownTestId: 'benchmark-combobox-dropdown',
4600
+ },
4601
+ actions: {
4602
+ newExperiment: 'New Evaluation',
4603
+ manageBenchmarks: 'Manage benchmarks',
4604
+ viewResults: 'View results',
4605
+ evaluate: 'New review',
4606
+ exportCsv: 'Export CSV',
4607
+ checkStatus: 'Check status',
4608
+ delete: 'Delete',
4609
+ },
4610
+ checkStatusToast: {
4611
+ pending: 'Evaluation is still running',
4612
+ completed: 'Evaluation completed',
4613
+ failed: 'Evaluation failed',
4614
+ },
4615
+ emptyBenchmarks: 'No benchmarks yet. Use "Manage benchmarks" above to create one.',
4616
+ startDialog: {
4617
+ /** `aria-label` on the dialog's `DialogContent` — scopes in-dialog locators. */
4618
+ dialogName: 'start-evaluation',
4619
+ experimentNameLabel: 'Evaluation name',
4620
+ cancel: 'Cancel',
4621
+ start: 'Start Evaluation',
4622
+ },
4623
+ manageBenchmarksDialog: {
4624
+ /** `aria-label` on the dialog's `DialogContent` — scopes in-dialog locators. */
4625
+ dialogName: 'manage-benchmarks',
4626
+ searchAriaLabel: 'Search benchmarks',
4627
+ newBenchmark: 'New Benchmark',
4628
+ empty: 'No benchmarks found',
4629
+ },
4630
+ createBenchmarkDialog: {
4631
+ /** `aria-label` on the dialog's `DialogContent` — scopes in-dialog locators. */
4632
+ dialogName: 'create-benchmark',
4633
+ nameLabel: 'Name',
4634
+ descriptionLabel: 'Description',
4635
+ cancel: 'Cancel',
4636
+ create: 'Create',
4637
+ },
4638
+ benchmarkItemsDialog: {
4639
+ /** `aria-label` on the dialog's `DialogContent` — scopes in-dialog locators. */
4640
+ dialogName: 'benchmark-items',
4641
+ addItems: 'Add Q&A',
4642
+ deleteItemAriaLabel: 'Delete Q&A',
4643
+ empty: 'No Q&A in this benchmark yet',
4644
+ },
4645
+ addItemsDialog: {
4646
+ /** `aria-label` on the dialog's `DialogContent` — scopes in-dialog locators. */
4647
+ dialogName: 'add-benchmark-items',
4648
+ manualTab: 'Manual',
4649
+ csvTab: 'CSV Upload',
4650
+ questionLabel: 'Question',
4651
+ answerLabel: 'Answer',
4652
+ addRow: 'Add row',
4653
+ removeRowAriaLabel: 'Remove row',
4654
+ add: 'Add',
4655
+ csvFileLabel: 'CSV file',
4656
+ upload: 'Upload',
4657
+ cancel: 'Cancel',
4658
+ },
4659
+ deleteItemDialog: {
4660
+ /** `aria-label` on the dialog's `DialogContent` — scopes in-dialog locators. */
4661
+ dialogName: 'delete-benchmark-item',
4662
+ delete: 'Delete',
4663
+ cancel: 'Cancel',
4664
+ },
4665
+ detailDialog: {
4666
+ /** `aria-label` on the dialog's `DialogContent` — scopes in-dialog locators. */
4667
+ dialogName: 'evaluation-detail',
4668
+ refreshAriaLabel: 'Refresh',
4669
+ newReview: 'New review',
4670
+ waitingHint: 'Waiting for the evaluation to produce traces. This page auto-refreshes.',
4671
+ noScoresHint: 'No scores yet',
4672
+ scoreNameLabel: 'Score name',
4673
+ scoreValueLabel: 'Value',
4674
+ scoreCommentLabel: 'Comment',
4675
+ addScore: 'Add',
4676
+ removeScoreAriaLabel: 'Remove score',
4677
+ reviewsHeading: 'Reviews',
4678
+ reviewDetailsAriaLabel: 'Toggle review details',
4679
+ reviewStatus: {
4680
+ pending: 'Queued',
4681
+ running: 'Running',
4682
+ completed: 'Complete',
4683
+ failed: 'Failed',
4684
+ },
4685
+ },
4686
+ judgeDialog: {
4687
+ /** `aria-label` on the dialog's `DialogContent` — scopes in-dialog locators. */
4688
+ dialogName: 'llm-judge',
4689
+ criteriaLabel: 'Criteria',
4690
+ scoreNameLabel: 'Score name',
4691
+ maxConcurrencyLabel: 'Max concurrency',
4692
+ selectorAriaLabel: 'LLM Model Selector',
4693
+ cancel: 'Cancel',
4694
+ submit: 'Evaluate',
4695
+ },
4696
+ llmPickerDialog: {
4697
+ /** `aria-label` on the dialog's `DialogContent` — scopes in-dialog locators. */
4698
+ dialogName: 'evaluate-llm-picker',
4699
+ searchPlaceholder: 'Search providers...',
4700
+ /** Title of the follow-up model-selection modal (llm-tab's `LLMProviderModal`, no aria-label of its own). */
4701
+ modelDialogTitle: 'LLM Selection',
4702
+ },
4703
+ deleteDialog: {
4704
+ /** `aria-label` on the dialog's `DialogContent` — scopes in-dialog locators. */
4705
+ dialogName: 'delete-evaluation',
4706
+ cancel: 'Cancel',
4707
+ confirm: 'Delete',
4708
+ },
4709
+ };
4710
+ /**
4711
+ * Resolve a Page from either a Page or a Locator. Used for DOM that Radix
4712
+ * renders in a portal outside the current scope (dialogs, dropdown menus,
4713
+ * toasts) — those queries must run on the page, never on the tab pane or a
4714
+ * parent dialog.
4715
+ */
4716
+ function asPage$1(scope) {
4717
+ return 'page' in scope ? scope.page() : scope;
4718
+ }
4719
+ /** Escape a user-supplied string (run / benchmark names) for use in a RegExp. */
4720
+ function escapeRegExp(value) {
4721
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
4722
+ }
4723
+ /** Attribute-based dialog locator — immune to title renames and to Radix's `aria-labelledby` name precedence. */
4724
+ function dialogByName(scope, dialogName) {
4725
+ return asPage$1(scope).locator(`[role="dialog"][aria-label="${dialogName}"]`);
4726
+ }
4727
+ // ── Tab navigation ─────────────────────────────────────────────────────
4728
+ /**
4729
+ * Check whether the Evals tab is rendered in the Edit Mentor dialog.
4730
+ * Returns false instead of throwing so callers can guard permission-gated
4731
+ * tabs.
4732
+ */
4733
+ async function isEvaluationTabVisible(page) {
4734
+ const tab = page.getByRole('tab', { name: EVALS_LABELS.tabName, exact: true });
4735
+ try {
4736
+ await test$1.expect(tab).toBeVisible({ timeout: 5000 });
4737
+ return true;
4738
+ }
4739
+ catch (_a) {
4740
+ return false;
4741
+ }
4742
+ }
4743
+ /**
4744
+ * Switch to the Evals tab. Assumes the Edit Mentor dialog is open. Waits
4745
+ * for the toolbar "Manage benchmarks" button (unique to this pane) before
4746
+ * returning, so callers can rely on the panel being interactive.
4747
+ */
4748
+ async function switchToEvaluationTab(page) {
4749
+ const tab = page.getByRole('tab', { name: EVALS_LABELS.tabName, exact: true });
4750
+ await test$1.expect(tab).toBeVisible({ timeout: 10000 });
4751
+ await tab.click();
4752
+ await test$1.expect(page.getByRole('button', { name: EVALS_LABELS.actions.manageBenchmarks })).toBeVisible({ timeout: 10000 });
4753
+ logger.info('Switched to Evals tab');
4754
+ }
4755
+ // ── Toolbar: benchmark picker + action buttons ─────────────────────────
4756
+ /** Locator for the benchmark combobox trigger. Its text is the selected benchmark name. */
4757
+ function getBenchmarkCombobox(scope) {
4758
+ return scope.getByTestId(EVALS_LABELS.benchmarkPicker.triggerTestId);
4759
+ }
4760
+ /** Locator for the combobox dropdown panel (rendered inline under the trigger, not in a portal). */
4761
+ function getBenchmarkComboboxDropdown(scope) {
4762
+ return scope.getByTestId(EVALS_LABELS.benchmarkPicker.dropdownTestId);
4763
+ }
4764
+ /** Locator for the toolbar "New Evaluation" button (opens the start dialog). */
4765
+ function getNewEvaluationButton(scope) {
4766
+ return scope.getByRole('button', { name: EVALS_LABELS.actions.newExperiment, exact: true });
4767
+ }
4768
+ /** Locator for the toolbar "Manage benchmarks" button (opens the benchmarks dialog). */
4769
+ function getManageBenchmarksButton(scope) {
4770
+ return scope.getByRole('button', { name: EVALS_LABELS.actions.manageBenchmarks, exact: true });
4771
+ }
4772
+ /** Assert the currently selected benchmark shown on the combobox trigger. */
4773
+ async function expectSelectedBenchmark(scope, benchmarkName) {
4774
+ await test$1.expect(getBenchmarkCombobox(scope)).toContainText(benchmarkName, { timeout: 10000 });
4775
+ }
4776
+ /**
4777
+ * Open the benchmark combobox, optionally filter via its search input, and
4778
+ * pick a benchmark by exact name. Waits for the dropdown to close and the
4779
+ * trigger to reflect the selection.
4780
+ */
4781
+ async function selectBenchmark(scope, benchmarkName, opts = {}) {
4782
+ const trigger = getBenchmarkCombobox(scope);
4783
+ await test$1.expect(trigger).toBeEnabled({ timeout: 10000 });
4784
+ await trigger.click();
4785
+ const dropdown = getBenchmarkComboboxDropdown(scope);
4786
+ await test$1.expect(dropdown).toBeVisible({ timeout: 5000 });
4787
+ if (opts.search) {
4788
+ const search = dropdown.getByPlaceholder(EVALS_LABELS.benchmarkPicker.searchPlaceholder);
4789
+ await search.fill(benchmarkName);
4790
+ await test$1.expect(search).toHaveValue(benchmarkName);
4791
+ }
4792
+ await dropdown
4793
+ .getByRole('button', { name: new RegExp(`^${escapeRegExp(benchmarkName)}$`) })
4794
+ .click();
4795
+ await test$1.expect(dropdown).toBeHidden({ timeout: 5000 });
4796
+ await expectSelectedBenchmark(scope, benchmarkName);
4797
+ logger.info(`Selected benchmark: ${benchmarkName}`);
4798
+ }
4799
+ /** Assert the "no benchmarks yet" empty notice is shown in the tab body. */
4800
+ async function expectNoBenchmarksNotice(scope) {
4801
+ await test$1.expect(scope.getByText(EVALS_LABELS.emptyBenchmarks)).toBeVisible({ timeout: 10000 });
4802
+ }
4803
+ // ── Runs table ─────────────────────────────────────────────────────────
4804
+ /**
4805
+ * Locator for an evaluation run's table row, matched by an exact cell so a
4806
+ * run whose name is a substring of another ("smoke" vs "smoke-2") can't
4807
+ * cross-match.
4808
+ */
4809
+ function getRunRow(scope, runName) {
4810
+ return scope
4811
+ .getByRole('row')
4812
+ .filter({ has: asPage$1(scope).getByRole('cell', { name: runName, exact: true }) });
4813
+ }
4814
+ async function expectRunInTable(scope, runName) {
4815
+ await test$1.expect(getRunRow(scope, runName)).toBeVisible({ timeout: 15000 });
4816
+ }
4817
+ async function expectRunNotInTable(scope, runName) {
4818
+ await test$1.expect(getRunRow(scope, runName)).toBeHidden({ timeout: 15000 });
4819
+ }
4820
+ /** Assert the status badge on a run's row (badge text is upper-cased by the UI). */
4821
+ async function expectRunStatus(scope, runName, status) {
4822
+ await test$1.expect(getRunRow(scope, runName).getByText(status, { exact: true })).toBeVisible({
4823
+ timeout: 15000,
4824
+ });
4825
+ }
4826
+ /** Assert the empty-state row shown when the selected benchmark has no runs for this agent. */
4827
+ async function expectRunsTableEmpty(scope, benchmarkName) {
4828
+ await test$1.expect(scope.getByText(`No evaluations yet for this agent on benchmark "${benchmarkName}".`)).toBeVisible({ timeout: 15000 });
4829
+ }
4830
+ /**
4831
+ * Open the three-dots actions menu on a run's row. The trigger carries
4832
+ * `aria-label="Actions for <runName>"`, so it can't collide with other rows.
4833
+ * Returns the menu locator (Radix portals it outside the table — page scope).
4834
+ */
4835
+ async function openRunActionsMenu(scope, runName) {
4836
+ await getRunRow(scope, runName)
4837
+ .getByRole('button', { name: `Actions for ${runName}`, exact: true })
4838
+ .click();
4839
+ const menu = asPage$1(scope).getByRole('menu');
4840
+ await test$1.expect(menu).toBeVisible({ timeout: 5000 });
4841
+ return menu;
4842
+ }
4843
+ /**
4844
+ * Row action: "View results" — opens the evaluation detail dialog.
4845
+ * Only enabled once the run is COMPLETED. Returns the scoped detail dialog.
4846
+ */
4847
+ async function openRunResults(scope, runName) {
4848
+ const menu = await openRunActionsMenu(scope, runName);
4849
+ const item = menu.getByRole('menuitem', { name: EVALS_LABELS.actions.viewResults });
4850
+ await test$1.expect(item).toBeEnabled({ timeout: 5000 });
4851
+ await item.click();
4852
+ const dialog = getEvaluationDetailDialog(scope);
4853
+ await test$1.expect(dialog).toBeVisible({ timeout: 10000 });
4854
+ logger.info(`Opened results for run: ${runName}`);
4855
+ return dialog;
4856
+ }
4857
+ /**
4858
+ * Row action: "New review" — opens the LLM-judge dialog for the run.
4859
+ * Only enabled once the run is COMPLETED. Returns the scoped judge dialog.
4860
+ */
4861
+ async function openNewReviewForRun(scope, runName) {
4862
+ const menu = await openRunActionsMenu(scope, runName);
4863
+ const item = menu.getByRole('menuitem', { name: EVALS_LABELS.actions.evaluate });
4864
+ await test$1.expect(item).toBeEnabled({ timeout: 5000 });
4865
+ await item.click();
4866
+ const dialog = getLlmJudgeDialog(scope);
4867
+ await test$1.expect(dialog).toBeVisible({ timeout: 10000 });
4868
+ return dialog;
4869
+ }
4870
+ /**
4871
+ * Row action: "Check status" — triggers a fresh status fetch and waits for
4872
+ * the outcome toast. Returns which toast appeared. The menu stays open by
4873
+ * design (the item prevents default), so we close it with Escape.
4874
+ */
4875
+ async function checkRunStatus(scope, runName) {
4876
+ const page = asPage$1(scope);
4877
+ const menu = await openRunActionsMenu(scope, runName);
4878
+ await menu.getByRole('menuitem', { name: EVALS_LABELS.actions.checkStatus }).click();
4879
+ const toasts = EVALS_LABELS.checkStatusToast;
4880
+ const toast = page
4881
+ .getByText(new RegExp(`${toasts.pending}|${toasts.completed}|${toasts.failed}`))
4882
+ .first();
4883
+ await test$1.expect(toast).toBeVisible({ timeout: 15000 });
4884
+ const text = (await toast.innerText()).trim();
4885
+ await page.keyboard.press('Escape');
4886
+ await test$1.expect(menu).toBeHidden({ timeout: 5000 });
4887
+ if (text.includes(toasts.completed))
4888
+ return 'completed';
4889
+ if (text.includes(toasts.failed))
4890
+ return 'failed';
4891
+ return 'pending';
4892
+ }
4893
+ /**
4894
+ * Row action: "Export CSV" — waits for the browser download and returns its
4895
+ * suggested filename (`<benchmark>_<run>_results.csv`).
4896
+ */
4897
+ async function exportRunCsv(scope, runName) {
4898
+ const page = asPage$1(scope);
4899
+ const menu = await openRunActionsMenu(scope, runName);
4900
+ const item = menu.getByRole('menuitem', { name: EVALS_LABELS.actions.exportCsv });
4901
+ await test$1.expect(item).toBeEnabled({ timeout: 5000 });
4902
+ const downloadPromise = page.waitForEvent('download', { timeout: 30000 });
4903
+ await item.click();
4904
+ const download = await downloadPromise;
4905
+ logger.info(`Exported run CSV: ${download.suggestedFilename()}`);
4906
+ return download.suggestedFilename();
4907
+ }
4908
+ // ── Start evaluation dialog ────────────────────────────────────────────
4909
+ /** Locator for the "New Evaluation" dialog. Scoped by aria-label — its title text collides with the toolbar button that opens it. */
4910
+ function getStartEvaluationDialog(scope) {
4911
+ return dialogByName(scope, EVALS_LABELS.startDialog.dialogName);
4912
+ }
4913
+ /** Open the start dialog via the toolbar button and wait for it to be interactive. */
4914
+ async function openStartEvaluationDialog(scope) {
4915
+ await getNewEvaluationButton(scope).click();
4916
+ const dialog = getStartEvaluationDialog(scope);
4917
+ await test$1.expect(dialog.getByLabel(EVALS_LABELS.startDialog.experimentNameLabel)).toBeVisible({
4918
+ timeout: 10000,
4919
+ });
4920
+ return dialog;
4921
+ }
4922
+ /**
4923
+ * End-to-end: open the "New Evaluation" dialog, optionally name the run,
4924
+ * submit, and wait for the dialog to close. When `name` is omitted the
4925
+ * backend auto-generates one.
4926
+ */
4927
+ async function startEvaluation(scope, opts = {}) {
4928
+ const dialog = await openStartEvaluationDialog(scope);
4929
+ if (opts.name !== undefined) {
4930
+ await dialog.getByLabel(EVALS_LABELS.startDialog.experimentNameLabel).fill(opts.name);
4931
+ }
4932
+ // Scoped to the dialog: "Start Evaluation" is unique today, but the toolbar
4933
+ // "New Evaluation" trigger sits right behind the overlay — never query the page.
4934
+ const submit = dialog.getByRole('button', { name: EVALS_LABELS.startDialog.start });
4935
+ await test$1.expect(submit).toBeEnabled({ timeout: 5000 });
4936
+ await submit.click();
4937
+ await test$1.expect(dialog).toBeHidden({ timeout: 15000 });
4938
+ logger.info(`Started evaluation${opts.name ? `: ${opts.name}` : ''}`);
4939
+ }
4940
+ /** Cancel out of the start dialog and wait for it to close. */
4941
+ async function cancelStartEvaluation(scope) {
4942
+ const dialog = getStartEvaluationDialog(scope);
4943
+ await dialog.getByRole('button', { name: EVALS_LABELS.startDialog.cancel }).click();
4944
+ await test$1.expect(dialog).toBeHidden({ timeout: 5000 });
4945
+ }
4946
+ // ── Delete evaluation dialog ───────────────────────────────────────────
4947
+ /** Locator for the "Delete Evaluation" confirm dialog. */
4948
+ function getDeleteEvaluationDialog(scope) {
4949
+ return dialogByName(scope, EVALS_LABELS.deleteDialog.dialogName);
4950
+ }
4951
+ /**
4952
+ * Delete a run from its row actions menu and confirm in the follow-up
4953
+ * dialog. The confirm button ("Delete") shares its name with the menu item
4954
+ * that opened the dialog, so the confirm click MUST stay dialog-scoped.
4955
+ */
4956
+ async function deleteEvaluation(scope, runName) {
4957
+ const menu = await openRunActionsMenu(scope, runName);
4958
+ await menu.getByRole('menuitem', { name: EVALS_LABELS.actions.delete }).click();
4959
+ const dialog = getDeleteEvaluationDialog(scope);
4960
+ const confirm = dialog.getByRole('button', { name: EVALS_LABELS.deleteDialog.confirm });
4961
+ await test$1.expect(confirm).toBeVisible({ timeout: 5000 });
4962
+ await confirm.click();
4963
+ await test$1.expect(dialog).toBeHidden({ timeout: 15000 });
4964
+ logger.info(`Deleted evaluation: ${runName}`);
4965
+ }
4966
+ /** Open the delete confirm from the row menu, then cancel out of it. */
4967
+ async function cancelDeleteEvaluation(scope, runName) {
4968
+ const menu = await openRunActionsMenu(scope, runName);
4969
+ await menu.getByRole('menuitem', { name: EVALS_LABELS.actions.delete }).click();
4970
+ const dialog = getDeleteEvaluationDialog(scope);
4971
+ await dialog.getByRole('button', { name: EVALS_LABELS.deleteDialog.cancel }).click();
4972
+ await test$1.expect(dialog).toBeHidden({ timeout: 5000 });
4973
+ }
4974
+ // ── LLM-judge ("Evaluate") dialog ──────────────────────────────────────
4975
+ /**
4976
+ * Locator for the LLM-judge dialog. Scoped by aria-label — its title AND its
4977
+ * submit button both read "Evaluate", and it can be stacked on top of the
4978
+ * evaluation-detail dialog.
4979
+ */
4980
+ function getLlmJudgeDialog(scope) {
4981
+ return dialogByName(scope, EVALS_LABELS.judgeDialog.dialogName);
4982
+ }
4983
+ /** Locator for the judge dialog's LLM provider-picker dialog (stacked above it). */
4984
+ function getLlmPickerDialog(scope) {
4985
+ return dialogByName(scope, EVALS_LABELS.llmPickerDialog.dialogName);
4986
+ }
4987
+ /**
4988
+ * Inside the judge dialog: pick the judging LLM. Opens the provider picker,
4989
+ * optionally filters, clicks the provider card, then picks the model in the
4990
+ * follow-up "LLM Selection" modal (llm-tab's `LLMProviderModal`; matched by
4991
+ * title text since it has no aria-label). Both pickers close on selection.
4992
+ */
4993
+ async function selectJudgeLlm(scope, providerName, modelName) {
4994
+ const page = asPage$1(scope);
4995
+ const judgeDialog = getLlmJudgeDialog(scope);
4996
+ await judgeDialog
4997
+ .getByRole('button', { name: EVALS_LABELS.judgeDialog.selectorAriaLabel })
4998
+ .click();
4999
+ const picker = getLlmPickerDialog(scope);
5000
+ await test$1.expect(picker).toBeVisible({ timeout: 10000 });
5001
+ await picker.getByPlaceholder(EVALS_LABELS.llmPickerDialog.searchPlaceholder).fill(providerName);
5002
+ await picker.getByRole('button', { name: new RegExp(escapeRegExp(providerName), 'i') }).click();
5003
+ // Model-selection modal is a separate stacked dialog outside the picker.
5004
+ const modelDialog = page
5005
+ .getByRole('dialog')
5006
+ .filter({ hasText: EVALS_LABELS.llmPickerDialog.modelDialogTitle });
5007
+ await test$1.expect(modelDialog).toBeVisible({ timeout: 10000 });
5008
+ await modelDialog.getByRole('button', { name: new RegExp(escapeRegExp(modelName), 'i') }).click();
5009
+ await test$1.expect(picker).toBeHidden({ timeout: 10000 });
5010
+ logger.info(`Selected judge LLM ${providerName}/${modelName}`);
5011
+ }
5012
+ /**
5013
+ * Fill and submit the LLM-judge dialog (assumes it is already open — see
5014
+ * `openNewReviewForRun` / `openNewReviewFromDetail`). Waits for the dialog
5015
+ * to close on success.
5016
+ */
5017
+ async function submitLlmJudge(scope, opts) {
5018
+ const dialog = getLlmJudgeDialog(scope);
5019
+ await test$1.expect(dialog).toBeVisible({ timeout: 10000 });
5020
+ await dialog.getByLabel(EVALS_LABELS.judgeDialog.criteriaLabel).fill(opts.criteria);
5021
+ await dialog.getByLabel(EVALS_LABELS.judgeDialog.scoreNameLabel).fill(opts.scoreName);
5022
+ if (opts.maxConcurrency !== undefined) {
5023
+ await dialog
5024
+ .getByLabel(EVALS_LABELS.judgeDialog.maxConcurrencyLabel)
5025
+ .fill(String(opts.maxConcurrency));
5026
+ }
5027
+ await selectJudgeLlm(scope, opts.provider, opts.model);
5028
+ // Dialog-scoped on purpose: the dialog title is also "Evaluate".
5029
+ const submit = dialog.getByRole('button', { name: EVALS_LABELS.judgeDialog.submit, exact: true });
5030
+ await test$1.expect(submit).toBeEnabled({ timeout: 5000 });
5031
+ await submit.click();
5032
+ await test$1.expect(dialog).toBeHidden({ timeout: 15000 });
5033
+ logger.info(`Started LLM review "${opts.scoreName}"`);
5034
+ }
5035
+ /** Cancel out of the judge dialog and wait for it to close. */
5036
+ async function cancelLlmJudge(scope) {
5037
+ const dialog = getLlmJudgeDialog(scope);
5038
+ await dialog.getByRole('button', { name: EVALS_LABELS.judgeDialog.cancel }).click();
5039
+ await test$1.expect(dialog).toBeHidden({ timeout: 5000 });
5040
+ }
5041
+ // ── Evaluation detail dialog ───────────────────────────────────────────
5042
+ /** Locator for the evaluation-detail dialog. Its title is the run name (dynamic), hence the aria-label hook. */
5043
+ function getEvaluationDetailDialog(scope) {
5044
+ return dialogByName(scope, EVALS_LABELS.detailDialog.dialogName);
5045
+ }
5046
+ /** Click the detail dialog's refresh icon button (aria-label "Refresh"). */
5047
+ async function refreshEvaluationDetail(scope) {
5048
+ const dialog = getEvaluationDetailDialog(scope);
5049
+ const refresh = dialog.getByRole('button', {
5050
+ name: EVALS_LABELS.detailDialog.refreshAriaLabel,
5051
+ exact: true,
5052
+ });
5053
+ await refresh.click();
5054
+ await test$1.expect(refresh).toBeEnabled({ timeout: 15000 });
5055
+ }
5056
+ /** Open the LLM-judge dialog from the detail dialog's "New review" CTA. Returns the judge dialog. */
5057
+ async function openNewReviewFromDetail(scope) {
5058
+ const dialog = getEvaluationDetailDialog(scope);
5059
+ const cta = dialog.getByRole('button', {
5060
+ name: EVALS_LABELS.detailDialog.newReview,
5061
+ exact: true,
5062
+ });
5063
+ await test$1.expect(cta).toBeEnabled({ timeout: 10000 });
5064
+ await cta.click();
5065
+ const judgeDialog = getLlmJudgeDialog(scope);
5066
+ await test$1.expect(judgeDialog).toBeVisible({ timeout: 10000 });
5067
+ return judgeDialog;
5068
+ }
5069
+ /** Assert the detail dialog is still waiting for traces (auto-refreshing hint). */
5070
+ async function expectDetailWaitingForTraces(scope) {
5071
+ await test$1.expect(getEvaluationDetailDialog(scope).getByText(EVALS_LABELS.detailDialog.waitingHint)).toBeVisible({ timeout: 10000 });
5072
+ }
5073
+ /**
5074
+ * Locator for a trace's expand/collapse row inside the detail dialog,
5075
+ * matched by (part of) its question text.
5076
+ */
5077
+ function getTraceRow(scope, questionText) {
5078
+ return getEvaluationDetailDialog(scope)
5079
+ .getByRole('button')
5080
+ .filter({ hasText: questionText })
5081
+ .first();
5082
+ }
5083
+ /** Expand a trace by its question text so its outputs and scores are visible. */
5084
+ async function expandTrace(scope, questionText) {
5085
+ await getTraceRow(scope, questionText).click();
5086
+ // The manual-score form only exists on the expanded trace.
5087
+ await test$1.expect(getEvaluationDetailDialog(scope).getByLabel(EVALS_LABELS.detailDialog.scoreNameLabel)).toBeVisible({ timeout: 10000 });
5088
+ }
5089
+ /**
5090
+ * Add a manual score to the currently expanded trace. `value` must be
5091
+ * between 0 and 1 (the UI rejects anything else with an error toast).
5092
+ */
5093
+ async function addManualScore(scope, opts) {
5094
+ const dialog = getEvaluationDetailDialog(scope);
5095
+ await dialog.getByLabel(EVALS_LABELS.detailDialog.scoreNameLabel).fill(opts.name);
5096
+ await dialog.getByLabel(EVALS_LABELS.detailDialog.scoreValueLabel).fill(String(opts.value));
5097
+ if (opts.comment !== undefined) {
5098
+ await dialog.getByLabel(EVALS_LABELS.detailDialog.scoreCommentLabel).fill(opts.comment);
5099
+ }
5100
+ await dialog
5101
+ .getByRole('button', { name: EVALS_LABELS.detailDialog.addScore, exact: true })
5102
+ .click();
5103
+ await expectTraceScore(scope, opts.name);
5104
+ logger.info(`Added manual score: ${opts.name}=${opts.value}`);
5105
+ }
5106
+ /** Assert a score chip with the given name is visible on the expanded trace. */
5107
+ async function expectTraceScore(scope, scoreName) {
5108
+ await test$1.expect(getEvaluationDetailDialog(scope).getByText(scoreName, { exact: true }).first()).toBeVisible({ timeout: 15000 });
5109
+ }
5110
+ /**
5111
+ * Remove a score chip from the expanded trace. The chip's trash button
5112
+ * carries `aria-label="Remove score"`; we scope through the chip matched by
5113
+ * score name so multiple chips can't cross-match.
5114
+ */
5115
+ async function removeTraceScore(scope, scoreName) {
5116
+ const dialog = getEvaluationDetailDialog(scope);
5117
+ const chip = dialog
5118
+ .locator('div')
5119
+ .filter({ has: asPage$1(scope).getByText(scoreName, { exact: true }) })
5120
+ .filter({
5121
+ has: asPage$1(scope).getByRole('button', {
5122
+ name: EVALS_LABELS.detailDialog.removeScoreAriaLabel,
5123
+ }),
5124
+ })
5125
+ .last();
5126
+ await chip
5127
+ .getByRole('button', { name: EVALS_LABELS.detailDialog.removeScoreAriaLabel })
5128
+ .first()
5129
+ .click();
5130
+ }
5131
+ /**
5132
+ * Locator for a review row's expand toggle in the detail dialog's Reviews
5133
+ * list. All toggles share the "Toggle review details" aria-label, so the
5134
+ * row is disambiguated by the score name it displays.
5135
+ */
5136
+ function getReviewRow(scope, scoreName) {
5137
+ return getEvaluationDetailDialog(scope)
5138
+ .getByRole('button', { name: EVALS_LABELS.detailDialog.reviewDetailsAriaLabel })
5139
+ .filter({ hasText: scoreName })
5140
+ .first();
5141
+ }
5142
+ /** Assert a review with the given score name appears in the Reviews list. */
5143
+ async function expectReviewInList(scope, scoreName) {
5144
+ await test$1.expect(getReviewRow(scope, scoreName)).toBeVisible({ timeout: 15000 });
5145
+ }
5146
+ /** Assert a review row's status pill (Queued / Running / Complete / Failed). */
5147
+ async function expectReviewStatus(scope, scoreName, status) {
5148
+ await test$1.expect(getReviewRow(scope, scoreName).getByText(EVALS_LABELS.detailDialog.reviewStatus[status], {
5149
+ exact: true,
5150
+ })).toBeVisible({ timeout: 15000 });
5151
+ }
5152
+ /** Expand a review row to show its criteria and produced scores. */
5153
+ async function expandReview(scope, scoreName) {
5154
+ const row = getReviewRow(scope, scoreName);
5155
+ await row.click();
5156
+ await test$1.expect(row).toHaveAttribute('aria-expanded', 'true', { timeout: 5000 });
5157
+ }
5158
+ /** Close the detail dialog via Escape and wait for it to be gone. */
5159
+ async function closeEvaluationDetailDialog(scope) {
5160
+ const dialog = getEvaluationDetailDialog(scope);
5161
+ await asPage$1(scope).keyboard.press('Escape');
5162
+ await test$1.expect(dialog).toBeHidden({ timeout: 10000 });
5163
+ }
5164
+ // ── Manage benchmarks dialog (embedded tenant Benchmarks) ──────────────
5165
+ /** Locator for the "Manage benchmarks" dialog. Scoped by aria-label — its title collides with the toolbar button that opens it. */
5166
+ function getManageBenchmarksDialog(scope) {
5167
+ return dialogByName(scope, EVALS_LABELS.manageBenchmarksDialog.dialogName);
5168
+ }
5169
+ /** Open the Manage benchmarks dialog from the toolbar and wait for its table UI. Returns the scoped dialog. */
5170
+ async function openManageBenchmarksDialog(scope) {
5171
+ await getManageBenchmarksButton(scope).click();
5172
+ const dialog = getManageBenchmarksDialog(scope);
5173
+ await test$1.expect(dialog.getByRole('button', { name: EVALS_LABELS.manageBenchmarksDialog.newBenchmark })).toBeVisible({ timeout: 10000 });
5174
+ return dialog;
5175
+ }
5176
+ /** Type into the benchmarks search input (aria-label "Search benchmarks") inside the manage dialog. */
5177
+ async function searchBenchmarks(scope, query) {
5178
+ const input = getManageBenchmarksDialog(scope).getByLabel(EVALS_LABELS.manageBenchmarksDialog.searchAriaLabel);
5179
+ await input.fill(query);
5180
+ await test$1.expect(input).toHaveValue(query);
5181
+ }
5182
+ /** Assert a benchmark appears in the manage dialog's table. */
5183
+ async function expectBenchmarkListed(scope, benchmarkName) {
5184
+ await test$1.expect(getManageBenchmarksDialog(scope).getByText(benchmarkName, { exact: true }).first()).toBeVisible({ timeout: 15000 });
5185
+ }
5186
+ /** Locator for the "New Benchmark" dialog. Scoped by aria-label — its title collides with the button that opens it. */
5187
+ function getCreateBenchmarkDialog(scope) {
5188
+ return dialogByName(scope, EVALS_LABELS.createBenchmarkDialog.dialogName);
5189
+ }
5190
+ /**
5191
+ * Create a benchmark from inside the manage dialog: open "New Benchmark",
5192
+ * fill the form, submit, and wait for both the dialog to close and the new
5193
+ * row to appear.
5194
+ */
5195
+ async function createBenchmark(scope, opts) {
5196
+ const manageDialog = getManageBenchmarksDialog(scope);
5197
+ await manageDialog
5198
+ .getByRole('button', { name: EVALS_LABELS.manageBenchmarksDialog.newBenchmark })
5199
+ .click();
5200
+ const dialog = getCreateBenchmarkDialog(scope);
5201
+ await dialog.getByLabel(EVALS_LABELS.createBenchmarkDialog.nameLabel).fill(opts.name);
5202
+ if (opts.description !== undefined) {
5203
+ await dialog
5204
+ .getByLabel(EVALS_LABELS.createBenchmarkDialog.descriptionLabel)
5205
+ .fill(opts.description);
5206
+ }
5207
+ const submit = dialog.getByRole('button', {
5208
+ name: EVALS_LABELS.createBenchmarkDialog.create,
5209
+ exact: true,
5210
+ });
5211
+ await test$1.expect(submit).toBeEnabled({ timeout: 5000 });
5212
+ await submit.click();
5213
+ await test$1.expect(dialog).toBeHidden({ timeout: 15000 });
5214
+ await expectBenchmarkListed(scope, opts.name);
5215
+ logger.info(`Created benchmark: ${opts.name}`);
5216
+ }
5217
+ /** Locator for a benchmark's items dialog (Q&A list). Its title is the benchmark name (dynamic), hence the aria-label hook. */
5218
+ function getBenchmarkItemsDialog(scope) {
5219
+ return dialogByName(scope, EVALS_LABELS.benchmarkItemsDialog.dialogName);
5220
+ }
5221
+ /**
5222
+ * Open a benchmark's Q&A items dialog from its row's eye button
5223
+ * (`aria-label="View items in <name>"`). Returns the scoped items dialog.
5224
+ */
5225
+ async function openBenchmarkItems(scope, benchmarkName) {
5226
+ await getManageBenchmarksDialog(scope)
5227
+ .getByRole('button', { name: `View items in ${benchmarkName}`, exact: true })
5228
+ .click();
5229
+ const dialog = getBenchmarkItemsDialog(scope);
5230
+ await test$1.expect(dialog.getByRole('button', { name: EVALS_LABELS.benchmarkItemsDialog.addItems })).toBeVisible({ timeout: 10000 });
5231
+ return dialog;
5232
+ }
5233
+ /** Assert a Q&A item (matched by its question text) is listed in the items dialog. */
5234
+ async function expectQaItemListed(scope, questionText) {
5235
+ await test$1.expect(getBenchmarkItemsDialog(scope).getByText(questionText, { exact: false }).first()).toBeVisible({ timeout: 15000 });
5236
+ }
5237
+ /** Locator for the "Add Q&A" dialog (Manual / CSV Upload sub-tabs). */
5238
+ function getAddItemsDialog(scope) {
5239
+ return dialogByName(scope, EVALS_LABELS.addItemsDialog.dialogName);
5240
+ }
5241
+ /** Open the "Add Q&A" dialog from the items dialog. Returns the scoped add dialog. */
5242
+ async function openAddItemsDialog(scope) {
5243
+ await getBenchmarkItemsDialog(scope)
5244
+ .getByRole('button', { name: EVALS_LABELS.benchmarkItemsDialog.addItems })
5245
+ .click();
5246
+ const dialog = getAddItemsDialog(scope);
5247
+ // Both sub-tab triggers are unique to this dialog — a reliable ready signal.
5248
+ await test$1.expect(dialog.getByRole('tab', { name: EVALS_LABELS.addItemsDialog.manualTab, exact: true })).toBeVisible({ timeout: 10000 });
5249
+ return dialog;
5250
+ }
5251
+ /**
5252
+ * Switch to a sub-tab INSIDE the Add Q&A dialog. The tab query must stay
5253
+ * dialog-scoped: the edit-mentor modal behind it has its own `role="tab"`
5254
+ * strip, and an unscoped `getByRole('tab')` can cross-match. Returns the
5255
+ * active tabpanel so callers interact only with that sub-tab's controls
5256
+ * (each panel has its own footer with its own Cancel/submit buttons).
5257
+ */
5258
+ async function switchToAddItemsSubTab(scope, subTab) {
5259
+ const dialog = getAddItemsDialog(scope);
5260
+ const tabName = subTab === 'manual'
5261
+ ? EVALS_LABELS.addItemsDialog.manualTab
5262
+ : EVALS_LABELS.addItemsDialog.csvTab;
5263
+ const tab = dialog.getByRole('tab', { name: tabName, exact: true });
5264
+ await tab.click();
5265
+ await test$1.expect(tab).toHaveAttribute('data-state', 'active', { timeout: 5000 });
5266
+ const panel = dialog.getByRole('tabpanel');
5267
+ await test$1.expect(panel).toBeVisible({ timeout: 5000 });
5268
+ return panel;
5269
+ }
5270
+ /**
5271
+ * Add Q&A pairs via the Manual sub-tab. Fills one row per pair (clicking
5272
+ * "Add row" as needed), submits, and waits for the dialog to close. Row
5273
+ * inputs are matched by label WITHIN each row container so the Question /
5274
+ * Answer labels of sibling rows can't collide.
5275
+ */
5276
+ async function addQaPairsManually(scope, pairs) {
5277
+ const dialog = getAddItemsDialog(scope);
5278
+ const panel = await switchToAddItemsSubTab(scope, 'manual');
5279
+ for (let i = 0; i < pairs.length; i++) {
5280
+ if (i > 0) {
5281
+ await panel
5282
+ .getByRole('button', { name: EVALS_LABELS.addItemsDialog.addRow, exact: true })
5283
+ .click();
5284
+ }
5285
+ const question = panel.getByLabel(EVALS_LABELS.addItemsDialog.questionLabel).nth(i);
5286
+ await test$1.expect(question).toBeVisible({ timeout: 5000 });
5287
+ await question.fill(pairs[i].question);
5288
+ if (pairs[i].answer !== undefined) {
5289
+ await panel
5290
+ .getByLabel(EVALS_LABELS.addItemsDialog.answerLabel)
5291
+ .nth(i)
5292
+ .fill(pairs[i].answer);
5293
+ }
5294
+ }
5295
+ await panel.getByRole('button', { name: EVALS_LABELS.addItemsDialog.add, exact: true }).click();
5296
+ await test$1.expect(dialog).toBeHidden({ timeout: 15000 });
5297
+ logger.info(`Added ${pairs.length} Q&A pair(s) manually`);
5298
+ }
5299
+ /**
5300
+ * Upload Q&A pairs via the CSV Upload sub-tab. `csvPath` is a path on the
5301
+ * test machine; the file needs `input` (required) and `expected_output`
5302
+ * (optional) columns. Waits for the dialog to close on success.
5303
+ */
5304
+ async function uploadQaCsv(scope, csvPath) {
5305
+ const dialog = getAddItemsDialog(scope);
5306
+ const panel = await switchToAddItemsSubTab(scope, 'csv');
5307
+ await panel.getByLabel(EVALS_LABELS.addItemsDialog.csvFileLabel).setInputFiles(csvPath);
5308
+ await panel
5309
+ .getByRole('button', { name: EVALS_LABELS.addItemsDialog.upload, exact: true })
5310
+ .click();
5311
+ await test$1.expect(dialog).toBeHidden({ timeout: 30000 });
5312
+ logger.info(`Uploaded Q&A CSV: ${csvPath}`);
5313
+ }
5314
+ /** Locator for the Q&A delete-confirm dialog. */
5315
+ function getDeleteQaItemDialog(scope) {
5316
+ return dialogByName(scope, EVALS_LABELS.deleteItemDialog.dialogName);
5317
+ }
5318
+ /**
5319
+ * Delete a Q&A item by its question text: click the row's trash button
5320
+ * (`aria-label="Delete Q&A"`) and confirm. The confirm "Delete" button must
5321
+ * stay scoped to the confirm dialog — the items dialog's rows keep their
5322
+ * own delete buttons mounted behind it.
5323
+ */
5324
+ async function deleteQaItem(scope, questionText) {
5325
+ const itemsDialog = getBenchmarkItemsDialog(scope);
5326
+ const row = itemsDialog.getByRole('row').filter({ hasText: questionText }).first();
5327
+ await row
5328
+ .getByRole('button', { name: EVALS_LABELS.benchmarkItemsDialog.deleteItemAriaLabel })
5329
+ .click();
5330
+ const confirmDialog = getDeleteQaItemDialog(scope);
5331
+ const confirm = confirmDialog.getByRole('button', {
5332
+ name: EVALS_LABELS.deleteItemDialog.delete,
5333
+ exact: true,
5334
+ });
5335
+ await test$1.expect(confirm).toBeVisible({ timeout: 5000 });
5336
+ await confirm.click();
5337
+ await test$1.expect(confirmDialog).toBeHidden({ timeout: 15000 });
5338
+ logger.info(`Deleted Q&A item: ${questionText}`);
5339
+ }
5340
+ /** Close the benchmark items dialog via Escape and wait for it to be gone. */
5341
+ async function closeBenchmarkItemsDialog(scope) {
5342
+ const dialog = getBenchmarkItemsDialog(scope);
5343
+ await asPage$1(scope).keyboard.press('Escape');
5344
+ await test$1.expect(dialog).toBeHidden({ timeout: 10000 });
5345
+ }
5346
+ /** Close the Manage benchmarks dialog via Escape and wait for it to be gone. */
5347
+ async function closeManageBenchmarksDialog(scope) {
5348
+ const dialog = getManageBenchmarksDialog(scope);
5349
+ await asPage$1(scope).keyboard.press('Escape');
5350
+ await test$1.expect(dialog).toBeHidden({ timeout: 10000 });
5351
+ }
5352
+
4555
5353
  /**
4556
5354
  * LTI tab helpers — Playwright bindings for the `AgentLtiTab` component from
4557
5355
  * `@iblai/web-containers`.
@@ -5214,6 +6012,7 @@ Object.defineProperty(exports, "expect", {
5214
6012
  exports.AuthFlowBuilder = AuthFlowBuilder;
5215
6013
  exports.CHAT_PRIVACY_LABELS = CHAT_PRIVACY_LABELS;
5216
6014
  exports.CustomReporter = CustomReporter;
6015
+ exports.EVALS_LABELS = EVALS_LABELS;
5217
6016
  exports.LTI_LABELS = LTI_LABELS;
5218
6017
  exports.LTI_TEST_IDS = LTI_TEST_IDS;
5219
6018
  exports.MailsacClient = MailsacClient;
@@ -5221,7 +6020,9 @@ exports.PRIVACY_LABELS = PRIVACY_LABELS;
5221
6020
  exports.SCREENSHARE_LABELS = SCREENSHARE_LABELS;
5222
6021
  exports.TASKS_LABELS = TASKS_LABELS;
5223
6022
  exports.VOICE_LABELS = VOICE_LABELS;
6023
+ exports.addManualScore = addManualScore;
5224
6024
  exports.addMemory = addMemory;
6025
+ exports.addQaPairsManually = addQaPairsManually;
5225
6026
  exports.archiveFirstMemory = archiveFirstMemory;
5226
6027
  exports.archiveMemoryByContent = archiveMemoryByContent;
5227
6028
  exports.billingAutoRechargeSection = billingAutoRechargeSection;
@@ -5229,13 +6030,17 @@ exports.billingCreditsSection = billingCreditsSection;
5229
6030
  exports.billingPlanSection = billingPlanSection;
5230
6031
  exports.buildReportUrl = buildReportUrl;
5231
6032
  exports.canChatWithEmbedMentor = canChatWithEmbedMentor;
6033
+ exports.cancelDeleteEvaluation = cancelDeleteEvaluation;
5232
6034
  exports.cancelDeleteInstance = cancelDeleteInstance;
5233
6035
  exports.cancelDeleteSkill = cancelDeleteSkill;
5234
6036
  exports.cancelDisconnectInstance = cancelDisconnectInstance;
5235
6037
  exports.cancelEnableChatPrivacyMidSession = cancelEnableChatPrivacyMidSession;
5236
6038
  exports.cancelKeyDelete = cancelKeyDelete;
6039
+ exports.cancelLlmJudge = cancelLlmJudge;
5237
6040
  exports.cancelNewInstanceDialog = cancelNewInstanceDialog;
6041
+ exports.cancelStartEvaluation = cancelStartEvaluation;
5238
6042
  exports.checkAdminStatus = checkAdminStatus;
6043
+ exports.checkRunStatus = checkRunStatus;
5239
6044
  exports.clearDateRangeFilter = clearDateRangeFilter;
5240
6045
  exports.clearInstanceSearch = clearInstanceSearch;
5241
6046
  exports.clickBackHome = clickBackHome;
@@ -5246,14 +6051,18 @@ exports.clickBillingUpgrade = clickBillingUpgrade;
5246
6051
  exports.clickChatPrivacyToggle = clickChatPrivacyToggle;
5247
6052
  exports.clickDownloadAgain = clickDownloadAgain;
5248
6053
  exports.clickManualDownloadLink = clickManualDownloadLink;
6054
+ exports.closeBenchmarkItemsDialog = closeBenchmarkItemsDialog;
5249
6055
  exports.closeCreditBalanceDropdown = closeCreditBalanceDropdown;
6056
+ exports.closeEvaluationDetailDialog = closeEvaluationDetailDialog;
5250
6057
  exports.closeKeyDetail = closeKeyDetail;
6058
+ exports.closeManageBenchmarksDialog = closeManageBenchmarksDialog;
5251
6059
  exports.closeWithEsc = closeWithEsc;
5252
6060
  exports.confirmEnableChatPrivacyMidSession = confirmEnableChatPrivacyMidSession;
5253
6061
  exports.confirmKeyDelete = confirmKeyDelete;
5254
6062
  exports.connectToInstance = connectToInstance;
5255
6063
  exports.copyEndpoint = copyEndpoint;
5256
6064
  exports.createAuthSetup = createAuthSetup;
6065
+ exports.createBenchmark = createBenchmark;
5257
6066
  exports.createEnvConfig = createEnvConfig;
5258
6067
  exports.createInstance = createInstance;
5259
6068
  exports.createKey = createKey;
@@ -5264,10 +6073,12 @@ exports.createTool = createTool;
5264
6073
  exports.creditBalancePanel = creditBalancePanel;
5265
6074
  exports.creditBalancePlanBadge = creditBalancePlanBadge;
5266
6075
  exports.creditBalanceTrigger = creditBalanceTrigger;
6076
+ exports.deleteEvaluation = deleteEvaluation;
5267
6077
  exports.deleteFirstMemory = deleteFirstMemory;
5268
6078
  exports.deleteInstance = deleteInstance;
5269
6079
  exports.deleteKey = deleteKey;
5270
6080
  exports.deleteMemoryByContent = deleteMemoryByContent;
6081
+ exports.deleteQaItem = deleteQaItem;
5271
6082
  exports.deleteSkill = deleteSkill;
5272
6083
  exports.deleteTask = deleteTask;
5273
6084
  exports.disableSkill = disableSkill;
@@ -5278,7 +6089,10 @@ exports.editLink = editLink;
5278
6089
  exports.editSkill = editSkill;
5279
6090
  exports.editTool = editTool;
5280
6091
  exports.enableSkill = enableSkill;
6092
+ exports.expandReview = expandReview;
6093
+ exports.expandTrace = expandTrace;
5281
6094
  exports.expectAllEndpointsVisible = expectAllEndpointsVisible;
6095
+ exports.expectBenchmarkListed = expectBenchmarkListed;
5282
6096
  exports.expectBillingAutoRechargeSection = expectBillingAutoRechargeSection;
5283
6097
  exports.expectBillingCreditsSection = expectBillingCreditsSection;
5284
6098
  exports.expectBillingPlanSection = expectBillingPlanSection;
@@ -5299,6 +6113,7 @@ exports.expectCreditBalancePanelForFreePlan = expectCreditBalancePanelForFreePla
5299
6113
  exports.expectCreditBalancePanelForPremiumPlan = expectCreditBalancePanelForPremiumPlan;
5300
6114
  exports.expectCreditBalancePanelForTrialPlan = expectCreditBalancePanelForTrialPlan;
5301
6115
  exports.expectCreditBalanceVisibilityForTenant = expectCreditBalanceVisibilityForTenant;
6116
+ exports.expectDetailWaitingForTraces = expectDetailWaitingForTraces;
5302
6117
  exports.expectEndpointUrl = expectEndpointUrl;
5303
6118
  exports.expectEntitySelected = expectEntitySelected;
5304
6119
  exports.expectFailedTasks = expectFailedTasks;
@@ -5316,14 +6131,23 @@ exports.expectLtiHeader = expectLtiHeader;
5316
6131
  exports.expectMentorVoiceTriggerShows = expectMentorVoiceTriggerShows;
5317
6132
  exports.expectNoAccessibilityViolations = expectNoAccessibilityViolations;
5318
6133
  exports.expectNoAccessibilityViolationsOnDialogs = expectNoAccessibilityViolationsOnDialogs;
6134
+ exports.expectNoBenchmarksNotice = expectNoBenchmarksNotice;
5319
6135
  exports.expectNoLogsForSelectedTask = expectNoLogsForSelectedTask;
5320
6136
  exports.expectOutputFilterEnabled = expectOutputFilterEnabled;
5321
6137
  exports.expectPrivacyFieldsHidden = expectPrivacyFieldsHidden;
5322
6138
  exports.expectPrivacyFieldsVisible = expectPrivacyFieldsVisible;
5323
6139
  exports.expectPrivateModeSelected = expectPrivateModeSelected;
5324
6140
  exports.expectPrivateModeTabReady = expectPrivateModeTabReady;
6141
+ exports.expectQaItemListed = expectQaItemListed;
6142
+ exports.expectReviewInList = expectReviewInList;
6143
+ exports.expectReviewStatus = expectReviewStatus;
6144
+ exports.expectRunInTable = expectRunInTable;
6145
+ exports.expectRunNotInTable = expectRunNotInTable;
6146
+ exports.expectRunStatus = expectRunStatus;
6147
+ exports.expectRunsTableEmpty = expectRunsTableEmpty;
5325
6148
  exports.expectScheduleStartTimeInPastError = expectScheduleStartTimeInPastError;
5326
6149
  exports.expectScreenShareDisabledHint = expectScreenShareDisabledHint;
6150
+ exports.expectSelectedBenchmark = expectSelectedBenchmark;
5327
6151
  exports.expectSttSelectDisabled = expectSttSelectDisabled;
5328
6152
  exports.expectTaskInList = expectTaskInList;
5329
6153
  exports.expectTaskNotInList = expectTaskNotInList;
@@ -5335,9 +6159,11 @@ exports.expectToolInList = expectToolInList;
5335
6159
  exports.expectToolNotInList = expectToolNotInList;
5336
6160
  exports.expectToolsEmpty = expectToolsEmpty;
5337
6161
  exports.expectTotalTasks = expectTotalTasks;
6162
+ exports.expectTraceScore = expectTraceScore;
5338
6163
  exports.expectTtsSelectDisabled = expectTtsSelectDisabled;
5339
6164
  exports.expectVoiceProviderSelected = expectVoiceProviderSelected;
5340
6165
  exports.expectVoiceVisible = expectVoiceVisible;
6166
+ exports.exportRunCsv = exportRunCsv;
5341
6167
  exports.fillLinkName = fillLinkName;
5342
6168
  exports.fillToolForm = fillToolForm;
5343
6169
  exports.filterByAction = filterByAction;
@@ -5347,14 +6173,19 @@ exports.filterByActorAndVerify = filterByActorAndVerify;
5347
6173
  exports.filterByDateRange = filterByDateRange;
5348
6174
  exports.generateBrowserSetupProjects = generateBrowserSetupProjects;
5349
6175
  exports.generateProjectConfig = generateProjectConfig;
6176
+ exports.getAddItemsDialog = getAddItemsDialog;
5350
6177
  exports.getAuditLogRowCount = getAuditLogRowCount;
5351
6178
  exports.getAvailableActors = getAvailableActors;
6179
+ exports.getBenchmarkCombobox = getBenchmarkCombobox;
6180
+ exports.getBenchmarkComboboxDropdown = getBenchmarkComboboxDropdown;
6181
+ exports.getBenchmarkItemsDialog = getBenchmarkItemsDialog;
5352
6182
  exports.getBillingAutoRechargeStatus = getBillingAutoRechargeStatus;
5353
6183
  exports.getBillingPlanLabel = getBillingPlanLabel;
5354
6184
  exports.getBrowserKey = getBrowserKey;
5355
6185
  exports.getCallConfigForm = getCallConfigForm;
5356
6186
  exports.getChatPrivacyConfirmDialog = getChatPrivacyConfirmDialog;
5357
6187
  exports.getChatPrivacyToggle = getChatPrivacyToggle;
6188
+ exports.getCreateBenchmarkDialog = getCreateBenchmarkDialog;
5358
6189
  exports.getCreateKeyButton = getCreateKeyButton;
5359
6190
  exports.getCreateLinkButton = getCreateLinkButton;
5360
6191
  exports.getCreateToolButton = getCreateToolButton;
@@ -5362,10 +6193,13 @@ exports.getCreditBalancePlanLabel = getCreditBalancePlanLabel;
5362
6193
  exports.getCreditBalanceRemaining = getCreditBalanceRemaining;
5363
6194
  exports.getCurrentModel = getCurrentModel;
5364
6195
  exports.getCurrentTenantShowPaywall = getCurrentTenantShowPaywall;
6196
+ exports.getDeleteEvaluationDialog = getDeleteEvaluationDialog;
6197
+ exports.getDeleteQaItemDialog = getDeleteQaItemDialog;
5365
6198
  exports.getEndpoint = getEndpoint;
5366
6199
  exports.getEndpointCopyButton = getEndpointCopyButton;
5367
6200
  exports.getEndpointUrl = getEndpointUrl;
5368
6201
  exports.getEntityChip = getEntityChip;
6202
+ exports.getEvaluationDetailDialog = getEvaluationDetailDialog;
5369
6203
  exports.getInstanceHealthLabel = getInstanceHealthLabel;
5370
6204
  exports.getInstanceRowCount = getInstanceRowCount;
5371
6205
  exports.getInstanceStatusLabel = getInstanceStatusLabel;
@@ -5387,15 +6221,23 @@ exports.getLinkNameInput = getLinkNameInput;
5387
6221
  exports.getLinkRow = getLinkRow;
5388
6222
  exports.getLinksEmptyState = getLinksEmptyState;
5389
6223
  exports.getLinksSection = getLinksSection;
6224
+ exports.getLlmJudgeDialog = getLlmJudgeDialog;
6225
+ exports.getLlmPickerDialog = getLlmPickerDialog;
5390
6226
  exports.getLtiSubTab = getLtiSubTab;
6227
+ exports.getManageBenchmarksButton = getManageBenchmarksButton;
6228
+ exports.getManageBenchmarksDialog = getManageBenchmarksDialog;
5391
6229
  exports.getMemoryCount = getMemoryCount;
5392
6230
  exports.getMentorIdFromUrl = getMentorIdFromUrl;
6231
+ exports.getNewEvaluationButton = getNewEvaluationButton;
5393
6232
  exports.getOutputFilterSwitch = getOutputFilterSwitch;
5394
6233
  exports.getPaginationInfo = getPaginationInfo;
5395
6234
  exports.getPrivateModeCard = getPrivateModeCard;
6235
+ exports.getReviewRow = getReviewRow;
6236
+ exports.getRunRow = getRunRow;
5396
6237
  exports.getScheduleTaskButton = getScheduleTaskButton;
5397
6238
  exports.getSearchInput = getSearchInput;
5398
6239
  exports.getSkillRowCount = getSkillRowCount;
6240
+ exports.getStartEvaluationDialog = getStartEvaluationDialog;
5399
6241
  exports.getTaskRow = getTaskRow;
5400
6242
  exports.getTenantChatPrivacyRow = getTenantChatPrivacyRow;
5401
6243
  exports.getTenantChatPrivacySwitch = getTenantChatPrivacySwitch;
@@ -5405,6 +6247,7 @@ exports.getToolModal = getToolModal;
5405
6247
  exports.getToolRow = getToolRow;
5406
6248
  exports.getToolsEmptyState = getToolsEmptyState;
5407
6249
  exports.getToolsSection = getToolsSection;
6250
+ exports.getTraceRow = getTraceRow;
5408
6251
  exports.getVoiceProviderCard = getVoiceProviderCard;
5409
6252
  exports.getVoiceRow = getVoiceRow;
5410
6253
  exports.goToFirstPage = goToFirstPage;
@@ -5413,6 +6256,7 @@ exports.goToNextPage = goToNextPage;
5413
6256
  exports.goToPage = goToPage;
5414
6257
  exports.goToPreviousPage = goToPreviousPage;
5415
6258
  exports.inviteUserTest = inviteUserTest;
6259
+ exports.isEvaluationTabVisible = isEvaluationTabVisible;
5416
6260
  exports.isFirefox = isFirefox;
5417
6261
  exports.isJSON = isJSON;
5418
6262
  exports.isLtiTabVisible = isLtiTabVisible;
@@ -5434,8 +6278,10 @@ exports.navigateToAuditLog = navigateToAuditLog;
5434
6278
  exports.navigateToAuditLogAndWaitForData = navigateToAuditLogAndWaitForData;
5435
6279
  exports.navigateToDataReports = navigateToDataReports;
5436
6280
  exports.navigateToReportDownload = navigateToReportDownload;
6281
+ exports.openAddItemsDialog = openAddItemsDialog;
5437
6282
  exports.openAddMemoryDialog = openAddMemoryDialog;
5438
6283
  exports.openAgentPromptEditModal = openAgentPromptEditModal;
6284
+ exports.openBenchmarkItems = openBenchmarkItems;
5439
6285
  exports.openCallConfigVoicePicker = openCallConfigVoicePicker;
5440
6286
  exports.openCreateKeyModal = openCreateKeyModal;
5441
6287
  exports.openCreateLinkModal = openCreateLinkModal;
@@ -5451,12 +6297,18 @@ exports.openKeyActionsMenu = openKeyActionsMenu;
5451
6297
  exports.openKeyDelete = openKeyDelete;
5452
6298
  exports.openKeyDetail = openKeyDetail;
5453
6299
  exports.openLLMProviderPicker = openLLMProviderPicker;
6300
+ exports.openManageBenchmarksDialog = openManageBenchmarksDialog;
5454
6301
  exports.openMentorVoicePicker = openMentorVoicePicker;
5455
6302
  exports.openNewInstanceDialog = openNewInstanceDialog;
6303
+ exports.openNewReviewForRun = openNewReviewForRun;
6304
+ exports.openNewReviewFromDetail = openNewReviewFromDetail;
5456
6305
  exports.openNewSkillDialog = openNewSkillDialog;
6306
+ exports.openRunActionsMenu = openRunActionsMenu;
6307
+ exports.openRunResults = openRunResults;
5457
6308
  exports.openScheduleTaskDialog = openScheduleTaskDialog;
5458
6309
  exports.openScreenSharePromptEditor = openScreenSharePromptEditor;
5459
6310
  exports.openSkillActionsMenu = openSkillActionsMenu;
6311
+ exports.openStartEvaluationDialog = openStartEvaluationDialog;
5460
6312
  exports.parseReportUrlParams = parseReportUrlParams;
5461
6313
  exports.previewCallConfigVoiceInline = previewCallConfigVoiceInline;
5462
6314
  exports.previewMentorVoiceInline = previewMentorVoiceInline;
@@ -5465,8 +6317,10 @@ exports.pushConfiguration = pushConfiguration;
5465
6317
  exports.readEndpointUrl = readEndpointUrl;
5466
6318
  exports.readKeyPublicJwk = readKeyPublicJwk;
5467
6319
  exports.readKeyPublicKey = readKeyPublicKey;
6320
+ exports.refreshEvaluationDetail = refreshEvaluationDetail;
5468
6321
  exports.reliableClick = reliableClick;
5469
6322
  exports.reliableFill = reliableFill;
6323
+ exports.removeTraceScore = removeTraceScore;
5470
6324
  exports.renameKey = renameKey;
5471
6325
  exports.resetCallConfig = resetCallConfig;
5472
6326
  exports.retry = retry;
@@ -5477,11 +6331,14 @@ exports.saveCallConfig = saveCallConfig;
5477
6331
  exports.saveScreenSharePrompts = saveScreenSharePrompts;
5478
6332
  exports.saveVoiceSettings = saveVoiceSettings;
5479
6333
  exports.scheduleTask = scheduleTask;
6334
+ exports.searchBenchmarks = searchBenchmarks;
5480
6335
  exports.searchInstances = searchInstances;
5481
6336
  exports.searchTasks = searchTasks;
5482
6337
  exports.searchVoices = searchVoices;
6338
+ exports.selectBenchmark = selectBenchmark;
5483
6339
  exports.selectCallMode = selectCallMode;
5484
6340
  exports.selectDateFromCalendar = selectDateFromCalendar;
6341
+ exports.selectJudgeLlm = selectJudgeLlm;
5485
6342
  exports.selectLLMModel = selectLLMModel;
5486
6343
  exports.selectLlmProvider = selectLlmProvider;
5487
6344
  exports.selectPrivacyAction = selectPrivacyAction;
@@ -5519,8 +6376,12 @@ exports.shouldSaveEditedCSVAndTriggerDownload = shouldSaveEditedCSVAndTriggerDow
5519
6376
  exports.shouldShowCombiningReportsDialog = shouldShowCombiningReportsDialog;
5520
6377
  exports.shouldVerifyCSVEditorDialogAccessibility = shouldVerifyCSVEditorDialogAccessibility;
5521
6378
  exports.signUpWithEmailAndPassword = signUpWithEmailAndPassword;
6379
+ exports.startEvaluation = startEvaluation;
5522
6380
  exports.submitLinkModal = submitLinkModal;
6381
+ exports.submitLlmJudge = submitLlmJudge;
5523
6382
  exports.submitToolModal = submitToolModal;
6383
+ exports.switchToAddItemsSubTab = switchToAddItemsSubTab;
6384
+ exports.switchToEvaluationTab = switchToEvaluationTab;
5524
6385
  exports.switchToLtiSubTab = switchToLtiSubTab;
5525
6386
  exports.switchToLtiTab = switchToLtiTab;
5526
6387
  exports.switchToMemoryTab = switchToMemoryTab;
@@ -5538,6 +6399,7 @@ exports.toggleAutoPush = toggleAutoPush;
5538
6399
  exports.toggleMemorySwitch = toggleMemorySwitch;
5539
6400
  exports.toggleSkill = toggleSkill;
5540
6401
  exports.toolFields = toolFields;
6402
+ exports.uploadQaCsv = uploadQaCsv;
5541
6403
  exports.verifyAgentConfigPromptsVisible = verifyAgentConfigPromptsVisible;
5542
6404
  exports.verifyAuditLogEmptyState = verifyAuditLogEmptyState;
5543
6405
  exports.verifyAuditLogEntryStructure = verifyAuditLogEntryStructure;