@iblai/iblai-js 2.5.1 → 2.5.6

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.
Files changed (33) hide show
  1. package/dist/data-layer/playwright/index.d.ts +4 -2
  2. package/dist/data-layer/playwright/lti-tab-helpers.d.ts +36 -1
  3. package/dist/data-layer/playwright/memory-admin-helpers.d.ts +153 -0
  4. package/dist/data-layer/playwright/memory-test-helpers.d.ts +8 -7
  5. package/dist/data-layer/playwright/voice-tab-helpers.d.ts +32 -0
  6. package/dist/playwright/index.cjs +550 -26
  7. package/dist/playwright/index.cjs.map +1 -1
  8. package/dist/playwright/index.d.ts +231 -10
  9. package/dist/playwright/index.esm.js +521 -27
  10. package/dist/playwright/index.esm.js.map +1 -1
  11. package/dist/playwright/playwright/index.d.ts +4 -2
  12. package/dist/playwright/playwright/lti-tab-helpers.d.ts +36 -1
  13. package/dist/playwright/playwright/memory-admin-helpers.d.ts +153 -0
  14. package/dist/playwright/playwright/memory-test-helpers.d.ts +8 -7
  15. package/dist/playwright/playwright/voice-tab-helpers.d.ts +32 -0
  16. package/dist/security/playwright/index.d.ts +4 -2
  17. package/dist/security/playwright/lti-tab-helpers.d.ts +36 -1
  18. package/dist/security/playwright/memory-admin-helpers.d.ts +153 -0
  19. package/dist/security/playwright/memory-test-helpers.d.ts +8 -7
  20. package/dist/security/playwright/voice-tab-helpers.d.ts +32 -0
  21. package/dist/web-containers/playwright/index.d.ts +4 -2
  22. package/dist/web-containers/playwright/lti-tab-helpers.d.ts +36 -1
  23. package/dist/web-containers/playwright/memory-admin-helpers.d.ts +153 -0
  24. package/dist/web-containers/playwright/memory-test-helpers.d.ts +8 -7
  25. package/dist/web-containers/playwright/voice-tab-helpers.d.ts +32 -0
  26. package/dist/web-containers/source/index.esm.js +12521 -11250
  27. package/dist/web-containers/source/next/index.esm.js +1734 -444
  28. package/dist/web-utils/playwright/index.d.ts +4 -2
  29. package/dist/web-utils/playwright/lti-tab-helpers.d.ts +36 -1
  30. package/dist/web-utils/playwright/memory-admin-helpers.d.ts +153 -0
  31. package/dist/web-utils/playwright/memory-test-helpers.d.ts +8 -7
  32. package/dist/web-utils/playwright/voice-tab-helpers.d.ts +32 -0
  33. package/package.json +5 -5
@@ -1618,19 +1618,20 @@ async function openAddMemoryDialog(page) {
1618
1618
  return dialog;
1619
1619
  }
1620
1620
  /**
1621
- * Toggle a memory setting switch and verify the state changes.
1622
- * Returns the new checked state.
1621
+ * Toggle a memory setting switch and verify the state changes. Completion is
1622
+ * gated on `aria-checked` flipping — which only happens once the settings
1623
+ * mutation resolves — rather than on timing. Returns the new checked state.
1623
1624
  */
1624
1625
  async function toggleMemorySwitch(page, switchName) {
1625
1626
  const switchEl = page.getByRole('switch', { name: switchName });
1626
1627
  await test$1.expect(switchEl).toBeVisible({ timeout: 10000 });
1627
1628
  const wasChecked = await switchEl.isChecked();
1628
1629
  await switchEl.click();
1629
- // Wait for API response
1630
- await page.waitForTimeout(1000);
1631
- const isNowChecked = await switchEl.isChecked();
1632
- logger.info(`Toggled "${switchName}" from ${wasChecked} to ${isNowChecked}`);
1633
- return isNowChecked;
1630
+ await test$1.expect(switchEl).toHaveAttribute('aria-checked', String(!wasChecked), {
1631
+ timeout: 15000,
1632
+ });
1633
+ logger.info(`Toggled "${switchName}" from ${wasChecked} to ${!wasChecked}`);
1634
+ return !wasChecked;
1634
1635
  }
1635
1636
  /**
1636
1637
  * Add a memory via the Add Memory dialog.
@@ -1650,32 +1651,47 @@ async function addMemory(page, content) {
1650
1651
  logger.info(`Added memory: "${content}"`);
1651
1652
  }
1652
1653
  /**
1653
- * Delete the first visible memory in the list.
1654
- * Hovers to reveal the delete button, then clicks it.
1654
+ * Delete a memory row via its three-dots menu: opens the menu, clicks
1655
+ * Delete, and confirms in the Delete Memory dialog. The confirmation dialog
1656
+ * is resolved into its own Locator first (topmost dialog matching the
1657
+ * title), and completion is gated on it closing after the mutation resolves.
1658
+ */
1659
+ async function deleteMemoryRow(page, memoryRow) {
1660
+ const menuTrigger = memoryRow.getByRole('button', { name: /^Memory actions:/ });
1661
+ await test$1.expect(menuTrigger).toBeVisible({ timeout: 5000 });
1662
+ await menuTrigger.click();
1663
+ // The menu portals to <body>; only one dropdown menu is ever open at once.
1664
+ const deleteItem = page.getByRole('menuitem', { name: 'Delete', exact: true });
1665
+ await test$1.expect(deleteItem).toBeVisible({ timeout: 5000 });
1666
+ await deleteItem.click();
1667
+ // Deleting asks for confirmation; the dialog stacks on top of everything.
1668
+ const confirmDialog = page.getByRole('dialog').filter({ hasText: 'Delete Memory' }).last();
1669
+ await test$1.expect(confirmDialog).toBeVisible({ timeout: 5000 });
1670
+ await confirmDialog.getByRole('button', { name: 'Delete', exact: true }).click();
1671
+ await test$1.expect(confirmDialog).toBeHidden({ timeout: 15000 });
1672
+ }
1673
+ /**
1674
+ * Delete the first visible memory in the list through its three-dots menu,
1675
+ * confirming in the Delete Memory dialog.
1655
1676
  */
1656
1677
  async function deleteFirstMemory(page) {
1657
- const deleteButton = page.locator('button[aria-label^="Delete memory:"]').first();
1658
- await test$1.expect(deleteButton).toBeVisible({ timeout: 5000 });
1659
- await deleteButton.hover();
1660
- await deleteButton.click();
1661
- // Wait for delete action to complete
1662
- await page.waitForTimeout(2000);
1678
+ const memoryRow = page.getByTestId('memory-row').first();
1679
+ await test$1.expect(memoryRow).toBeVisible({ timeout: 5000 });
1680
+ await deleteMemoryRow(page, memoryRow);
1663
1681
  logger.info('Memory deleted successfully');
1664
1682
  }
1665
1683
  /**
1666
- * Delete a specific memory by matching its content text.
1667
- * Finds the memory item containing the text, hovers to reveal
1668
- * the delete button, and clicks it.
1684
+ * Delete a specific memory by matching its content text through its
1685
+ * three-dots menu, confirming in the Delete Memory dialog. Completion is
1686
+ * gated on the row leaving the list after the refetch.
1669
1687
  */
1670
1688
  async function deleteMemoryByContent(page, content) {
1671
- // Find the memory row containing this text
1672
- const memoryRow = page.locator('.group').filter({ hasText: content }).first();
1689
+ const memoryRow = page.getByTestId('memory-row').filter({ hasText: content }).first();
1673
1690
  await test$1.expect(memoryRow).toBeVisible({ timeout: 5000 });
1674
- const deleteButton = memoryRow.locator('button[aria-label^="Delete memory:"]');
1675
- await deleteButton.hover();
1676
- await deleteButton.click();
1677
- // Wait for delete to complete and item to be removed
1678
- await test$1.expect(memoryRow.getByText(content)).not.toBeVisible({ timeout: 10000 });
1691
+ await deleteMemoryRow(page, memoryRow);
1692
+ await test$1.expect(page.getByTestId('memory-row').filter({ hasText: content })).toBeHidden({
1693
+ timeout: 15000,
1694
+ });
1679
1695
  logger.info(`Deleted memory: "${content}"`);
1680
1696
  }
1681
1697
  /**
@@ -1713,6 +1729,356 @@ async function verifyMemoryNotExists(page, content) {
1713
1729
  logger.info(`Verified memory removed: "${content}"`);
1714
1730
  }
1715
1731
 
1732
+ /**
1733
+ * Tenant-settings **Memory** tab helpers — Playwright bindings for the memory
1734
+ * administration UI from `@iblai/web-containers` (`MemoryAdminTab`):
1735
+ *
1736
+ * - the **Global** sub-tab: tenant users table with server-side search, and a
1737
+ * per-user popup (`user-memories-modal`) hosting the shared memories list
1738
+ * plus the user's two memory setting switches, and
1739
+ * - the **Agent** sub-tab: agents table with the shared agent autocomplete
1740
+ * filter, and a per-agent popup (`agent-memories-modal`) hosting the same
1741
+ * `ManageMemories` editor the agent settings modal renders.
1742
+ *
1743
+ * Selector policy (flakiness-proof by construction):
1744
+ * - Dialog-first scoping: every popup is resolved into a Locator variable
1745
+ * first (`openUserMemoriesPopup` / `openAgentMemoriesPopup` return it) and
1746
+ * all sub-elements are queried from that variable — never a bare page-wide
1747
+ * match that could hit same-named elements in nested portals. The popups
1748
+ * stack ON TOP of the tenant settings dialog, and the add/edit/delete
1749
+ * dialogs stack on top of the popups, so three dialogs can be open at once;
1750
+ * the innermost ones are resolved by content filter + `.last()` (stacked
1751
+ * Radix dialogs portal to the end of `<body>` in mount order, making the
1752
+ * last match the one on top).
1753
+ * - Stable hooks only: `data-testid`, role + accessible name (aria-labels).
1754
+ * No CSS class or structural selectors.
1755
+ * - No `waitForTimeout` / `networkidle`. Progress is gated on UI state that
1756
+ * only exists after the awaited transition: a section testid rendering, a
1757
+ * dialog closing after its mutation resolves, a row appearing or
1758
+ * disappearing after the list refetch, a switch's `aria-checked` flipping.
1759
+ */
1760
+ const MEMORY_ADMIN_LABELS = {
1761
+ /** Tenant settings rail item name. */
1762
+ tabName: 'Memory',
1763
+ subTabs: {
1764
+ global: 'Global',
1765
+ agent: 'Agent',
1766
+ },
1767
+ /** Add button inside the user memories popup (shared list styling). */
1768
+ addMemoryButton: 'Add Memory',
1769
+ menu: {
1770
+ /** aria-label prefix of a memory row's three-dots trigger. */
1771
+ actionsAriaPrefix: 'Memory actions:',
1772
+ edit: 'Edit',
1773
+ delete: 'Delete',
1774
+ },
1775
+ dialogs: {
1776
+ add: 'Add Memory',
1777
+ edit: 'Edit Memory',
1778
+ deleteConfirm: 'Delete Memory',
1779
+ },
1780
+ /**
1781
+ * Switch accessible-name prefixes. The full names carry the current state
1782
+ * ("Auto memory capture enabled"), so helpers match on the prefix.
1783
+ */
1784
+ switches: {
1785
+ autoCapture: /^Auto memory capture/,
1786
+ useMemory: /^Use memory in responses/,
1787
+ },
1788
+ };
1789
+ const UI_TIMEOUT$2 = 10000;
1790
+ const MUTATION_TIMEOUT$2 = 15000;
1791
+ // ---------------------------------------------------------------------------
1792
+ // Sections and rows
1793
+ // ---------------------------------------------------------------------------
1794
+ /** The Global sub-tab's body (users table + search). */
1795
+ function memoryAdminGlobalSection(page) {
1796
+ return page.getByTestId('memory-admin-global-section');
1797
+ }
1798
+ /** The Agent sub-tab's body (agents table + autocomplete filter). */
1799
+ function memoryAdminAgentSection(page) {
1800
+ return page.getByTestId('memory-admin-agent-section');
1801
+ }
1802
+ /** A users-table row for the given username. */
1803
+ function memoryAdminUserRow(page, username) {
1804
+ return memoryAdminGlobalSection(page).getByTestId(`memory-admin-user-row-${username}`);
1805
+ }
1806
+ /** An agents-table row for the given agent unique_id. */
1807
+ function memoryAdminAgentRow(page, mentorUniqueId) {
1808
+ return memoryAdminAgentSection(page).getByTestId(`memory-admin-agent-row-${mentorUniqueId}`);
1809
+ }
1810
+ /**
1811
+ * A memory row inside an open popup, found by (part of) its content text.
1812
+ * `scope` is the popup Locator returned by `openUserMemoriesPopup`.
1813
+ */
1814
+ function memoryRowByContent(scope, content) {
1815
+ return scope.getByTestId('memory-row').filter({ hasText: content });
1816
+ }
1817
+ // ---------------------------------------------------------------------------
1818
+ // Tab navigation
1819
+ // ---------------------------------------------------------------------------
1820
+ /**
1821
+ * Returns false when the Memory rail item isn't rendered in the tenant
1822
+ * settings dialog (non-admin viewer).
1823
+ */
1824
+ async function isTenantMemoryTabVisible(page) {
1825
+ const railItem = page
1826
+ .getByRole('button', { name: MEMORY_ADMIN_LABELS.tabName, exact: true })
1827
+ .first();
1828
+ try {
1829
+ await test$1.expect(railItem).toBeVisible({ timeout: 5000 });
1830
+ return true;
1831
+ }
1832
+ catch (_a) {
1833
+ return false;
1834
+ }
1835
+ }
1836
+ /**
1837
+ * Open the Memory tab from the tenant settings rail. Assumes the tenant
1838
+ * settings dialog is already open. Completion is gated on the Global sub-tab
1839
+ * trigger rendering (it appears once the memsearch status check resolves).
1840
+ */
1841
+ async function switchToTenantMemoryTab(page) {
1842
+ // Desktop and mobile rails both render the item; the first match is the
1843
+ // desktop one, which is the visible rail on desktop viewports.
1844
+ const railItem = page
1845
+ .getByRole('button', { name: MEMORY_ADMIN_LABELS.tabName, exact: true })
1846
+ .first();
1847
+ await test$1.expect(railItem).toBeVisible({ timeout: UI_TIMEOUT$2 });
1848
+ await railItem.click();
1849
+ await test$1.expect(page.getByTestId('memory-admin-sub-tab-global')).toBeVisible({
1850
+ timeout: UI_TIMEOUT$2,
1851
+ });
1852
+ logger.info('Switched to tenant Memory tab');
1853
+ }
1854
+ /**
1855
+ * Switch between the Memory tab's Global / Agent sub-tabs. Completion is
1856
+ * gated on the target section's testid rendering.
1857
+ */
1858
+ async function switchToMemoryAdminSubTab(page, subTab) {
1859
+ const trigger = page.getByTestId(`memory-admin-sub-tab-${subTab}`);
1860
+ await test$1.expect(trigger).toBeVisible({ timeout: UI_TIMEOUT$2 });
1861
+ await trigger.click();
1862
+ const section = subTab === 'global' ? memoryAdminGlobalSection(page) : memoryAdminAgentSection(page);
1863
+ await test$1.expect(section).toBeVisible({ timeout: UI_TIMEOUT$2 });
1864
+ logger.info(`Switched to Memory "${subTab}" sub-tab`);
1865
+ }
1866
+ // ---------------------------------------------------------------------------
1867
+ // Global sub-tab: users table + per-user popup
1868
+ // ---------------------------------------------------------------------------
1869
+ /**
1870
+ * Search the users table and gate on the expected user's row appearing.
1871
+ * Terms shorter than three characters search as empty (the Management tab's
1872
+ * debounce contract), so pass at least three characters.
1873
+ */
1874
+ async function searchMemoryAdminUsers(page, term, expectedUsername) {
1875
+ const input = memoryAdminGlobalSection(page).getByTestId('memory-admin-users-search');
1876
+ await test$1.expect(input).toBeVisible({ timeout: UI_TIMEOUT$2 });
1877
+ await input.fill(term);
1878
+ await test$1.expect(memoryAdminUserRow(page, expectedUsername)).toBeVisible({
1879
+ timeout: MUTATION_TIMEOUT$2,
1880
+ });
1881
+ logger.info(`Searched memory users for "${term}"`);
1882
+ }
1883
+ /**
1884
+ * Open the global memories popup for a user's row and return the popup's
1885
+ * Locator — pass it as the `popup` argument of every helper below so their
1886
+ * queries stay pinned to the popup instead of the dialogs underneath.
1887
+ * Completion is gated on the popup's list state rendering (rows, the empty
1888
+ * state, or the loading skeletons resolving into either).
1889
+ */
1890
+ async function openUserMemoriesPopup(page, username) {
1891
+ const viewButton = memoryAdminUserRow(page, username).getByTestId(`memory-admin-user-view-${username}`);
1892
+ await test$1.expect(viewButton).toBeVisible({ timeout: UI_TIMEOUT$2 });
1893
+ await viewButton.click();
1894
+ const popup = page.getByTestId('user-memories-modal');
1895
+ await test$1.expect(popup).toBeVisible({ timeout: UI_TIMEOUT$2 });
1896
+ await test$1.expect(popup.getByTestId('memories-list').or(popup.getByTestId('memories-list-empty'))).toBeVisible({ timeout: MUTATION_TIMEOUT$2 });
1897
+ logger.info(`Opened global memories popup for "${username}"`);
1898
+ return popup;
1899
+ }
1900
+ /** Close the user memories popup (Escape) and gate on it disappearing. */
1901
+ async function closeUserMemoriesPopup(page) {
1902
+ const popup = page.getByTestId('user-memories-modal');
1903
+ await test$1.expect(popup).toBeVisible({ timeout: UI_TIMEOUT$2 });
1904
+ await page.keyboard.press('Escape');
1905
+ await test$1.expect(popup).toBeHidden({ timeout: UI_TIMEOUT$2 });
1906
+ }
1907
+ /**
1908
+ * The innermost open dialog whose text matches `title`. Stacked Radix
1909
+ * dialogs portal to the end of `<body>` in mount order, so `.last()` is the
1910
+ * one on top — required here because the add/edit/delete dialogs open above
1911
+ * the popup, which itself sits above the tenant settings dialog.
1912
+ */
1913
+ function topDialogByTitle(page, title) {
1914
+ return page.getByRole('dialog').filter({ hasText: title }).last();
1915
+ }
1916
+ /** Open a memory row's three-dots menu and click one of its actions. */
1917
+ async function clickMemoryRowAction(page, row, action) {
1918
+ const trigger = row.getByRole('button', {
1919
+ name: new RegExp(`^${MEMORY_ADMIN_LABELS.menu.actionsAriaPrefix}`),
1920
+ });
1921
+ await test$1.expect(trigger).toBeVisible({ timeout: UI_TIMEOUT$2 });
1922
+ await trigger.click();
1923
+ // The menu portals to <body>; only one dropdown menu is ever open at once.
1924
+ const item = page.getByRole('menuitem', { name: action, exact: true });
1925
+ await test$1.expect(item).toBeVisible({ timeout: UI_TIMEOUT$2 });
1926
+ await item.click();
1927
+ }
1928
+ /**
1929
+ * Add a global memory for the popup's user: opens the Add Memory dialog,
1930
+ * fills the content (minimum 10 characters), saves, and gates on the dialog
1931
+ * closing and the new row appearing in the popup's list.
1932
+ */
1933
+ async function addUserGlobalMemory(page, popup, content) {
1934
+ const addButton = popup.getByTestId('user-memories-add');
1935
+ await test$1.expect(addButton).toBeVisible({ timeout: UI_TIMEOUT$2 });
1936
+ await addButton.click();
1937
+ const dialog = topDialogByTitle(page, MEMORY_ADMIN_LABELS.dialogs.add);
1938
+ await test$1.expect(dialog).toBeVisible({ timeout: UI_TIMEOUT$2 });
1939
+ await dialog.locator('#memory-content').fill(content);
1940
+ const saveButton = dialog.getByRole('button', { name: 'Save Memory', exact: true });
1941
+ await test$1.expect(saveButton).toBeEnabled({ timeout: UI_TIMEOUT$2 });
1942
+ await saveButton.click();
1943
+ await test$1.expect(dialog).toBeHidden({ timeout: MUTATION_TIMEOUT$2 });
1944
+ await test$1.expect(memoryRowByContent(popup, content)).toBeVisible({ timeout: MUTATION_TIMEOUT$2 });
1945
+ logger.info(`Added global memory: "${content}"`);
1946
+ }
1947
+ /**
1948
+ * Edit a global memory found by its current content: three-dots → Edit,
1949
+ * replace the content, save, and gate on the dialog closing and the updated
1950
+ * row appearing in the popup's list.
1951
+ */
1952
+ async function editUserGlobalMemory(page, popup, currentContent, newContent) {
1953
+ const row = memoryRowByContent(popup, currentContent);
1954
+ await test$1.expect(row).toBeVisible({ timeout: UI_TIMEOUT$2 });
1955
+ await clickMemoryRowAction(page, row, MEMORY_ADMIN_LABELS.menu.edit);
1956
+ const dialog = topDialogByTitle(page, MEMORY_ADMIN_LABELS.dialogs.edit);
1957
+ await test$1.expect(dialog).toBeVisible({ timeout: UI_TIMEOUT$2 });
1958
+ await dialog.locator('#edit-memory-content').fill(newContent);
1959
+ const saveButton = dialog.getByRole('button', { name: 'Save Memory', exact: true });
1960
+ await test$1.expect(saveButton).toBeEnabled({ timeout: UI_TIMEOUT$2 });
1961
+ await saveButton.click();
1962
+ await test$1.expect(dialog).toBeHidden({ timeout: MUTATION_TIMEOUT$2 });
1963
+ await test$1.expect(memoryRowByContent(popup, newContent)).toBeVisible({ timeout: MUTATION_TIMEOUT$2 });
1964
+ logger.info(`Edited global memory to: "${newContent}"`);
1965
+ }
1966
+ /**
1967
+ * Delete a global memory found by its content: three-dots → Delete, confirm
1968
+ * in the Delete Memory dialog, and gate on the confirmation closing and the
1969
+ * row leaving the popup's list.
1970
+ */
1971
+ async function deleteUserGlobalMemory(page, popup, content) {
1972
+ const row = memoryRowByContent(popup, content);
1973
+ await test$1.expect(row).toBeVisible({ timeout: UI_TIMEOUT$2 });
1974
+ await clickMemoryRowAction(page, row, MEMORY_ADMIN_LABELS.menu.delete);
1975
+ const confirmDialog = topDialogByTitle(page, MEMORY_ADMIN_LABELS.dialogs.deleteConfirm);
1976
+ await test$1.expect(confirmDialog).toBeVisible({ timeout: UI_TIMEOUT$2 });
1977
+ await confirmDialog.getByRole('button', { name: 'Delete', exact: true }).click();
1978
+ await test$1.expect(confirmDialog).toBeHidden({ timeout: MUTATION_TIMEOUT$2 });
1979
+ await test$1.expect(memoryRowByContent(popup, content)).toBeHidden({ timeout: MUTATION_TIMEOUT$2 });
1980
+ logger.info(`Deleted global memory: "${content}"`);
1981
+ }
1982
+ /**
1983
+ * Toggle one of the user's memory setting switches inside the popup and
1984
+ * gate on its `aria-checked` state flipping (which only happens after the
1985
+ * settings mutation resolves and the query refetches). Returns the new
1986
+ * checked state.
1987
+ */
1988
+ async function toggleUserMemoryAdminSetting(page, popup, setting) {
1989
+ const switchEl = popup.getByRole('switch', { name: MEMORY_ADMIN_LABELS.switches[setting] });
1990
+ await test$1.expect(switchEl).toBeVisible({ timeout: UI_TIMEOUT$2 });
1991
+ const wasChecked = await switchEl.isChecked();
1992
+ await switchEl.click();
1993
+ await test$1.expect(switchEl).toHaveAttribute('aria-checked', String(!wasChecked), {
1994
+ timeout: MUTATION_TIMEOUT$2,
1995
+ });
1996
+ logger.info(`Toggled memory setting "${setting}" from ${wasChecked} to ${!wasChecked}`);
1997
+ return !wasChecked;
1998
+ }
1999
+ // ---------------------------------------------------------------------------
2000
+ // Agent sub-tab: agents table + per-agent popup
2001
+ // ---------------------------------------------------------------------------
2002
+ /**
2003
+ * Filter the agents table to one agent via the autocomplete: types the name,
2004
+ * clicks the matching option, and gates on the selected chip rendering.
2005
+ */
2006
+ async function filterAgentMemories(page, agentName) {
2007
+ const section = memoryAdminAgentSection(page);
2008
+ const input = section.getByTestId('agent-memories-filter-input');
2009
+ await test$1.expect(input).toBeVisible({ timeout: UI_TIMEOUT$2 });
2010
+ await input.fill(agentName);
2011
+ const option = section
2012
+ .getByTestId('agent-memories-filter-results')
2013
+ .getByRole('button', { name: agentName, exact: true });
2014
+ await test$1.expect(option).toBeVisible({ timeout: MUTATION_TIMEOUT$2 });
2015
+ await option.click();
2016
+ await test$1.expect(section.getByTestId('agent-memories-filter-selected')).toBeVisible({
2017
+ timeout: UI_TIMEOUT$2,
2018
+ });
2019
+ logger.info(`Filtered agent memories to "${agentName}"`);
2020
+ }
2021
+ /** Clear the agents autocomplete filter (back to the full agents list). */
2022
+ async function clearAgentMemoriesFilter(page) {
2023
+ const section = memoryAdminAgentSection(page);
2024
+ const clearButton = section.getByTestId('agent-memories-filter-clear');
2025
+ await test$1.expect(clearButton).toBeVisible({ timeout: UI_TIMEOUT$2 });
2026
+ await clearButton.click();
2027
+ await test$1.expect(section.getByTestId('agent-memories-filter-input')).toBeVisible({
2028
+ timeout: UI_TIMEOUT$2,
2029
+ });
2030
+ }
2031
+ /**
2032
+ * Open the memories popup for an agent's row and return the popup's Locator
2033
+ * — pass it as the `popup` argument of `addAgentMemoryFromPopup` and scope
2034
+ * any further queries to it. Completion is gated on the `ManageMemories`
2035
+ * editor rendering its user filter combobox.
2036
+ */
2037
+ async function openAgentMemoriesPopup(page, mentorUniqueId) {
2038
+ const viewButton = memoryAdminAgentRow(page, mentorUniqueId).getByTestId(`memory-admin-agent-view-${mentorUniqueId}`);
2039
+ await test$1.expect(viewButton).toBeVisible({ timeout: UI_TIMEOUT$2 });
2040
+ await viewButton.click();
2041
+ const popup = page.getByTestId('agent-memories-modal');
2042
+ await test$1.expect(popup).toBeVisible({ timeout: UI_TIMEOUT$2 });
2043
+ await test$1.expect(popup.getByRole('combobox').first()).toBeVisible({ timeout: MUTATION_TIMEOUT$2 });
2044
+ logger.info(`Opened agent memories popup for "${mentorUniqueId}"`);
2045
+ return popup;
2046
+ }
2047
+ /** Close the agent memories popup (Escape) and gate on it disappearing. */
2048
+ async function closeAgentMemoriesPopup(page) {
2049
+ const popup = page.getByTestId('agent-memories-modal');
2050
+ await test$1.expect(popup).toBeVisible({ timeout: UI_TIMEOUT$2 });
2051
+ await page.keyboard.press('Escape');
2052
+ await test$1.expect(popup).toBeHidden({ timeout: UI_TIMEOUT$2 });
2053
+ }
2054
+ /**
2055
+ * Add an agent memory through the popup's `ManageMemories` editor: opens its
2056
+ * Add Memory dialog, optionally picks a category, fills the content (minimum
2057
+ * 10 characters), saves, and gates on the dialog closing and the content
2058
+ * appearing in the popup.
2059
+ */
2060
+ async function addAgentMemoryFromPopup(page, popup, content, categoryName) {
2061
+ const addButton = popup.getByRole('button', { name: MEMORY_ADMIN_LABELS.addMemoryButton });
2062
+ await test$1.expect(addButton).toBeVisible({ timeout: UI_TIMEOUT$2 });
2063
+ await addButton.click();
2064
+ const dialog = topDialogByTitle(page, MEMORY_ADMIN_LABELS.dialogs.add);
2065
+ await test$1.expect(dialog).toBeVisible({ timeout: UI_TIMEOUT$2 });
2066
+ if (categoryName) {
2067
+ await dialog.getByRole('combobox').click();
2068
+ // Radix Select portals its listbox to <body>; one is open at a time.
2069
+ const option = page.getByRole('option', { name: categoryName, exact: true });
2070
+ await test$1.expect(option).toBeVisible({ timeout: UI_TIMEOUT$2 });
2071
+ await option.click();
2072
+ }
2073
+ await dialog.getByRole('textbox').fill(content);
2074
+ const saveButton = dialog.getByRole('button', { name: 'Save', exact: true });
2075
+ await test$1.expect(saveButton).toBeEnabled({ timeout: UI_TIMEOUT$2 });
2076
+ await saveButton.click();
2077
+ await test$1.expect(dialog).toBeHidden({ timeout: MUTATION_TIMEOUT$2 });
2078
+ await test$1.expect(popup.getByText(content)).toBeVisible({ timeout: MUTATION_TIMEOUT$2 });
2079
+ logger.info(`Added agent memory: "${content}"`);
2080
+ }
2081
+
1716
2082
  // ============================
1717
2083
  // Navigation Helpers
1718
2084
  // ============================
@@ -3864,6 +4230,14 @@ const VOICE_LABELS = {
3864
4230
  openai: 'OpenAI Voice',
3865
4231
  google: 'Google Voice',
3866
4232
  },
4233
+ voiceInstructions: {
4234
+ label: 'Voice Instructions',
4235
+ presets: {
4236
+ warm: 'Warm and encouraging',
4237
+ calm: 'Calm and measured',
4238
+ energetic: 'Energetic and upbeat',
4239
+ },
4240
+ },
3867
4241
  saveVoiceButton: /save voice settings|saving…/i,
3868
4242
  // The "Save" button reads "Save" when no config exists yet and
3869
4243
  // "Save changes" once one does. Helpers match both via a regex.
@@ -4075,6 +4449,62 @@ async function previewVoice(scope, voiceName) {
4075
4449
  await test$1.expect(previewBtn).toBeVisible({ timeout: 10000 });
4076
4450
  await previewBtn.click();
4077
4451
  }
4452
+ /**
4453
+ * Open the "Voice Instructions" editor modal from its prompt card on the
4454
+ * Voice sub-tab. The card is only rendered when the OpenAI or Google
4455
+ * provider is selected. Mirrors the Prompts / Screen share card pattern:
4456
+ * an "Edit <label>" button popping the rich-text modal.
4457
+ */
4458
+ async function openVoiceInstructionsEditor(scope) {
4459
+ const btn = scope.getByRole('button', { name: `Edit ${VOICE_LABELS.voiceInstructions.label}` });
4460
+ await test$1.expect(btn).toBeVisible({ timeout: 10000 });
4461
+ await btn.click();
4462
+ await test$1.expect(asPage$5(scope).getByText(`Edit ${VOICE_LABELS.voiceInstructions.label}`)).toBeVisible({ timeout: 10000 });
4463
+ logger.info('Opened voice-instructions editor');
4464
+ }
4465
+ /**
4466
+ * Replace the "Voice Instructions" text via the editor modal and confirm.
4467
+ * The new value is written to local form state; call `saveVoiceSettings`
4468
+ * afterwards to persist. Pass an empty string to clear the stored
4469
+ * instructions (saving then sends `""`).
4470
+ */
4471
+ async function setVoiceInstructions(scope, text) {
4472
+ await openVoiceInstructionsEditor(scope);
4473
+ const page = asPage$5(scope);
4474
+ const editor = page.getByRole('dialog').locator('[contenteditable="true"]').first();
4475
+ await test$1.expect(editor).toBeVisible({ timeout: 10000 });
4476
+ await editor.click();
4477
+ await page.keyboard.press('ControlOrMeta+A');
4478
+ await page.keyboard.press('Delete');
4479
+ if (text)
4480
+ await editor.pressSequentially(text);
4481
+ // The modal's Save button — scope to dialog so we don't grab the
4482
+ // outer tab's Save.
4483
+ await page
4484
+ .getByRole('dialog')
4485
+ .getByRole('button', { name: /^save$/i })
4486
+ .click();
4487
+ await test$1.expect(page.getByRole('dialog')).toBeHidden({ timeout: 10000 });
4488
+ logger.info(`Voice instructions set (${text.length} chars)`);
4489
+ }
4490
+ /**
4491
+ * Assert the voice-instructions prompt card shows the given text.
4492
+ */
4493
+ async function expectVoiceInstructionsValue(scope, text) {
4494
+ await test$1.expect(scope.getByTestId('voice-instructions-card')).toContainText(text, {
4495
+ timeout: 10000,
4496
+ });
4497
+ }
4498
+ /**
4499
+ * Click one of the example preset chips under the voice-instructions
4500
+ * textarea; the chip's canned text replaces the textarea content.
4501
+ */
4502
+ async function applyVoiceInstructionsPreset(scope, preset) {
4503
+ const chip = scope.getByTestId(`voice-instructions-preset-${preset}`);
4504
+ await test$1.expect(chip).toBeVisible({ timeout: 10000 });
4505
+ await chip.click();
4506
+ logger.info(`Applied voice-instructions preset "${preset}"`);
4507
+ }
4078
4508
  /**
4079
4509
  * Click the Save button on the Voice sub-tab. Asserts the button is
4080
4510
  * enabled first (a no-op on a pristine form would be a test bug).
@@ -6736,6 +7166,14 @@ const LTI_LABELS = {
6736
7166
  keys: 'No LTI keys yet.',
6737
7167
  tools: 'No LTI tools yet.',
6738
7168
  },
7169
+ /** Badge text for the async-create link status. */
7170
+ status: {
7171
+ pending: 'Pending',
7172
+ building: 'Building',
7173
+ ready: 'Ready',
7174
+ failed: 'Failed',
7175
+ },
7176
+ retry: 'Retry',
6739
7177
  };
