Skip to content

V2 Admin Settings: manage global agents and actions, fix category scroll - #1672

Merged
Paul Lizer (paullizer) merged 9 commits into
paullizer-react-v2-uifrom
paullizer-admin-v2-agents-actions
Oct 7, 2026
Merged

Paul Lizer (paullizer) merged 9 commits into
paullizer-react-v2-uifrom
paullizer-admin-v2-agents-actions

Conversation

@paullizer

Copy link
Copy Markdown
Contributor

Summary

V2 Admin Settings had no way to see or author the organisation's global agents and actions. Agents & Actions only offered a "Global agent delegation" card for Call agent actions, so admins still had to use the classic page. Separately, switching settings categories kept the previous category's scroll offset, so leaving Knowledge near its end opened Security near its end.

  • Category scroll reset: choosing a category in the rail (or the phone category select) now opens it at its top, including re-choosing the current one, and restarts the On this page index.
  • Global Agents and Global Actions sections: list, create, edit, enable/disable, and delete global agents and actions, and choose the default agent. Editing opens the same full-page V2 editors My Workspace uses (model, actions, public-only knowledge, instructions, templates; connector catalogue, MCP presets, connection tests, Key Vault handling), through new GLOBAL_AGENT_WORKBENCH / GLOBAL_ACTION_WORKBENCH adapters and 13 admin-only routes under /api/v2/admin.
  • Delegation card removed: a Call agent action is now an ordinary global action. The Call agent target picker now follows the editor's scope (global, group, or personal) instead of always reading personal targets, which also fixes group action editors.
  • Templates: "Start from a template" and "Publish as template" for global agents, and Agent Template Approvals links to the approvals queue (new tab).

Backend approach. functions_workspace_authoring.py gains a global_scope path through the existing editor engine instead of a parallel implementation. Credentials stage under fresh names in the global Key Vault namespace ({id}--agent|action|action-addset--global--...), every write is conditional on the opened revision (_etag), names are unique case-insensitively, the stored shape matches what the classic global saves write, the default agent cannot be deleted and follows renames, and Call agent targets and bindings are validated for the global scope. Enable/disable and make-default reuse the classic routes.

Worth a careful look

  • Merge conflicts and version number: paullizer-react-v2-ui has moved 26 commits ahead and is at 0.261.266. Merging conflicts in config.py, docs/_data/app_surface.yml, and docs/explanation/release_notes.md, and 0.261.260 (used here) is already taken on the target, so the version, the release-notes section, and doc/test version headers need renumbering when resolving.
  • Classic interop: delete_global_agent now reads the stored document instead of the masked one, so the classic delete removes credentials the V2 editor stored. It used to rebuild the classic secret name from the Stored_In_KeyVault placeholder and fail. Known limitation, shared with the personal and group editors and documented: after V2 replaces a classic-named key, a classic edit that enters a new value for that key fails until the soft-deleted name is purged (entering it in V2 works).
  • Unsaved settings: AdminSettingsPage now registers a useBlocker scoped to navigations into the global editors, so opening an editor asks before dropping unsaved settings. Other navigation is unchanged.
  • Tests removed: ui_tests/test_agent_delegation_v2.py and its MemoryRouter harness only covered the removed card; ui_tests/test_v2_admin_global_agents_actions.py replaces them.
  • Not in V2 yet: Duplicate for global agents and actions.

Screenshots (built SPA, fixture data):

Global Agents section

Global Actions section

Global agent editor

Linked issue

N/A (no linked issue)

Release Notes & Latest Features

  • New Feature
  • Bug Fix
  • UI Enhancement
  • Breaking Change
  • Internal only

Is this visible to end users?

  • Yes (only the Call agent target picker in group action editors; everything else is in Admin Settings)
  • No

Is this admin-facing (Admin Settings, governance, deployment, config)?

  • Yes
  • No

Should this become a Latest Feature card?

  • Yes
  • No (Latest Features cards are user-facing; this is admin-facing)
  • Already added