6740
7178
  /** data-testid values rendered by the LTI components. */
6741
7179
  const LTI_TEST_IDS = {
@@ -6754,6 +7192,9 @@ const LTI_TEST_IDS = {
6754
7192
  row: 'lti-link-row',
6755
7193
  modal: 'lti-link-modal',
6756
7194
  nameInput: 'lti-link-name-input',
7195
+ status: 'lti-link-status',
7196
+ retryButton: 'lti-link-retry-button',
7197
+ refreshButton: 'lti-links-refresh-button',
6757
7198
  },
6758
7199
  keys: {
6759
7200
  section: 'lti-keys-section',
@@ -6900,7 +7341,12 @@ async function submitLinkModal(scope) {
6900
7341
  await submit.click();
6901
7342
  await test$1.expect(getLinkModal(scope)).toBeHidden({ timeout: 15000 });
6902
7343
  }
6903
- /** Full create-link flow. */
7344
+ /**
7345
+ * Full create-link flow. Creation is asynchronous on the backend (202 +
7346
+ * celery build): the modal closes immediately and the row appears with a
7347
+ * Pending/Building status badge. Follow with `waitForLinkReady` before
7348
+ * asserting on `target_link_uri` or editing the link.
7349
+ */
6904
7350
  async function createLink(scope, name) {
6905
7351
  await openCreateLinkModal(scope);
6906
7352
  await fillLinkName(scope, name);
@@ -6926,6 +7372,54 @@ async function expectLinkNotInList(scope, name) {
6926
7372
  async function expectLinkTargetUri(scope, name, targetUri) {
6927
7373
  await test$1.expect(getLinkRow(scope, name)).toContainText(targetUri, { timeout: 10000 });
6928
7374
  }
7375
+ function getLinkStatusBadge(scope, name) {
7376
+ return getLinkRow(scope, name).getByTestId(LTI_TEST_IDS.links.status);
7377
+ }
7378
+ async function expectLinkStatus(scope, name, status) {
7379
+ await test$1.expect(getLinkStatusBadge(scope, name)).toHaveAttribute('data-status', status, {
7380
+ timeout: 10000,
7381
+ });
7382
+ }
7383
+ /** The Refresh button shown in the Links header while a build is in flight. */
7384
+ function getLinksRefreshButton(scope) {
7385
+ return getLinksSection(scope).getByTestId(LTI_TEST_IDS.links.refreshButton);
7386
+ }
7387
+ /** Manually refresh the links list (visible only while a build is in flight). */
7388
+ async function refreshLinks(scope) {
7389
+ await getLinksRefreshButton(scope).click();
7390
+ }
7391
+ /**
7392
+ * Wait for the async link build to finish (`ready`). The build creates an edX
7393
+ * course via celery, so allow a generous timeout (default 3 minutes). The UI
7394
+ * does not auto-poll — this helper clicks the header Refresh button every few
7395
+ * seconds until the row's badge reports `ready` (throws if it turns `failed`).
7396
+ */
7397
+ async function waitForLinkReady(scope, name, timeoutMs = 180000) {
7398
+ const badge = getLinkStatusBadge(scope, name);
7399
+ const deadline = Date.now() + timeoutMs;
7400
+ for (;;) {
7401
+ const status = await badge.getAttribute('data-status');
7402
+ if (status === 'ready')
7403
+ break;
7404
+ if (status === 'failed') {
7405
+ throw new Error(`LTI link build failed: ${name}`);
7406
+ }
7407
+ if (Date.now() >= deadline) {
7408
+ throw new Error(`Timed out waiting for LTI link to be ready: ${name} (last: ${status})`);
7409
+ }
7410
+ await asPage$1(scope).waitForTimeout(5000);
7411
+ await refreshLinks(scope);
7412
+ }
7413
+ logger.info(`LTI link ready: ${name}`);
7414
+ }
7415
+ /**
7416
+ * Retry a failed link build via the row's Retry button (deletes the failed
7417
+ * entity and re-posts the original payload).
7418
+ */
7419
+ async function retryFailedLink(scope, name) {
7420
+ await getLinkRow(scope, name).getByTestId(LTI_TEST_IDS.links.retryButton).click();
7421
+ logger.info(`Retried LTI link: ${name}`);
7422
+ }
6929
7423
  // ═══════════════════════════════════════════════════════════════════════
6930
7424
  // Sub-tab 2 — Keys
6931
7425
  // ═══════════════════════════════════════════════════════════════════════
@@ -7860,6 +8354,7 @@ exports.EVALS_LABELS = EVALS_LABELS;
7860
8354
  exports.GRADER_LABELS = GRADER_LABELS;
7861
8355
  exports.LTI_LABELS = LTI_LABELS;
7862
8356
  exports.LTI_TEST_IDS = LTI_TEST_IDS;
8357
+ exports.MEMORY_ADMIN_LABELS = MEMORY_ADMIN_LABELS;
7863
8358
  exports.MailsacClient = MailsacClient;
7864
8359
  exports.PRIVACY_LABELS = PRIVACY_LABELS;
7865
8360
  exports.SCREENSHARE_LABELS = SCREENSHARE_LABELS;
@@ -7867,16 +8362,19 @@ exports.SPEND_LIMITS_LABELS = SPEND_LIMITS_LABELS;
7867
8362
  exports.SUPPORT_LABELS = SUPPORT_LABELS;
7868
8363
  exports.TASKS_LABELS = TASKS_LABELS;
7869
8364
  exports.VOICE_LABELS = VOICE_LABELS;
8365
+ exports.addAgentMemoryFromPopup = addAgentMemoryFromPopup;
7870
8366
  exports.addGraderCriterion = addGraderCriterion;
7871
8367
  exports.addManualScore = addManualScore;
7872
8368
  exports.addMemory = addMemory;
7873
8369
  exports.addQaPairsManually = addQaPairsManually;
7874
8370
  exports.addSkillToAgent = addSkillToAgent;
7875
8371
  exports.addTextResource = addTextResource;
8372
+ exports.addUserGlobalMemory = addUserGlobalMemory;
7876
8373
  exports.addUserSpendLimit = addUserSpendLimit;
7877
8374
  exports.addUserSpendLimitFromTenantBilling = addUserSpendLimitFromTenantBilling;
7878
8375
  exports.agentLimitsRow = agentLimitsRow;
7879
8376
  exports.agentLimitsSection = agentLimitsSection;
8377
+ exports.applyVoiceInstructionsPreset = applyVoiceInstructionsPreset;
7880
8378
  exports.archiveFirstMemory = archiveFirstMemory;
7881
8379
  exports.archiveMemoryByContent = archiveMemoryByContent;
7882
8380
  exports.billingAutoRechargeSection = billingAutoRechargeSection;
@@ -7896,6 +8394,7 @@ exports.cancelStartEvaluation = cancelStartEvaluation;
7896
8394
  exports.checkAdminStatus = checkAdminStatus;
7897
8395
  exports.checkRunStatus = checkRunStatus;
7898
8396
  exports.clearAgentLimitsFilter = clearAgentLimitsFilter;
8397
+ exports.clearAgentMemoriesFilter = clearAgentMemoriesFilter;
7899
8398
  exports.clearDateRangeFilter = clearDateRangeFilter;
7900
8399
  exports.clearGradeResultOverride = clearGradeResultOverride;
7901
8400
  exports.clearInstanceSearch = clearInstanceSearch;
@@ -7910,11 +8409,13 @@ exports.clickDownloadAgain = clickDownloadAgain;
7910
8409
  exports.clickManualDownloadLink = clickManualDownloadLink;
7911
8410
  exports.clickSetSpendLimitForFilteredAgent = clickSetSpendLimitForFilteredAgent;
7912
8411
  exports.closeAgentLimitsPopup = closeAgentLimitsPopup;
8412
+ exports.closeAgentMemoriesPopup = closeAgentMemoriesPopup;
7913
8413
  exports.closeBenchmarkItemsDialog = closeBenchmarkItemsDialog;
7914
8414
  exports.closeCreditBalanceDropdown = closeCreditBalanceDropdown;
7915
8415
  exports.closeEvaluationDetailDialog = closeEvaluationDetailDialog;
7916
8416
  exports.closeKeyDetail = closeKeyDetail;
7917
8417
  exports.closeManageBenchmarksDialog = closeManageBenchmarksDialog;
8418
+ exports.closeUserMemoriesPopup = closeUserMemoriesPopup;
7918
8419
  exports.closeWithEsc = closeWithEsc;
7919
8420
  exports.confirmEnableChatPrivacyMidSession = confirmEnableChatPrivacyMidSession;
7920
8421
  exports.confirmKeyDelete = confirmKeyDelete;
@@ -7946,6 +8447,7 @@ exports.deleteQaItem = deleteQaItem;
7946
8447
  exports.deleteResource = deleteResource;
7947
8448
  exports.deleteSkill = deleteSkill;
7948
8449
  exports.deleteTask = deleteTask;
8450
+ exports.deleteUserGlobalMemory = deleteUserGlobalMemory;
7949
8451
  exports.deleteUserSpendLimit = deleteUserSpendLimit;
7950
8452
  exports.deleteWorkspaceSpendLimit = deleteWorkspaceSpendLimit;
7951
8453
  exports.disableSkill = disableSkill;
@@ -7958,6 +8460,7 @@ exports.editLink = editLink;
7958
8460
  exports.editSkill = editSkill;
7959
8461
  exports.editTextResource = editTextResource;
7960
8462
  exports.editTool = editTool;
8463
+ exports.editUserGlobalMemory = editUserGlobalMemory;
7961
8464
  exports.editUserSpendLimit = editUserSpendLimit;
7962
8465
  exports.enableSkill = enableSkill;
7963
8466
  exports.enableSupport = enableSupport;
@@ -8000,6 +8503,7 @@ exports.expectKeysEmpty = expectKeysEmpty;
8000
8503
  exports.expectLastCriterionDeleteDisabled = expectLastCriterionDeleteDisabled;
8001
8504
  exports.expectLinkInList = expectLinkInList;
8002
8505
  exports.expectLinkNotInList = expectLinkNotInList;
8506
+ exports.expectLinkStatus = expectLinkStatus;
8003
8507
  exports.expectLinkTargetUri = expectLinkTargetUri;
8004
8508
  exports.expectLinksEmpty = expectLinksEmpty;
8005
8509
  exports.expectLogDetailsStatus = expectLogDetailsStatus;
@@ -8045,6 +8549,7 @@ exports.expectToolsEmpty = expectToolsEmpty;
8045
8549
  exports.expectTotalTasks = expectTotalTasks;
8046
8550
  exports.expectTraceScore = expectTraceScore;
8047
8551
  exports.expectTtsSelectDisabled = expectTtsSelectDisabled;
8552
+ exports.expectVoiceInstructionsValue = expectVoiceInstructionsValue;
8048
8553
  exports.expectVoiceProviderSelected = expectVoiceProviderSelected;
8049
8554
  exports.expectVoiceVisible = expectVoiceVisible;
8050
8555
  exports.expectWorkspaceActualSpendStats = expectWorkspaceActualSpendStats;
@@ -8053,6 +8558,7 @@ exports.exportRunCsv = exportRunCsv;
8053
8558
  exports.fillLinkName = fillLinkName;
8054
8559
  exports.fillToolForm = fillToolForm;
8055
8560
  exports.filterAgentLimits = filterAgentLimits;
8561
+ exports.filterAgentMemories = filterAgentMemories;
8056
8562
  exports.filterByAction = filterByAction;
8057
8563
  exports.filterByActionAndVerify = filterByActionAndVerify;
8058
8564
  exports.filterByActor = filterByActor;
@@ -8112,6 +8618,7 @@ exports.getLinkModal = getLinkModal;
8112
8618
  exports.getLinkNameInput = getLinkNameInput;
8113
8619
  exports.getLinkRow = getLinkRow;
8114
8620
  exports.getLinksEmptyState = getLinksEmptyState;
8621
+ exports.getLinksRefreshButton = getLinksRefreshButton;
8115
8622
  exports.getLinksSection = getLinksSection;
8116
8623
  exports.getLlmJudgeDialog = getLlmJudgeDialog;
8117
8624
  exports.getLlmPickerDialog = getLlmPickerDialog;
@@ -8179,10 +8686,16 @@ exports.isSpendLimitsTabVisible = isSpendLimitsTabVisible;
8179
8686
  exports.isSupportEnabled = isSupportEnabled;
8180
8687
  exports.isSupportTabVisible = isSupportTabVisible;
8181
8688
  exports.isTasksTabVisible = isTasksTabVisible;
8689
+ exports.isTenantMemoryTabVisible = isTenantMemoryTabVisible;
8182
8690
  exports.isVoiceTabVisible = isVoiceTabVisible;
8183
8691
  exports.logger = logger;
8184
8692
  exports.loginWithEmailAndPassword = loginWithEmailAndPassword;
8185
8693
  exports.loginWithMicrosoftIdp = loginWithMicrosoftIdp;
8694
+ exports.memoryAdminAgentRow = memoryAdminAgentRow;
8695
+ exports.memoryAdminAgentSection = memoryAdminAgentSection;
8696
+ exports.memoryAdminGlobalSection = memoryAdminGlobalSection;
8697
+ exports.memoryAdminUserRow = memoryAdminUserRow;
8698
+ exports.memoryRowByContent = memoryRowByContent;
8186
8699
  exports.navigateToAccountComponent = navigateToAccountComponent;
8187
8700
  exports.navigateToAuditLog = navigateToAuditLog;
8188
8701
  exports.navigateToAuditLogAndWaitForData = navigateToAuditLogAndWaitForData;
@@ -8191,6 +8704,7 @@ exports.navigateToReportDownload = navigateToReportDownload;
8191
8704
  exports.openAddItemsDialog = openAddItemsDialog;
8192
8705
  exports.openAddMemoryDialog = openAddMemoryDialog;
8193
8706
  exports.openAgentLimitsManage = openAgentLimitsManage;
8707
+ exports.openAgentMemoriesPopup = openAgentMemoriesPopup;
8194
8708
  exports.openAgentPromptEditModal = openAgentPromptEditModal;
8195
8709
  exports.openBenchmarkItems = openBenchmarkItems;
8196
8710
  exports.openCallConfigVoicePicker = openCallConfigVoicePicker;
@@ -8222,6 +8736,8 @@ exports.openSkillActionsMenu = openSkillActionsMenu;
8222
8736
  exports.openSlashSkillPicker = openSlashSkillPicker;
8223
8737
  exports.openStartEvaluationDialog = openStartEvaluationDialog;
8224
8738
  exports.openTicket = openTicket;
8739
+ exports.openUserMemoriesPopup = openUserMemoriesPopup;
8740
+ exports.openVoiceInstructionsEditor = openVoiceInstructionsEditor;
8225
8741
  exports.overrideGradeResult = overrideGradeResult;
8226
8742
  exports.parseReportUrlParams = parseReportUrlParams;
8227
8743
  exports.previewCallConfigVoiceInline = previewCallConfigVoiceInline;
@@ -8232,6 +8748,7 @@ exports.readEndpointUrl = readEndpointUrl;
8232
8748
  exports.readKeyPublicJwk = readKeyPublicJwk;
8233
8749
  exports.readKeyPublicKey = readKeyPublicKey;
8234
8750
  exports.refreshEvaluationDetail = refreshEvaluationDetail;
8751
+ exports.refreshLinks = refreshLinks;
8235
8752
  exports.refreshTickets = refreshTickets;
8236
8753
  exports.reliableClick = reliableClick;
8237
8754
  exports.reliableFill = reliableFill;
@@ -8242,6 +8759,7 @@ exports.replyToTicket = replyToTicket;
8242
8759
  exports.requestSupportTicketViaChat = requestSupportTicketViaChat;
8243
8760
  exports.resetCallConfig = resetCallConfig;
8244
8761
  exports.retry = retry;
8762
+ exports.retryFailedLink = retryFailedLink;
8245
8763
  exports.runConnectedInstanceChecks = runConnectedInstanceChecks;
8246
8764
  exports.runInstanceChecks = runInstanceChecks;
8247
8765
  exports.safeWaitForURL = safeWaitForURL;
@@ -8252,6 +8770,7 @@ exports.saveVoiceSettings = saveVoiceSettings;
8252
8770
  exports.scheduleTask = scheduleTask;
8253
8771
  exports.searchBenchmarks = searchBenchmarks;
8254
8772
  exports.searchInstances = searchInstances;
8773
+ exports.searchMemoryAdminUsers = searchMemoryAdminUsers;
8255
8774
  exports.searchTasks = searchTasks;
8256
8775
  exports.searchVoices = searchVoices;
8257
8776
  exports.selectBenchmark = selectBenchmark;
@@ -8287,6 +8806,7 @@ exports.setTenantChatPrivacyEnabled = setTenantChatPrivacyEnabled;
8287
8806
  exports.setTicketStatus = setTicketStatus;
8288
8807
  exports.setUseFunctionCallingEnabled = setUseFunctionCallingEnabled;
8289
8808
  exports.setUserSpendLimitEnabled = setUserSpendLimitEnabled;
8809
+ exports.setVoiceInstructions = setVoiceInstructions;
8290
8810
  exports.setWorkspaceSpendLimit = setWorkspaceSpendLimit;
8291
8811
  exports.setupSandboxInstance = setupSandboxInstance;
8292
8812
  exports.shouldAddNewRowWhenClickingAddRowButton = shouldAddNewRowWhenClickingAddRowButton;
@@ -8320,6 +8840,7 @@ exports.switchToGraderSubTab = switchToGraderSubTab;
8320
8840
  exports.switchToGraderTab = switchToGraderTab;
8321
8841
  exports.switchToLtiSubTab = switchToLtiSubTab;
8322
8842
  exports.switchToLtiTab = switchToLtiTab;
8843
+ exports.switchToMemoryAdminSubTab = switchToMemoryAdminSubTab;
8323
8844
  exports.switchToMemoryTab = switchToMemoryTab;
8324
8845
  exports.switchToPlanAndCredits = switchToPlanAndCredits;
8325
8846
  exports.switchToPrivacyTab = switchToPrivacyTab;
@@ -8332,6 +8853,7 @@ exports.switchToSpendLimitsSubTab = switchToSpendLimitsSubTab;
8332
8853
  exports.switchToSpendLimitsTab = switchToSpendLimitsTab;
8333
8854
  exports.switchToSupportTab = switchToSupportTab;
8334
8855
  exports.switchToTasksTab = switchToTasksTab;
8856
+ exports.switchToTenantMemoryTab = switchToTenantMemoryTab;
8335
8857
  exports.switchToVoiceSubTab = switchToVoiceSubTab;
8336
8858
  exports.switchToVoiceTab = switchToVoiceTab;
8337
8859
  exports.switchToWorkspaceSpendLimits = switchToWorkspaceSpendLimits;
@@ -8340,6 +8862,7 @@ exports.test = test;
8340
8862
  exports.toggleAutoPush = toggleAutoPush;
8341
8863
  exports.toggleMemorySwitch = toggleMemorySwitch;
8342
8864
  exports.toggleSkill = toggleSkill;
8865
+ exports.toggleUserMemoryAdminSetting = toggleUserMemoryAdminSetting;
8343
8866
  exports.toolFields = toolFields;
8344
8867
  exports.uploadAssetResource = uploadAssetResource;
8345
8868
  exports.uploadQaCsv = uploadQaCsv;
@@ -8375,6 +8898,7 @@ exports.waitForBillingTabReady = waitForBillingTabReady;
8375
8898
  exports.waitForCreditBalanceLoaded = waitForCreditBalanceLoaded;
8376
8899
  exports.waitForDialogReady = waitForDialogReady;
8377
8900
  exports.waitForElementStable = waitForElementStable;
8901
+ exports.waitForLinkReady = waitForLinkReady;
8378
8902
  exports.waitForPageLoad = waitForPageLoad;
8379
8903
  exports.waitForPageReady = waitForPageReady;
8380
8904
  exports.waitForReportDownload = waitForReportDownload;