Screenshot needed for the card?

  • Yes
  • No
  • Attached

Version bump

  • application/single_app/config.py VERSION third segment bumped, or not needed because this is docs-only (0.261.259 -> 0.261.260; needs renumbering past the target's 0.261.266 when the conflicts are resolved)
  • deployers/version.txt bumped, or not needed because deployers/ was not changed

Testing / validation

  • npm --prefix application/v2_ui run typecheck - passed
  • npm --prefix application/v2_ui run build - passed
  • python -m pytest ui_tests/test_v2_admin_global_agents_actions.py - 13 passed
  • python -m pytest ui_tests/test_v2_admin_global_agents_actions.py ui_tests/test_v2_workspace_authoring.py ui_tests/test_v2_group_agents.py ui_tests/test_v2_group_actions.py ui_tests/test_v2_group_workspace_shell.py ui_tests/test_v2_workspace_draft_navigation.py ui_tests/test_v2_admin_settings_wide_layout.py ui_tests/test_v2_admin_agents_visual_hierarchy.py ui_tests/test_v2_admin_version_status.py ui_tests/test_admin_orchestration_actions.py ui_tests/test_v2_admin_model_catalog_workbench.py (with PYTHONPATH=ui_tests/fixtures) - 254 passed, 1 failed: test_v2_workspace_authoring.py::test_create_reload_edit_each_agent_type_with_applicable_controls[chromium-aifoundry], which then passed in 8 isolated reruns (flake under load)
  • python -m pytest ui_tests/test_v2_cross_scope_journeys.py ui_tests/test_v2_group_journeys.py - 51 passed
  • Regression proofs: both category-scroll tests fail with the selectCategory scroll reset removed; the unsaved-settings test fails with the blocker disabled; the classic global delete test fails against the previous functions_global_agents.py
  • python -m pytest functional_tests/test_v2_admin_global_editor_backend.py functional_tests/test_workspace_authoring_credential_compatibility.py functional_tests/test_v2_admin_agents_parity.py functional_tests/test_v2_admin_actions_parity.py functional_tests/test_v2_admin_field_renderer_coverage.py functional_tests/test_v2_admin_capability_placement.py functional_tests/test_admin_global_item_enabled_state.py - 56 passed, 1 failed (pre-existing, identical on the base commit: test_real_sdk_retry_preserves_potentially_committed_credentials)
  • python -m pytest functional_tests/test_admin_settings_nav_map.py functional_tests/test_v2_admin_settings_schema.py - 13 passed, 2 failed (both pre-existing, identical on the base commit: the content-screening tab has no pane, and m365_trusted_download_hosts default type)
  • node functional_tests/test_v2_admin_global_editor_logic.mjs - 12 passed
  • node functional_tests/test_v2_workspace_action_connectors_logic.mjs - 36 passed
  • node functional_tests/test_v2_admin_agents_logic.mjs - 40/40 passed
  • node functional_tests/test_v2_workspace_authoring_logic.mjs - 27 passed
  • python functional_tests/route_tests/test_route_blueprint_policy_inventory.py - 12/12 passed; test_route_unauthenticated_policy_contract.py - 7/7 passed; test_route_policy_test_coverage.py - 2/2 passed
  • python scripts/build_docs_inventory.py (regenerated), then python functional_tests/test_docs_app_surface_coverage.py - 7/7 passed and python functional_tests/test_docs_site_quality.py - 6/6 passed
  • python functional_tests/test_docs_release_notes_integrity.py - failed, pre-existing: the generated release-note pages were already 192 releases behind before this change; not regenerated here
  • python scripts/check_xss_sinks.py --base-sha 1b8d4c47b --head-sha HEAD <45 changed files> - passed
  • git diff --check - clean

Documentation

  • Release notes updated, or not needed
  • Feature documentation updated, or not needed (V2_ADMIN_GLOBAL_AGENTS_ACTIONS.md new; V2_ADMIN_AGENTS_ACTIONS.md, AGENT_DELEGATION_ACTION.md, docs/admin/agents-actions.md, docs/guides/call-another-agent.md updated)
  • Fix documentation updated, or not needed (V2_ADMIN_CATEGORY_SCROLL_RESET_FIX.md)

Security checklist

  • New Flask routes include @swagger_route(security=get_auth_security()) (all 13, plus @login_required and @admin_required on an admin-required blueprint)
  • Settings sent to non-admin frontends use sanitize_settings_for_user() (new routes are admin-only; agent options still pass through it and mask connection secrets)
  • Browser JavaScript is served from local SimpleChat static assets only; no CDN-hosted JS
  • No secrets, keys, connection strings, or local-only artifacts are included (test fixtures use synthetic values)

Paul Lizer (paullizer) and others added 7 commits October 6, 2026 16:29
- Choosing an Admin Settings category, in the rail or the phone select, now
  opens it at its top and restarts the On this page index.
- Global Agents and Global Actions sections list, create, edit, enable,
  disable and delete the organisation's agents and actions, and choose the
  default agent, using the same V2 editors workspaces use. Thirteen new
  admin-only routes under /api/v2/admin serve them through the shared editor
  engine, now with a global scope: masked secrets, global Key Vault names,
  conditional writes and case-insensitive name checks.
- Templates start and publish global agents; Agent Template Approvals links
  to the approvals queue in a new tab.
- Remove the Global agent delegation card. Call agent is an ordinary global
  action whose targets are global agents; the target picker now follows the
  editor's scope instead of always reading personal targets.
- Admin Settings asks before opening a global editor would drop unsaved
  settings.
- The classic global agent delete reads stored Key Vault references, so it
  removes credentials the V2 editor saved instead of failing.
- Version 0.261.260.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
… and actions editors to 0.261.268

The base already uses 0.261.260 through 0.261.267, so this change moves to
0.261.268: config.py, the release notes section, and every version this
branch added to its docs and test headers. The generated docs inventory is
rebuilt from the merged source, and the category rail keeps both the
base's collapsed-rail tooltip and this branch's scroll reset.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The full-page agent and action editors, shared by My Workspace, group
workspaces and Admin Settings, now use the Admin Settings card system.

- EditorLayout primitives: setting rows with the label and help beside the
  control on a wide row, fieldsets, switch rows with a lead switch and
  indented dependents, collapsible groups, titled panels and fieldset panels.
- WorkspaceEditorFrame: section rail with icons that marks the section in
  view, section cards with an icon, title and description, a save status
  chip, and a pinned jump (initialSection) that keeps the chosen section in
  place while lists above it load; ?templates=1 now uses it.
- Every agent and action section, including the MCP, OpenAPI and Call agent
  connectors, converted to rows, switches, groups and panels with the same
  accessible names.
- Global Agents and Global Actions lists use the AI Connections row pattern
  with Enabled/Disabled and Default agent badges and icon actions.
- Editor rows measure their own width instead of being CSS size containers:
  in Chromium 145 a size container could keep a stale, collapsed layout when
  React added a section after it, hiding the Action type row once a new
  action's type was chosen. A UI test now guards this.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
A section chosen from the editor rail stayed pinned through Save, because
the pin was only released by input inside the scrolling pane and Save sits
in the header. When the save failed, or native validation revealed a field
in a closed group, the content resized and the pin scrolled the focused
error or field back out of view.

The pin is now released by pointer or key input anywhere in the editor
form, on submit, on an invalid field, before the error summary is focused,
and when focus moves to anything other than the pinned section. A rail
choice still pins, because its click follows the pointerdown or keydown.
Two UI tests cover a failed save and an invalid field after a rail jump.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…ditors to 0.261.269

Brings in the durable batched search metadata sync port (#1674), which took
0.261.268, the version this pull request used.

- config.py: VERSION 0.261.269 (both sides had bumped 0.261.267 to 0.261.268).
- release_notes.md: this pull request's section moves to v0.261.269 above the
  base's v0.261.268 section, which is kept unchanged.
- Renumbered this pull request's 30 other 0.261.268 references (feature, fix,
  admin and guide docs, functional and UI test headers, version assertions).
  The fix doc's version transition now reads 0.261.268 -> 0.261.269. The
  base's own 0.261.268 references are untouched.
- docs/_data/app_surface.yml regenerated; unchanged.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…ditors to 0.261.270

Brings in the V2 Operations settings parity (#1675), which took 0.261.269,
the version this pull request had just moved to.

- AdminSettingsPage.tsx: kept both sides in all three conflicts. Imports
  keep the global agents, global actions and approvals components and the
  base's Operations helpers; the Global agent delegation imports stay
  removed, because nothing on the page uses that card any more. Helpers keep
  selectCategory (category scroll reset) beside the base's readSwitch and
  renderRelatedSection. Overlays keep the unsaved-settings prompt for the
  global editors and the base's section guide dialog.
- admin_settings_fields.py auto-merged; the global manager components are
  intact and no delegation card was reintroduced.
- config.py: VERSION 0.261.270 (both sides had moved to 0.261.269).
- release_notes.md: this pull request's section moves to v0.261.270 above
  the base's v0.261.269 Operations section, which is kept unchanged.
- Renumbered this pull request's 30 other 0.261.269 references; the fix doc's
  version transition now reads 0.261.269 -> 0.261.270. The base's own
  0.261.269 references are untouched.
- docs/_data/app_surface.yml regenerated; unchanged.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Comment thread application/single_app/route_backend_agents.py Fixed
Comment thread application/single_app/route_backend_agents.py Fixed
Comment thread functional_tests/test_v2_admin_global_editor_backend.py Fixed
Comment thread functional_tests/test_v2_admin_global_editor_backend.py Fixed
Comment thread ui_tests/test_v2_admin_global_agents_actions.py
Paul Lizer (paullizer) and others added 2 commits October 6, 2026 21:12
…ditors to 0.261.271

Brings in the shared Orchestrate fixes (#1676), which took 0.261.270, the
version this pull request had moved to.

- config.py: VERSION 0.261.271 (both sides had moved to 0.261.270).
- release_notes.md: this pull request's section moves to v0.261.271 above
  the base's v0.261.270 Orchestrate section, which is kept unchanged.
- Renumbered this pull request's 30 other 0.261.270 references; the fix doc's
  version transition now reads 0.261.270 -> 0.261.271. The base's own
  0.261.270 references are untouched.
- No other files changed on both sides; docs/_data/app_surface.yml
  regenerated and unchanged.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Address the CodeQL review findings on the global agents and actions editor.

- route_backend_agents.py: _prepare_global_agent_payload no longer returns a
  caught AgentPayloadError or AssignedKnowledgeError's text in its 400
  response (py/stack-trace-exposure, alerts 4054 and 4055). It answers with a
  fixed message and logs the reason as a warning. The editor engine already
  replaced the body with "Invalid agent configuration.", so nothing changes on
  screen; the reason, which was discarded, is now kept in the server log.
- test_v2_admin_global_editor_backend.py: a test runs the real function from
  its source and checks both refusals answer with a fixed message while the
  log keeps the reason, and that no response in the function names a caught
  exception. It fails against the previous code. Also drops the unused
  MagicMock import (alert 4057) and passes deepcopy directly instead of
  wrapping it in a lambda (alert 4058).
- The feature doc notes where a refused global agent's reason is logged.

The connect_options import flagged in the UI test (alert 4056) is kept: it
overrides pytest-playwright's fixture by name, as in the other UI tests.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@paullizer
Paul Lizer (paullizer) merged commit 78d7cef into paullizer-react-v2-ui Oct 7, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants