From dbb8f4eba1b2890884bf8f67c05788c9c25f2200 Mon Sep 17 00:00:00 2001 From: Paul Lizer Date: Wed, 19 Aug 2026 09:50:40 -0400 Subject: [PATCH 01/25] Split Admin Settings template into per-tab partials Admin Settings had grown to 13,526 lines in a single 1 MB template, which made the upcoming information architecture rework impractical to do safely. Each tab pane now lives in application/single_app/templates/admin/_panes/ and is pulled back in with {% include %}. The parent template keeps the global form, the modals, and the script blocks, so the submitted payload is unchanged: every field name, and all 109 configuration card ids, are byte-identical to before the split. Functional tests that read admin_settings.html straight from disk would now only see the parent shell, so they route through a new shared helper, test_support.templates, which inlines the Admin Settings partials and leaves every other repository file untouched. Adds test_admin_settings_template_composition.py to pin the contract: the parent delegates to partials, no partial is orphaned, composition restores the full card inventory, and no functional test asserts on a partial-backed card without composing the template first. Verified against the 75 functional test files that touch admin_settings.html: 32 pre-existing failures before and after, identical sets, zero regressions. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../templates/admin/_panes/agents.html | 1021 ++ .../templates/admin/_panes/ai-models.html | 831 ++ .../templates/admin/_panes/citation.html | 244 + .../admin/_panes/control-center-config.html | 180 + .../templates/admin/_panes/custom-pages.html | 81 + .../admin/_panes/data-management.html | 1621 +++ .../templates/admin/_panes/file-sync.html | 411 + .../templates/admin/_panes/general.html | 682 + .../templates/admin/_panes/governance.html | 250 + .../admin/_panes/latest-features.html | 1253 ++ .../templates/admin/_panes/logging.html | 177 + .../templates/admin/_panes/safety.html | 280 + .../templates/admin/_panes/scale.html | 1628 +++ .../admin/_panes/search-extract.html | 1698 +++ .../templates/admin/_panes/security.html | 230 + .../templates/admin/_panes/send-feedback.html | 72 + .../admin/_panes/workspace-identities.html | 12 + .../templates/admin/_panes/workspaces.html | 1138 ++ .../single_app/templates/admin_settings.html | 11833 +--------------- ...est_admin_agent_default_model_migration.py | 3 +- ...est_admin_app_role_settings_consistency.py | 3 +- ...n_document_action_capabilities_location.py | 10 +- .../test_admin_global_item_enabled_state.py | 3 +- .../test_admin_latest_features_tab.py | 12 +- .../test_admin_send_feedback_tab.py | 12 +- ...test_admin_settings_sidebar_card_parity.py | 5 +- ...est_admin_settings_template_composition.py | 173 + .../test_admin_source_review_depth_save.py | 3 +- .../test_agents_catalog_feature.py | 6 +- ..._ai_models_tab_embedding_image_location.py | 5 +- .../test_audio_ffmpeg_fallback.py | 5 +- .../test_azure_openai_identity_split.py | 6 +- .../test_chat_completion_audio_cues.py | 5 +- .../test_chat_file_upload_access_control.py | 3 +- .../test_content_safety_violation_message.py | 4 +- ...content_understanding_extraction_engine.py | 6 +- ...st_control_center_auto_refresh_schedule.py | 4 +- ...t_conversation_contents_drawer_settings.py | 6 +- ...cosmos_wave4a1_admin_document_access_ui.py | 6 +- functional_tests/test_custom_pages_wiring.py | 6 +- ...test_data_management_history_pagination.py | 5 +- .../test_data_management_security_patterns.py | 5 +- .../test_deep_research_explicit_toggle.py | 3 +- .../test_default_model_selection_fallback.py | 3 +- .../test_desktop_notification_settings.py | 6 +- ...est_document_action_capability_settings.py | 6 +- ..._intelligence_pdf_image_extraction_mode.py | 6 +- .../test_endpoints_tab_order_visibility.py | 3 +- ...d_citations_startup_storage_degradation.py | 5 +- .../test_file_sync_azure_blob_storage.py | 6 +- .../test_file_sync_azure_files_identity.py | 6 +- functional_tests/test_file_sync_capability.py | 6 +- .../test_file_sync_onedrive_personal.py | 6 +- ...overnance_admin_scope_toggle_visibility.py | 3 +- .../test_governance_enforcement_logic.py | 3 +- ...st_governance_route_and_wiring_coverage.py | 3 +- functional_tests/test_idle_logout_timeout.py | 4 +- functional_tests/test_inbound_mcp_admin_ui.py | 6 +- .../test_latest_features_action_links.py | 5 +- .../test_multimedia_support_reorganization.py | 7 +- ...imodal_vision_multi_endpoint_connection.py | 4 +- .../test_personal_workflows_feature.py | 6 +- ..._public_workspace_display_name_settings.py | 3 +- functional_tests/test_support/templates.py | 96 + .../test_support_menu_user_feature.py | 5 +- ...rocessing_enhanced_citations_dependency.py | 5 +- .../test_workflow_access_controls.py | 6 +- ...t_workflow_auto_invoke_attempt_settings.py | 6 +- 68 files changed, 12289 insertions(+), 11867 deletions(-) create mode 100644 application/single_app/templates/admin/_panes/agents.html create mode 100644 application/single_app/templates/admin/_panes/ai-models.html create mode 100644 application/single_app/templates/admin/_panes/citation.html create mode 100644 application/single_app/templates/admin/_panes/control-center-config.html create mode 100644 application/single_app/templates/admin/_panes/custom-pages.html create mode 100644 application/single_app/templates/admin/_panes/data-management.html create mode 100644 application/single_app/templates/admin/_panes/file-sync.html create mode 100644 application/single_app/templates/admin/_panes/general.html create mode 100644 application/single_app/templates/admin/_panes/governance.html create mode 100644 application/single_app/templates/admin/_panes/latest-features.html create mode 100644 application/single_app/templates/admin/_panes/logging.html create mode 100644 application/single_app/templates/admin/_panes/safety.html create mode 100644 application/single_app/templates/admin/_panes/scale.html create mode 100644 application/single_app/templates/admin/_panes/search-extract.html create mode 100644 application/single_app/templates/admin/_panes/security.html create mode 100644 application/single_app/templates/admin/_panes/send-feedback.html create mode 100644 application/single_app/templates/admin/_panes/workspace-identities.html create mode 100644 application/single_app/templates/admin/_panes/workspaces.html create mode 100644 functional_tests/test_admin_settings_template_composition.py create mode 100644 functional_tests/test_support/templates.py diff --git a/application/single_app/templates/admin/_panes/agents.html b/application/single_app/templates/admin/_panes/agents.html new file mode 100644 index 000000000..5994976d2 --- /dev/null +++ b/application/single_app/templates/admin/_panes/agents.html @@ -0,0 +1,1021 @@ +
+ +

+ Configure AI agents and actions for enhanced functionality. Agents provide AI-driven task automation while Actions extend functionality with custom tools and integrations. +

+ + + + + + {% set analyze_capability = settings.document_action_capabilities.analyze %} + {% set comparison_capability = settings.document_action_capabilities.comparison %} +
+
+
+
Document Action Capabilities
+

Configure the document actions shown in the Action dropdown in Chat and Workflow. These controls live in the Agents and Actions tab, but remain separate from the global agent and custom action cards below.

+
+ Chat: 2-300 | Workflow: 2-1000 +
+ +
+
+
+
+ + + +
+
+
+ + {{ analyze_capability.chat_max_documents }} +
+
+
+ +
+
+ +
+
+
+ 2 + 300 +
+
+
+
+ + {{ analyze_capability.workflow_max_documents }} +
+
+
+ +
+
+ +
+
+
+ 2 + 1000 +
+
+
+
+
+
+
+ + + +
+
+
+ + {{ comparison_capability.chat_max_documents }} +
+
+
+ +
+
+ +
+
+
+ 2 + 300 +
+
+
+
+ + {{ comparison_capability.workflow_max_documents }} +
+
+
+ +
+
+ +
+
+
+ 2 + 1000 +
+
+
+
+
+
+ + +
+
+ Agents Configuration +
+

+ Configure AI agents powered by Semantic Kernel for task automation and orchestration. +

+
+
+ Agents Page Customization +
+

Customize the public Agents page hero, color treatment, and optional guidance message.

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
Used when two tone gradient is selected.
+
+
+ + +
Shown below the Agents page hero. Use this for contact details, request guidance, or governance reminders.
+
+
+
+ + +
+
When disabled, the Agents tab details popup hides instructions and the catalog API response omits them.
+
+
+
+
Promoted Popular Agents
+

Highlight selected agents in the Popular tab even before they have usage counts. Users only see promoted agents that are already visible to them.

+ +
+
+ + +
+
+ + +
+
+
+
+ + +
+
When enabled, promoted agents show the configured tag in the Popular tab.
+
+
+
+
+
+ + +
+
+ +
+
+
+ + + + + + + + + +
AgentShow InActions
+
+ + +
+
+
+
+
+ + + + + +
+ +
+ {% if not settings.enable_semantic_kernel %} +
+ Agents UI is not available while agents are disabled. +
+ {% else %} +
+
+ + + +
+ {% if settings.per_user_semantic_kernel %} + +
+
Workspace Feature Toggles
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + +
+
+ + + +
+
+ + + {% endif %} + +
+

Agent Orchestration Settings

+
+
+

Configure how the chat system orchestrates agents (single or multi-agent group chat).

+
+ + +
+ + +
+
+

Global Agents

+
+ + +
+
+

Disable a global agent to keep it saved for admins while hiding it from runtime selection until it is re-enabled.

+
+ {% if settings.orchestration_type == "default_agent" %} + + {% else %} + + {% endif %} + + +
+ + + + + + + {% if settings.orchestration_type == "default_agent" %} + + {% else %} + + {% endif %} + + + + +
NameDisplay NameDescriptionSelected AgentOrchestrator AgentActions
+
+ {% endif %} +
+ + {% if settings.enable_agent_template_gallery %} +
+
+
+
+ + + +
+
+
+
+ + + +
+
+
+
+
+
+ Agent Template Approvals +
+

Template submissions are now reviewed from the shared approvals page.

+
+ + Open Approvals Queue + +
+
+ {% endif %} + + +
+
+ Global Actions Configuration +
+

+ Configure custom actions and tools to extend functionality with integrations and specialized capabilities. +

+ + {% if not settings.enable_semantic_kernel %} +
+ Actions are not available while agents are disabled. Enable agents above to configure actions. +
+ {% else %} +
+
+
Global Actions
+ +
+

Disable a global action to keep the configuration without exposing it to runtime action loading until it is re-enabled.

+ {% if settings.per_user_semantic_kernel %} + +
+
Workspace Action Feature Toggles
+
+ + + + +
+
+ + + + +
+
+ {% endif %} + + +
+
Core Action Toggles
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+
+ Tabular Processing Action + +
+ Automatically enabled when Enhanced Citations is enabled +
+
+ + + + + + + + + +
Display NameDescriptionActions
+
+ {% endif %} +
+ {% if mcp_ui_enabled %} +
+
+
+
+ Inbound MCP Server +
+

+ Configure the governed SimpleChat MCP endpoint used by external MCP clients. Personal tools use delegated user tokens by default; tool access remains deny-by-default until authentication, source, client, and governance policy all allow the request. +

+
+
+ Preview + +
+
+ + + +
+
+
+
Runtime Gate
+
+ + + +
+
+ + +
Default: DelegatedMcpServerAccess. VS Code and other user clients must present this delegated scope.
+
+
+ + +
Default: InboundMCPUserAccess. Governance determines which users/groups can use tools after this Entra role and delegated scope pass.
+
+
+ + +
Default: InboundMCPAppAccess. Reserved for future app-only MCP tools and still governed separately.
+
+
+
+ +
+
+
Endpoint Contract
+
+
MCP endpoint
+
{{ inbound_mcp_resource_path }}
+
Protected resource metadata
+
{{ inbound_mcp_prm_path }}
+
Transport
+
Streamable HTTP JSON-RPC
+
Current tool surface
+
Personal delegated tools only.
+
Tool identity
+
Delegated user token required for personal data.
+
+ +
+
+ +
+
+
+
+
Request Size & Throttling
+
Protect the inbound MCP endpoint from oversized payloads and noisy clients. Rate limits are enforced per caller and tool category across app instances.
+
+
+ + +
+
+
+
+ + +
Default: 65536. Range: 1 KB to 1 MB.
+
+
+ + +
Default: 60. Applies to each throttle category.
+
+
+ + +
Default: 120.
+
+
+ + +
Default: 30.
+
+
+ + +
Default: 10.
+
+
+ +
+
+ +
+ + + +
+ +
+
+
+
+
Allowed client app IDs
+
Required allowlist. Empty means no MCP clients can connect.
+
+ +
+
+ + + + + + + + + +
Client app IDDescriptionActions
+
+
+
+
+ +
+
+
+ + +
+
+ Off allows only the configured SimpleChat tenant. Turning this on auto-includes the SimpleChat tenant and lets admins add more tenants. +
+
+
+
Allowed tenant IDs
+ +
+
+ + + + + + + + + +
Tenant IDDescriptionActions
+
+
+
+
+
+ +
+
+
+ + +
+
+ Default on accepts any source signal at the runtime allowlist layer. Turn off to require explicit source values here. In both modes, admins must still create an inbound MCP source governance policy before tools are returned. +
+
+ + +
Default: X-SimpleChat-MCP-Source.
+
+ + + +
+
+
Allowed source IDs
+ +
+
+ + + + + + + + + +
Source valueDescriptionActions
+
+
+
+
+
+
+
+ + + + {% endif %} +
diff --git a/application/single_app/templates/admin/_panes/ai-models.html b/application/single_app/templates/admin/_panes/ai-models.html new file mode 100644 index 000000000..3df45b251 --- /dev/null +++ b/application/single_app/templates/admin/_panes/ai-models.html @@ -0,0 +1,831 @@ +
+ +

+ Configure all AI model settings including GPT for text generation, embeddings for semantic search, and image generation capabilities. +

+ +
+
+ Model Endpoints +
+

+ Manage multiple AI model endpoints (Azure OpenAI and Azure AI Foundry). When enabled, model selection in chat is driven by these endpoints. +

+ + {% if not settings.enable_multi_model_endpoints %} +
+ + +
+ {% endif %} + + {% if settings.enable_semantic_kernel %} +
+
+
+
Agent Default Model Review
+

+ Review local agents in a modal, bulk-bind inherited agents to the saved default model endpoint, and explicitly override selected agent model choices when you need to manage cost or move to newer models. +

+
+
+ +
+
+
+ {% if settings.enable_multi_model_endpoints %} + Save your AI model settings before reviewing or migrating agents. + {% else %} + Enable multi-endpoint model management to review and rebind agents to a saved default model. + {% endif %} +
+ +
+ +
+
+ Ready: 0 + Needs Default: 0 + Manual Review: 0 + Already Migrated: 0 +
+
+
+ Open the review modal to search, filter, and selectively rebind agents to the saved default model. +
+
+ + + {% else %} + + {% endif %} + +
+
+
+
Model Endpoint Identity Header
+

+ Send a stable HMAC-hashed user key to model endpoint backends for APIM counters, quota policies, or routing policies. +

+
+
+ + +
+
+
+
+ + +
Reserved auth/protocol headers cannot be used.
+
+
+ + +
The selected identity is HMAC-hashed before leaving SimpleChat. Missing identity values omit the header.
+
+
+
+ +
+ + +
+ Used for tasks such as conversation summarization, fallback, and other operations when an agent is selected. +
+
+ + + + +
+
+
Global Endpoints
+
+ + +
+
+
+ + + + + + + + + + + + + +
NameProviderSelected ModelsStatusActions
+
+
+ {% if not settings.enable_multi_model_endpoints %} + + {% endif %} +
+ + +
+
+ Processing Thoughts +
+

When enabled, real-time processing steps are shown to users during chat responses and persisted for later review.

+
+ + + +
+
+ + +
+
+ Shared Conversation File Approvals +
+

Files generated by participants in a shared conversation are saved into the conversation owner's storage. When enabled, those files are held until an approver releases them.

+
+ + + +
+
+ + +
+
+ Embeddings Configuration +
+

+ Configure your embeddings settings. These are used for semantic search, knowledge-base lookups, etc. +

+ + +
+ + + +
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + +
+
+
+
+ +
+ Save pending changes to settings before clicking Fetch Embedding Models
+ + + + + + + + +
+ +
+
+
+ + +
+
+
+
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + +
+
+
+ + +
+
+ + +
+
+ Image Generation Configuration +
+

+ Configure image generation settings. Enable/disable, set endpoints, and choose a model. +

+ + +
+ + + +
+
+ +
+ + + +
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + +
+
+
+
+ +
+ + Save pending changes to settings before clicking Fetch Image Generation Models
+ + + + + + + + +
+ +
+
+
+ + +
+
+
+
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + +
+
+
+
+ + +
+
+ + + + {% include '_multiendpoint_modal.html' %} + + + +
diff --git a/application/single_app/templates/admin/_panes/citation.html b/application/single_app/templates/admin/_panes/citation.html new file mode 100644 index 000000000..3c6542a29 --- /dev/null +++ b/application/single_app/templates/admin/_panes/citation.html @@ -0,0 +1,244 @@ +
+ +

+ Configure standard and enhanced citations features for your and group workspaces. +

+ + +
+
+ Standard Citations +
+

+ Standard citations is always enabled for both Your Workspace and Group Workspace. +

+

+ Users can see text content of the source/citation for documents. +

+
+ + +
+
+ Enhanced Citations +
+

+ Enable Enhanced Citation to store files in a Storage Account, + and show direct references (Preview feature, files are saved to storage, presentation + layer will be available in a future release). +

+ + {% set ec_storage_status = settings.enhanced_citations_storage_status or {} %} + {% set ec_storage_state = ec_storage_status.state or 'not_initialized' %} + {% if settings.enable_enhanced_citations %} +
+
Enhanced Citations storage startup status
+
{{ ec_storage_status.message or 'Enhanced Citations storage status is not available for this process.' }}
+
+ Startup skips live storage container checks so storage outages do not block application boot. + Use the connection test to validate current storage reachability and container access. +
+ +
+
+ {% endif %} + + +
+ + + +
+ + + +
+
diff --git a/application/single_app/templates/admin/_panes/control-center-config.html b/application/single_app/templates/admin/_panes/control-center-config.html new file mode 100644 index 000000000..dd2143276 --- /dev/null +++ b/application/single_app/templates/admin/_panes/control-center-config.html @@ -0,0 +1,180 @@ +
+

+ Configure Control Center access and permissions for administrators. +

+ +
+
+
+
+ +
+
+

+ Automatic Data Refresh +

+

Run the Control Center metrics refresh overnight on a daily timezone-aware schedule

+
+
+
+ +
+ + + +
+ +
+
+ + + +
+ Your browser timezone is local time. +
+
+
+ +
+ {% if settings.control_center_auto_refresh_next_run %} + Loading local time... + {% else %} + Will be calculated when saved + {% endif %} +
+
+
+
+ + +
+
+
+
+ +
+
+

+ Control Center Access +

+

Manage who can access Control Center features and administrative tools

+
+
+ +
+ +
+ + About Control Center: The Control Center is a powerful administrative dashboard that provides user management, group oversight, public workspace control, and detailed activity monitoring. Use role-based access controls below to delegate administrative responsibilities. +
+ + +
+
+ + +
+

+ Required app role value: ControlCenterAdmin. Assign this role to users or groups in the Enterprise App before enabling the requirement. When enabled, only users assigned this role can access the Control Center and all management features. Regular Admins will lose access. +

+
+ + User Management + + + Group Management + + + Public Workspaces + + + Activity Logs + +
+
+ + +
+
+ + +
+

+ Dashboard-only app role value: ControlCenterDashboardReader. Assign this role to users or groups in the Enterprise App before enabling dashboard-only access. Users assigned ControlCenterAdmin can also access the dashboard when Control Center app-role enforcement is enabled. +

+
+ + Dashboard Statistics + + + Activity Trends + + + Usage Metrics + +
+
+ +
+ + Important: Configure these roles in your identity provider (Entra ID). When role requirements are enabled, standard Admins without the specific roles will be denied access to Control Center features. +
+ + +
+
+ Best Practices +
+
    +
  • ControlCenterAdmin: Grant to IT administrators who need full control over users, groups, and workspaces
  • +
  • ControlCenterDashboardReader: Grant to managers, compliance officers, or stakeholders who need visibility into platform usage without administrative powers
  • +
  • Separation of Duties: Enable role requirements if you need to restrict Control Center access from general application admins
  • +
  • Audit Trail: All Control Center actions are logged in Activity Logs for compliance and security auditing
  • +
+
+
+
diff --git a/application/single_app/templates/admin/_panes/custom-pages.html b/application/single_app/templates/admin/_panes/custom-pages.html new file mode 100644 index 000000000..a3d0f32ef --- /dev/null +++ b/application/single_app/templates/admin/_panes/custom-pages.html @@ -0,0 +1,81 @@ +
+

+ Enable trusted deployment-time custom pages and manage metadata for simple static pages served from the application custom_pages folders. +

+ +
+
+
+ Custom Pages +
+ +
+
+ + + + +
+

When disabled, /custom routes return Not Found before loading custom metadata, files, or Python extensions.

+ +
+
+ + + This name appears when custom pages are grouped into a menu. +
+ +
+ + + When disabled, 1-2 custom pages show as top-level nav items and 3+ pages show as a menu. +
+ +
+
+
Static Page Metadata
+

Create metadata contracts for simple pages that use files from custom_pages/html, css, js, assets, and json.

+
+
+ + +
+
+ +
+ + + + + + + + + + + + + + + + + + +
SlugTitleTypeAccessRolesStatusNavActions
Loading custom pages...
+
+
+
+
diff --git a/application/single_app/templates/admin/_panes/data-management.html b/application/single_app/templates/admin/_panes/data-management.html new file mode 100644 index 000000000..aba966397 --- /dev/null +++ b/application/single_app/templates/admin/_panes/data-management.html @@ -0,0 +1,1621 @@ +
+
+
+

Backup, Migrate & Restore

+

Protect SimpleChat data, move selected workspaces to another environment, and stage restore decisions with guided checks.

+
+ +
+ + + +
+ +
+
+
+

Start Here

+

Use these checkpoints before running backup, migration, restore, or advanced repair actions.

+
+ +
+
+
+
+
Back up
+

Configure dedicated storage, encryption, schedule, and backup scope before queueing jobs.

+ +
+
+
+
+
Migrate
+

Connect a destination, choose who moves, run preflight, then execute a recoverable transfer.

+ +
+
+
+
+
Restore
+

Review backup readiness and stage restore decisions from Backup Inventory.

+ +
+
+
+
+
RU Boost
+

Temporarily raise eligible Cosmos capacity during approved backup or migration windows.

+ +
+
+
+
+ +
+
+
+ +
+
+

Backup

+

Configure when backups run, where artifacts are stored, and how backup files are encrypted.

+
+
+ +
+
+
+
Schedule
+

Full backups run on the selected cadence; partial backups run daily only.

+
+
+
+ + +
+
+
+ + +
+
+ + +
Default is 03:00 UTC.
+
+
+ +
+ + + +
+ +
Automatic cleanup keeps the newest successful full backup as a safety baseline.
+
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ +
+
+
+ +
+
+
+ + +
Core application records required for meaningful restore and migration.
+
+
+
+
+ + +
Search index schemas and retrievable indexed documents.
+
+
+
+
+ + +
Original source files used by Enhanced Citations.
+
+
+
+
+
+
+
+ +
+
+
+
Storage
+

Store backup artifacts in Azure Blob Storage.

+
+ +
+
+ + Use a dedicated backup storage account. Data Management will reject storage that matches the Enhanced Citations connection string or Blob endpoint. +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + +
No connection string saved yet.
+
+
+ + +
+
+
+ +
+
+
+
Encryption
+

Generate a 256-bit backup encryption key.

+
+ +
+
+ + +
+
+
Key storage
+
Not configured
+
Key reference
+
Not configured
+
+
+ +
+
Key Vault is strongly recommended
+
Generated backup encryption keys are stored in the Data Management settings document when Key Vault is not enabled.
+ Open Key Vault settings +
+
+
+ +
+
+
+
Cosmos Backup Performance
+

Backups stream deterministic checkpoint batches and commit only verified work. Higher concurrency can increase source Cosmos cost and pressure.

+
+ +
+ +
+
+
Source Blob Backup Performance
+

Source files stream through bounded chunks and durable per-file checkpoints. Peak transfer buffering is bounded by concurrent transfers multiplied by chunk size.

+
+
+
+ + +
+
+ + +
+
+ + +
+
+
Defaults bound application transfer buffering to approximately 32 MiB, excluding Azure SDK overhead. Throttling temporarily reduces active transfers.
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
The backup records the current source capacity, raises only eligible targets up to 10,000 RU/s, and restores the original setting after completion, cancellation, failure, or recovery. This can increase Cosmos charges and requires source ARM throughput permission.
+
+
+
+ +
+
+
+
Backup Operations
+

Queue immediate full or partial backup jobs using the settings above.

+
+
+ + +
+
+

Jobs use Cosmos-backed leases so scaled-out App Service workers do not run the same backup twice.

+
+
+ + +
+
+
+

+ Migration +

+

Move SimpleChat data through a reviewed, recoverable environment transfer.

+
+
+ + Not reviewed +
+
+ +
+ + + + + +
+
+
+
Connect the destination
+

Configure the services this migration will write to. Stored credentials remain redacted.

+
+ Destination database: SimpleChat +
+ +
+
+
+
+
Target Cosmos Database
+

Required for every migration.

+
+ +
+
+ Managed identity requires Cosmos DB Data Contributor and target network access. +
+
+
+ + +
+
+ + +
+
+ + +
Fixed app contract.
+
+
+
+ + +
+
+ +
+
+
+
Target Search
+

Required when AI Search documents are included.

+
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+
+
+
Target Enhanced Citation Storage
+

Required only when source document blobs are included.

+
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ + +
+
+
+
Choose who and what moves
+

Selections persist while you search and page. “All” always uses the exhaustive server count.

+
+
0 principal scopes selected
+
+ +
+ + + +
+ +
+ +
+ Migration mode +
+ + + +
+
+ +
+
+
+
+
Available users
+

Search the server catalog.

+
+ +
+
+
+ + Page 1 + +
+
+ +
+ +
+ +
+ Loading exhaustive count… + Every current user record will be resolved by the server when the job starts. +
+
+ +
+ + +
+
+ +
+ +
+ Migration mode +
+ + + +
+
+
+
+
+
+
Available groups
+

Search the server catalog.

+
+ +
+
+
+ + Page 1 + +
+
+ +
+
+ +
+ Loading exhaustive count… + Every current group record will be resolved by the server when the job starts. +
+
+
+ + +
+
+ +
+ +
+ Migration mode +
+ + + +
+
+
+
+
+
+
Available public workspaces
+

Search the server catalog.

+
+ +
+
+
+ + Page 1 + +
+
+ +
+
+ +
+ Loading exhaustive count… + Every current public workspace record will be resolved by the server when the job starts. +
+
+
+ + +
+
+
+ + +
+
+
+
Choose what happens at the destination
+

Choose whether to copy only missing items, catch up changes, or make migrated destination data match the source.

+
+
+ +
+ Destination behavior +
+ + + + + + +
+

+ Copies source items that are absent from the destination. Existing destination data is never updated or deleted. +

+
+
+ + +
Leave blank to let SimpleChat choose the latest compatible completed migration as the starting point for this catch-up run.
+
+
+
+ +
+ Data surfaces +
+
+
+ + +
+
+ + +
SimpleChat pauses its own target indexing. Freeze other writers before review.
+
+
+
+
+ + +
Requires Enhanced Citation storage at both source and destination.
+
+
+
+
+ +
+ Performance and resume +

Migration uses durable resource checkpoints and retains the same migration ID after Retry or Resume.

+
+
+ + +
+
+ + +
+
+ + +
+
+
+ + + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
RU Boost validates Azure management-plane throughput permissions separately from Cosmos data-copy access. Eligible capacity is raised only up to 10,000 RU/s during execution and restored after completion or failure.
+
+
+
+
+ + +
+
+
+
Prove the plan is ready
+

Preflight runs server-owned access probes and inventory. Any earlier change makes this review stale.

+
+
+ + +
+
+
+ +
+ Review has not run. + Run preflight to verify target access, counts, collisions, locks, and capacity policy. +
+
+
+
+ +
+ + +
+
+
+
Confirm execution
+

Review the final server-normalized plan. Submission is guarded against duplicate requests.

+
+
+
+

Complete preflight review before confirmation.

+
+ +
+ + +
+ +
+ + +
+
+
+
Operate the migration
+

Progress comes from the durable job record. Cancel, Retry, and Resume retain verified checkpoints.

+
+
+ + +
+
+
+ +
+ No migration is attached to this workflow yet. + After execution, this stage follows the queued job and exposes its recovery actions. +
+
+
+
+
+
+
+
+ +
+
+ + +
+ Step 1 of 6 +
+
+
+ +
+
+
+

Cosmos DB JSON Editor

+

Query SimpleChat Cosmos DB containers, inspect one document, and save JSON changes with ETag protection.

+
+ +
+ +
+ The Cosmos DB JSON editor is locked. Acknowledge the danger prompt before querying or editing data. +
+
+
+
+ + +
Choose a known SimpleChat Cosmos DB container.
+
+
+ + +
Max 100 per request.
+
+
+ + +
Empty query returns only the first 100 documents. Custom SELECT queries can page beyond 100 with Next Page.
+
+
+
+ + No query has run yet. +
+
+ Query results and the JSON editor open in a modal so the Data Management page stays compact. +
+
+
+ +
+
+
+

Backup Inventory

+

Track completed full and partial backups created by Data Management jobs.

+
+
+ + + +
+
+
+
+
What does Run Retention Cleanup do?
+

+ It permanently deletes backups whose age exceeds the retention period configured in Data Management settings, + and removes their stored artifacts from the backup container. Backups newer than the retention cutoff are left alone. +

+
    +
  • Only backups in a finished state are eligible; running or queued jobs are skipped.
  • +
  • When Keep latest full backup is enabled, the most recent successful full backup is protected even if it is past the cutoff.
  • +
  • Each run deletes at most 25 backups, so very large cleanups may need several runs.
  • +
  • Cleanup also runs automatically on the configured schedule; this button just runs it now.
  • +
+

+ Seeing “found no expired backups to delete” means every backup is still inside the retention window. That is expected, not an error. +

+
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + + + + + + + + + + + + + + + + +
BackupCompletedContentsStorageProtectionWarningsActions
Backup inventory has not loaded yet.
+
+ +
+ + + +
+
+

Job History

+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + + + + + + + + + + + + + + + +
CreatedOperationStatusProgressMessageActions
Job history has not loaded yet.
+
+ +
+ + + + + + + + + + + + + + + + + + + + +
diff --git a/application/single_app/templates/admin/_panes/file-sync.html b/application/single_app/templates/admin/_panes/file-sync.html new file mode 100644 index 000000000..9a1d3c6e8 --- /dev/null +++ b/application/single_app/templates/admin/_panes/file-sync.html @@ -0,0 +1,411 @@ +
+

+ Configure file synchronization for personal, group, and public workspaces. +

+ +
+
+
+ File Sync +
+ +
+

+ Enable File Sync globally, set conservative run limits, and choose whether workspace managers or SimpleChat admins manage sync sources. +

+ + + + {% if not settings.file_sync_redis_ready %} + + {% elif settings.requested_enable_file_sync and not settings.file_sync_effective_enabled %} + + {% endif %} + +
+ + + +
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+
+ + +
+
+
+ + {% set file_sync_visible_source_types = settings.file_sync_visible_source_types|default(['smb', 'azure_files']) %} +
+
Visible Source Types
+

+ Choose which source types appear in the Add Source workflow. OneDrive, SharePoint, and Google Workspace connectors are coming soon while validation continues. +

+
+
+
+ + +
Available now.
+
+
+
+
+ + +
Available now.
+
+
+
+
+ + +
Available now.
+
+
+
+
+ + +
Coming Soon.
+
+
+
+
+ + +
Coming Soon.
+
+
+
+
+ + +
Coming Soon.
+
+
+
+
+ + + +
+
+
+
Personal Workspace Sync
+
+ + +
+
+ + +
+
+ + +
+
Required app role value: PersonalFileSyncUser. Assign this role to users or groups in the Enterprise App before enabling the requirement for personal sync.
+
+
+ +
+ + +
+
+
+ + +
+
No user selected.
+
+
+
+ +
+
+
Group Workspace Sync
+
+ + +
+
+ + +
+
+ + +
+
+ + No groups assigned. +
+ +
+
+ +
+ + +
+
+
+ + +
+
No group selected.
+
+
+
+ +
+
+
Public Workspace Sync
+
+ + +
+
+ + +
+
+ + +
+
+ + No public workspaces assigned. +
+ +
+
+ +
+ + +
+
+
+ + +
+
No public workspace selected.
+
+
+
+
+ +
+ SMB source credentials are stored in Azure Key Vault when Key Vault secret storage is enabled; otherwise they use the app's existing encrypted settings path. +
+
+
+ + + + + + + + +
diff --git a/application/single_app/templates/admin/_panes/general.html b/application/single_app/templates/admin/_panes/general.html new file mode 100644 index 000000000..1275b3c22 --- /dev/null +++ b/application/single_app/templates/admin/_panes/general.html @@ -0,0 +1,682 @@ +
+ +

+ Configure general application settings, including the application's title, logo, and landing page text. +

+
+
+ Branding +
+

+ Configure your application's title, logo, and branding elements. +

+
+ + +
+
Logo Settings
+
+ + + +
+
+ + + +
+
+
+ + {{ settings.landing_page_logo_scale_percent | default(100) }}% +
+ +
+ 50% + 500% +
+ + Adjust the logo size on the home page only. This does not change the logo size in the top or sidebar navigation. + +
+
+ + + This logo will be displayed in light mode and stored at up to 500px tall so the main page can render it sharply without keeping oversized assets in settings. +
+
+ + + This logo will be displayed in dark mode. If not provided, the light mode logo will be used in both themes. Dark logos are also stored at up to 500px tall. +
+
+ + + Recommended: 16x16 or 32x32 pixel png or jpg or ico. +
+
+ +
+
+ Home Page Text +
+

Configure the text content displayed on your application's home page using Markdown formatting.

+ +
+ + + Choose how the landing page markdown is aligned on the home page. +
+ +
+ + + +
+ + + + +
+ +
+
+ Appearance +
+

Configure the app appearance and theme settings.

+ +
+ +
+ + + +
+

Users can still toggle dark mode individually from the navigation bar.

+
+ +
+ +
+ + + +
+

Users can still toggle the left navigation sidebar individually from the header.

+
+
+ +
+
+
+ Health Check +
+ +
+

Configure health check endpoint for external monitoring systems.

+ +
+
+
+
+
+
Authenticated Endpoint
+

Use when your monitoring system can reach a protected route and you want the endpoint governed by the normal app access boundary.

+
+ Protected +
+
+ + + +
+

Best for internal monitors or diagnostics tooling that already authenticates to the application.

+
+
+
+
+
+
+
Unauthenticated Endpoint
+

Use when a platform health probe cannot sign in and only needs a lightweight availability response.

+
+ No Auth +
+
+ + + +
+ +
+
+
+
+ +
+
+
+ API Documentation +
+
+ + + Open Swagger UI + +
+
+

Configure automatic OpenAPI/Swagger documentation for API endpoints.

+ +
+ + + +
+

+ Provides interactive API documentation, endpoint testing, and schema validation. + Useful for developers, API integration, and system troubleshooting. +

+
+ +
+
+ Classification Banner +
+
+ + + +
+
+ + +
+
+ + +
+
+ + +
+
+ + {{ settings.classification_banner_text or 'Banner Preview' }} + +
+
+ +
+
+ Chat AI Notice +
+

+ Display an administrator-provided reminder directly below the chat input, such as guidance that responses are AI-generated and should be reviewed. +

+
+ + +
+
+
+ + +
Plain text only. Line breaks are preserved.
+
+
+ + +
Changing the notice text or display behavior creates a new message version and shows it again.
+
+
+
+ +
+
+ Terms of Use +
+

+ Require users to accept the Terms of Use before using SimpleChat. Standard Microsoft sign-in users see it before authentication; Teams SSO and other passive sign-in flows are gated immediately after the app session is created. +

+ +
+ + + +
+ +
+
+ + +
+
+ + + Changing the title, message, or frequency creates a new terms version that users must accept again. +
+
+ +
+ + + Plain text is shown to users with line breaks preserved. +
+ +
+ + + Use a local path such as / or an admin-approved HTTP(S) URL. Signed-in users are locally logged out before this redirect. +
+ +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+ Support +
+
+ + + +
+

When enabled, users can access Support destinations like Send Feedback and Latest Features directly from navigation.

+ +
+
+ + + This name will appear in user navigation as the Support menu title. +
+ +
+ + + +
+ +
+ + + User Send Feedback drafts will be addressed to this internal email address. +
+ +
+ + + +
+ +
+
User-Facing Latest Features
+

Choose which recent features are shared on the user Latest Features page. Deployment and Redis start unchecked because they are mainly admin-facing rollout and infrastructure topics.

+
+ + + + When enabled, user-facing Latest Features cards can show public guide buttons in addition to the direct in-app shortcuts. +
+ {% for release_group in support_latest_feature_release_groups %} + {% set checklist_collapse_id = release_group.collapse_id ~ 'Checklist' %} +
+
+
+
+
{{ release_group.label }}{% if release_group.release_version %}v{{ release_group.release_version }}{% endif %}
+

{{ release_group.description }}

+
+ {% if not release_group.default_expanded %} + + {% endif %} +
+
+
+ {% for feature in release_group.features %} +
+
+ + +
+
+ {% endfor %} +
+
+
+
+ {% endfor %} +
+
+
+ + + +
+
+ System Settings +
+

+ System-level settings that control application behavior, including file size limits, conversation history, + and default prompts. +

+
+ + +
+
+ + +
+
+ + + +
+
+
+ + + Users are logged out locally after this many minutes of inactivity. Minimum value: 10 minutes. +
+
+ + + Show the warning modal after this many minutes of inactivity. Set this equal to the logout timeout to disable the warning dialog window. +
+
+ + + Custom text shown at the top of the idle warning dialog. +
+
+
+ + +
+
+ + Shown to signed-in users who lack the required roles. Use Enter for line breaks. + +
+
+
diff --git a/application/single_app/templates/admin/_panes/governance.html b/application/single_app/templates/admin/_panes/governance.html new file mode 100644 index 000000000..450af4b92 --- /dev/null +++ b/application/single_app/templates/admin/_panes/governance.html @@ -0,0 +1,250 @@ +
+
+

+ Configure governance enforcement for endpoint, agent, and action creation or usage. Governance is disabled by default and only enforced when toggled on. +

+ +
+ +
+
+ Governance Feature Toggles +
+

Turn on governance checks for each feature area. Save with the main Settings button to persist these toggles.

+ +
+
+
Personal Scope
+
+ + +
+
+ + +
+
+ + +
+
+
+
Group Scope
+
+ + +
+
+ + +
+
+ + +
+
+
+
Global Scope
+
+ + + Always On +
+
+ + +
+
+ + +
+
+
+
+ +
+
+ MCP Action Destination Governance +
+

+ Restrict outbound remote MCP servers by destination pattern and scope. Destination policies reuse delegated item policies, so admins can allow all users, specific users, or workspace groups for each approved destination. +

+
+
+
+ + +
+
+ + +
+
+ Save with the main Settings button. Unsafe literal-IP blocking can be enabled even before the allowlist is enforced. +
+
+
+
Supported destination patterns
+
    +
  • * for any remote MCP destination after identity/auth checks.
  • +
  • preconfiguration:microsoft_learn or preset:generic.
  • +
  • *.contoso.com, https://example.com/mcp*, or transport:streamable-http.
  • +
  • group:<group-id>::preconfiguration:github for one group-specific override.
  • +
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ +
+
+ Inbound MCP Source Governance +
+

+ Control which delegated users and groups can use SimpleChat as an inbound MCP server through source-scoped policies. +

+ +
+
+
Policy required for inbound MCP
+
    +
  • inbound_mcp_source with delegated item *: allows selected users or groups to use any accepted source ID.
  • +
  • inbound_mcp_source with a configured source ID: allows selected users or groups to use only requests with that source value.
  • +
+
+ Client, tenant, delegated scope, and Entra role checks remain in Inbound MCP configuration. The source ID comes from the configured header when present and is advisory unless a trusted gateway sets or validates it. +
+
+
+
+
+ + +
+
+
+
+
+ +
+
+
Feature Policies
+ +
+

Set allow-all, explicit allow lists, and block lists for each governed feature. Block lists override allow settings.

+
+ + + + + + + + + + +
FeatureAllow AllAllow ListBlock List
+
+
+ +
+
+
Delegated Item Policies
+
+ + +
+
+

Manage delegated governance for configured global resources and action type entitlements that admins assign to specific users or groups.

+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + + + + + + + + + + +
PolicyEntity TypeItemAllow AllActions
+
+ +
+
+
+ + +
+
+
+ + +
diff --git a/application/single_app/templates/admin/_panes/latest-features.html b/application/single_app/templates/admin/_panes/latest-features.html new file mode 100644 index 000000000..1ee42a66a --- /dev/null +++ b/application/single_app/templates/admin/_panes/latest-features.html @@ -0,0 +1,1253 @@ +
+

+ Review the main capabilities introduced in v{{ config['VERSION'] }} and use this page as an admin-facing overview for what to highlight with your teams. +

+ + + {% for release_group in admin_latest_feature_release_groups %} + {% if release_group.id == 'current_release' %} +
+
+
{{ release_group.label }}
+

{{ release_group.description }}

+
+ {% if release_group.release_version %} + v{{ release_group.release_version }} + {% endif %} +
+ + {% for feature in release_group.features %} + {% set feature_card_id = 'latest-features-' ~ feature.id|replace('_', '-') ~ '-card' %} + {% set feature_collapse_id = feature_card_id ~ '-details' %} +
+
+
+
{{ feature.title }}
+

{{ feature.summary }}

+
+ +
+
+
+ {% if feature.details %} +

{{ feature.details }}

+ {% endif %} + {% if feature.why %} + + {% endif %} + {% if feature.guidance %} +
{% if feature.images %}Screenshot and rollout notes{% else %}Rollout notes{% endif %}
+
    + {% for tip in feature.guidance %} +
  • {{ tip }}
  • + {% endfor %} +
+ {% endif %} + {% if feature.images %} + + {% endif %} + {% if feature.actions %} +
+ {% for action in feature.actions %} + + {{ action.label }} + + {% endfor %} +
+ {% endif %} +
+
+
+ {% endfor %} + {% else %} + {% set release_card_id = 'latest-features-' ~ release_group.id|replace('_', '-') ~ '-card' %} + {% set release_collapse_id = release_group.collapse_id %} +
+
+
+
{{ release_group.label }}
+

{{ release_group.description }}

+
+ +
+
+
+ {% if release_group.release_version %} + + {% endif %} +
+ {% for feature in release_group.features %} +
+
+
+
+
{{ feature.title }}
+

{{ feature.summary }}

+
+

{{ feature.details }}

+ {% if feature.guidance %} +
    + {% for tip in feature.guidance %} +
  • {{ tip }}
  • + {% endfor %} +
+ {% endif %} + {% if feature.images %} + + {% endif %} + {% if feature.actions %} +
+ {% for action in feature.actions %} + + {{ action.label }} + + {% endfor %} +
+ {% endif %} +
+
+
+ {% endfor %} +
+
+
+
+ {% endif %} + {% endfor %} + + {% if false %} +
+
+
+
+ Registered / Unregistered Badge +
+

The badge next to the Admin Settings version number shows whether this admin instance is registered for latest release and community call notifications.

+
+ +
+
+
+ +

Use this badge as the quick status check for whether your environment is already registered to receive SimpleChat release updates.

+
    +
  • Unregistered means this environment has not saved release notification registration details yet.
  • +
  • Registered means the environment already has saved contact details for release and community call notifications.
  • +
  • Clicking the badge opens the registration modal where admins can add or edit the name, email, and organization for this instance.
  • +
  • Submitting the form opens a prefilled email draft to simplechat@microsoft.com and stores the registration details back in Admin Settings.
  • +
  • The badge lives beside the version number at the top of Admin Settings so admins can confirm status without hunting through tabs.
  • +
+
+
+
+ +
+
+
+
+ Performance Improvements for Apps running Native Python (not containers) +
+

Native Python App Service deployments should use Gunicorn as the startup command. Container deployments already do this for you.

+
+ +
+
+
+

If you run the native Python App Service deployment instead of the container deployment, set the App Service Startup command to the following:

+
python -m gunicorn -c gunicorn.conf.py app:app
+

Use container deployments when possible if you want the runtime defaults and startup behavior handled by the shipped image.

+ + +
+
+
+ +
+
+
+
+ Guided Tutorials +
+

New guided walkthroughs help users learn the live chat and personal workspace interfaces without leaving the app.

+
+ +
+
+
+

Admins can use these walkthroughs to accelerate onboarding for new users and reduce orientation time across the primary product surfaces.

+
    +
  • Guided Chat Tutorial covers the active chat experience, message actions, search flows, citations, thoughts, and export-related actions.
  • +
  • Personal Workspace Guided Tutorial walks through uploads, search, filters, list and grid views, prompts, agents, actions, and tags.
  • +
+ +
+
+
+ +
+
+
+
+ Background Chat Completion +
+

Personal chats can continue finishing after a user leaves the chat page, and completed responses surface as notifications with unread indicators.

+
+ +
+
+
+

This makes long-running responses more reliable for users who navigate into other areas of the app before generation finishes.

+
    +
  • Completion notifications deep-link back into the finished conversation.
  • +
  • Unread markers remain visible until the response is actually opened.
  • +
  • Personal, workspace, and navigation flows now behave more consistently while chat completion continues in the background.
  • +
+ +
+
+
+ +
+
+
+
+ Multi-Endpoint GPT Selection +
+

Admins can expose multiple GPT choices across Azure OpenAI and Azure AI Foundry, set a saved default fallback model, and now review agent rebinding in a modal before applying changes.

+
+ +
+
+
+

This is the high-value admin control for teams that want broader GPT choice without locking every workflow to a single deployment.

+
    +
  • Configure multiple model endpoints and choose which GPT deployments are available to users.
  • +
  • Set a default fallback model so summarization, agent requests, and other background flows still resolve a usable GPT when an explicit selection is missing.
  • +
  • Use the admin-only Agent Default Model Review modal to inspect inherited agents, search and filter large agent sets, and explicitly choose which review rows should be overridden to the saved default.
  • +
+ + + +
+
+
+ +
+
+
+
+ Tabular Data Analysis +
+

Enhanced citations now unlock a more consistent tabular analysis experience for normal chat and workspace-backed files, not just dedicated agent scenarios.

+
+ +
+
+
+

This is the highest-impact settings area to mirror here because it combines storage-backed citations, tabular previews, and consistent behavior across workspace and chat uploads.

+
    +
  • Tabular files can be analyzed with the same core approach whether they come from chat uploads or any workspace container.
  • +
  • Enhanced citations provide the blob-backed dependency for richer tabular processing and citation transparency.
  • +
  • Admins can right-size tabular preview limits based on the memory profile of their runtime.
  • +
+ +
+ + +
+ +
+
+
All filetypes
+
+ + +
+ +
+ +
+ + +
+
+ +
+ +
+ + +
+
+
+ +
+
Tabular Preview Limits
+
+ + + + Mirror of the Citations setting. Larger values support bigger previews but increase runtime memory pressure. + +
+
+
+ + +
+
+
+ +
+
+
+
+ Citation Improvements +
+

Conversation history citation replay and citation amplification keep more grounded evidence available across follow-up turns without making the chat history unwieldy.

+
+ +
+
+
+

These improvements make follow-up questions more reliable because the system can reuse prior grounded evidence instead of relying only on the assistant's last prose response.

+
    +
  • Conversation history citations carry forward prior citation summaries so later prompts can reuse earlier grounded results.
  • +
  • Citation amplification keeps richer supporting payloads available for inspection and export while keeping prompt history compact.
  • +
  • This is especially useful for larger or tabular outputs where users often ask multiple follow-up questions against the same evidence chain.
  • +
+ + +
+
+
+ +
+
+
+
+ Document Revisioning and Management +
+

Uploading a document with the same name now creates a new revision instead of overwriting the previous file, while older versions remain available for traceability and later analysis.

+
+ +
+
+
+

This improves both document management and citation integrity for long-lived conversations that span multiple document updates.

+
    +
  • Same-name uploads create a revision family with an incremented current version instead of destroying prior history.
  • +
  • Previous versions are retained for analysis or comparison, while workspace and search views focus on the current revision by default.
  • +
  • Older conversations can still resolve citations against the correct historical document version.
  • +
  • Updated revisions inherit document classifications and tags so admins do not have to reapply metadata after every refresh.
  • +
+ +
+
+
+ +
+
+
+
+ Conversation Summaries and Export +
+

Conversation export now includes persistent summaries, PDF output, and easier per-message reuse and sharing actions.

+
+ +
+
+
+
    +
  • Conversation details now surface a summary card with generate and regenerate actions.
  • +
  • Export supports PDF alongside existing export options.
  • +
  • Per-message actions support Markdown export, Word export, open in email, and use as prompt.
  • +
+
+
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+ Agent and Action Operations +
+

Recent updates improve manageability for agents and actions with richer views, easier SQL connectivity checks, and better operational tracking.

+
+ +
+
+
+
    +
  • List and grid view toggles give admins and users a better way to browse agents and actions.
  • +
  • SQL actions now include an inline Test Connection button before save.
  • +
  • User tracking and activity logging improve auditability for agent and action changes.
  • +
+ +
+
+
+ +
+
+
+
+ AI Transparency +
+

Processing Thoughts provide a clearer view into model activity, duration, and execution flow while responses are being generated.

+
+ +
+
+
+

Admins can enable this once and let users see more of the model pipeline, including sending, generating, and responded states with clearer timing information.

+ +
+ + +
+ +
+
+
+ +
+
+
+
+ Fact Memory +
+

Users can now save Instructions and Facts on their profile so the assistant can apply durable preferences every time and recall relevant personal context only when needed.

+
+ +
+
+
+

Fact Memory gives users a compact profile experience for managing two distinct memory types: always-on Instructions and relevance-based Facts.

+
    +
  • Instructions act like stable user preferences and are applied on every prompt unless the current message overrides them.
  • +
  • Facts are recalled only when relevant and now appear as dedicated thought and citation entries in chat.
  • +
  • The profile page includes a compact add flow plus a popup manager for search, paging, edit, retag, and delete actions.
  • +
  • Admins can choose whether this announcement is visible on the user-facing Latest Features page from General > User-Facing Latest Features.
  • +
+ + +
+
+
+ +
+
+
+
+ Redis and Key Vault +
+

Redis configuration now supports using a Key Vault secret name instead of storing the access key directly in settings.

+
+ +
+
+
+

This is useful for teams standardizing on Key Vault-backed secret storage while still using Redis for cache and session scenarios.

+
    +
  • Use Redis authentication type Key Vault when you want the stored value to represent a secret name instead of a raw Redis key.
  • +
  • This works best when Key Vault is already configured for the app.
  • +
+ +
+ + +
+ +
+
+
Redis Cache Settings
+
+ + +
+
+ + +
+
+ +
+ + +
+
+ When using Key Vault, store the secret name here instead of the raw Redis key. +
+
+
+
+ +
+
+
+ +
+
+
+
+ Send Feedback to the SimpleChat Team +
+

Admins now have a dedicated Send Feedback tab for bug reports and feature requests that opens a prefilled email draft and writes an audit record to the activity log.

+
+ +
+
+
+

Use the Send Feedback tab when you want to report a bug or suggest a feature directly from Admin Settings without leaving the page.

+
    +
  • Opens a prefilled email draft addressed to simplechat@microsoft.com.
  • +
  • Logs the action to the activity log so admins can audit when feedback emails were prepared.
  • +
  • Keeps the workflow simple by using text-only email content with no confusing pseudo-attachment step.
  • +
+ + Open Send Feedback + +
+
+
+ +
+
+
+
+ Support Menu +
+

Support can now expose user-facing Latest Features and Send Feedback workflows directly in everyday navigation.

+
+ +
+
+
+ +
    +
  • Admins can enable or disable the Support menu and rename it for user navigation.
  • +
  • Send Feedback now routes users to an internal recipient email address defined by admins.
  • +
  • The user-facing Latest Features page can be curated feature-by-feature so teams only share the items they want visible.
  • +
  • The General tab now includes a dedicated User-Facing Latest Features checklist so admins can quickly confirm which announcements will be visible to end users.
  • +
+ + Open General Settings + + +
+
+
+ {% endif %} + + {% for release_group in support_latest_feature_release_groups_preview %} + {% if release_group.id != 'current_release' %} + {% set preview_card_id = 'latest-features-user-preview-' ~ release_group.id|replace('_', '-') ~ '-card' %} + {% set preview_collapse_id = 'latestFeaturesUserPreview' ~ release_group.id|replace('_', ' ')|title|replace(' ', '') %} +
+
+
+
+ {{ release_group.label }} +
+

{{ release_group.description }}

+
+ +
+
+
+ +
+ {% for feature in release_group.features %} +
+
+
+
+
+
{{ feature.title }}
+

{{ feature.summary }}

+
+ + {% if settings.support_latest_features_visibility.get(feature.id, true) %}Shared with Users{% else %}Hidden from Users{% endif %} + +
+

{{ feature.details }}

+ {% if feature.guidance %} +
    + {% for tip in feature.guidance %} +
  • {{ tip }}
  • + {% endfor %} +
+ {% endif %} + {% if feature.images %} + + {% endif %} + {% if feature.actions %} +
+ {% for action in feature.actions %} + + {{ action.label }} + + {% endfor %} +
+ {% endif %} +
+
+
+ {% endfor %} +
+

Visibility still comes from General > User-Facing Latest Features, so admins can keep previous and archived release content available without promoting every item forever.

+
+
+
+ {% endif %} + {% endfor %} +
diff --git a/application/single_app/templates/admin/_panes/logging.html b/application/single_app/templates/admin/_panes/logging.html new file mode 100644 index 000000000..f8cf52e23 --- /dev/null +++ b/application/single_app/templates/admin/_panes/logging.html @@ -0,0 +1,177 @@ +
+

+ Configure logging settings for monitoring, debugging, and auditing purposes. These settings control various types of logging throughout the application including application insights, debug messages, and file processing events. +

+ +
+
+ Application Insights Logging +
+

Enable global logging to Application Insights for all agents and orchestration events.

+
+ + + +
+ +
+ +
+
+ Debug Logging +
+

Control debug print statements across the application for development and troubleshooting.

+
+ + + +
+ + +
+
+ + + +
+ +
+
+ + +
+
+ + +
+
+ +
+ {% if settings.debug_logging_turnoff_time %} + {% if settings.debug_logging_turnoff_time is string %} + {{ settings.debug_logging_turnoff_time }} + {% else %} + {{ settings.debug_logging_turnoff_time.strftime('%Y-%m-%d %H:%M:%S') }} + {% endif %} + {% else %} + Will be calculated when saved + {% endif %} +
+
+
+
+ + +
+ +
+
+ File Process Logging +
+

Enable logging of file processing events for debugging and auditing purposes. Logs are stored in the file_processing container in Cosmos DB.

+
+ + + +
+ + +
+
+ + + +
+ +
+
+ + +
+
+ + +
+
+ +
+ {% if settings.file_processing_logs_turnoff_time %} + {% if settings.file_processing_logs_turnoff_time is string %} + {{ settings.file_processing_logs_turnoff_time }} + {% else %} + {{ settings.file_processing_logs_turnoff_time.strftime('%Y-%m-%d %H:%M:%S') }} + {% endif %} + {% else %} + Will be calculated when saved + {% endif %} +
+
+
+
+ +
+
+
Delete stored logs
+

+ Permanently remove file processing logs from Cosmos DB. One month is treated as 30 days. +

+
+
+ + +
+ Enter a whole number greater than zero. +
+
+
+ + +
+
+ + +
+
+
+
+
diff --git a/application/single_app/templates/admin/_panes/safety.html b/application/single_app/templates/admin/_panes/safety.html new file mode 100644 index 000000000..b0b57c366 --- /dev/null +++ b/application/single_app/templates/admin/_panes/safety.html @@ -0,0 +1,280 @@ +
+ +

+ Configure content safety, archiving, and user feedback settings. If Content Safety is enabled, user + messages will be sent to the safety endpoint for analysis. If User Feedback is enabled, users will see + thumbs up/down to provide feedback on AI responses. +

+
+
+ Content Safety +
+

Enable content safety to filter out inappropriate content.

+
+ + + +
+ + +
+ + +
+ + + +
+ + +
+
+ + +
+
+ + +
+
+ +
+ + +
+
+
+ + +
+
+ + +
+
+ +
+ + +
+
+
+ + +
+ + +
Displayed when Content Safety blocks a chat message.
+
+ +
+ + + +
+

Disable this option to show only the configured message.

+ + +
+
+
+ + +
+
+ User Feedback +
+

Enable user feedback (thumbs up/down) for AI responses.

+
+ + + +
+
+ +
+
+ Desktop Conversation Notifications +
+

+ Allow users to receive an operating system notification when an AI response finishes while SimpleChat is open in a hidden or unfocused browser tab. +

+
+ + +
+

+ Users can turn notifications off from Profile. Browser permission is required, and notifications stop when the SimpleChat tab is closed. +

+
+ + +
+
+ Permissions +
+

Control which users can access specific administrative views related to safety and feedback.

+ + +
+ + + +
+

+ Required app role value: SafetyViolationAdmin. Assign this role to users or groups in the Enterprise App before enabling the requirement. If disabled, any user with the general Admin app role can access the Safety Violations admin page. +

+ + +
+ + + +
+

+ Required app role value: FeedbackAdmin. Assign this role to users or groups in the Enterprise App before enabling the requirement. If disabled, any user with the general Admin app role can access the User Feedback admin page. Requires Enable User Feedback to be active. +

+
+ + + +
+
+ Conversation Archiving +
+

When enabled, conversation deletions will be archived instead of permanently deleted.

+
+ + + +
+
+ +
diff --git a/application/single_app/templates/admin/_panes/scale.html b/application/single_app/templates/admin/_panes/scale.html new file mode 100644 index 000000000..ac6786cc6 --- /dev/null +++ b/application/single_app/templates/admin/_panes/scale.html @@ -0,0 +1,1628 @@ +
+ {% set enable_dai_debug = settings.enable_dai_debug | default(false) %} +

+ Configure Redis cache to improve enterprise scale and performance by caching session data. Enabling Redis allows you to horizontally scale your application across multiple instances without losing session data. +

+
+
+ Redis Cache +
+

+ Enable Redis Cache to store session data in a distributed cache. This is recommended for production and multi-instance deployments. +

+
+ + + +
+
+
+ +

(example: simple-chat.redis.cache.windows.net)

+ +
+
+ + +
+
+ +
+ + +
+
+ Enter the full Key Vault secret name. + Enable Key Vault for Agent and Action Secrets + must be enabled and configured. +
+
+ +
+
+
+
+
+
+ Redis Monitoring +
+

+ Monitor Redis availability, memory pressure, hit rate, evictions, and runtime cache usage before Redis-backed document list caching is enabled. +

+
+
+ + +
+
+
+
+
+
+
Configuration
+ Not loaded +
+
+
+
+
Health
+ Not loaded +
+
+
+
+
App Cache Runtime
+ Not loaded +
+
+
+
+
Session Runtime
+ Not loaded +
+
+
+
+
+
+
Ping Latency
+
Not loaded
+
+
+
+
+
Memory Usage
+
Not loaded
+
Not loaded
+
+
+
+
+
Connected Clients
+
Not loaded
+
+
+
+
+
Ops/sec
+
Not loaded
+
+
+
+
+
Keyspace Hit Rate
+
Not loaded
+
+
+
+
+
Tracked Keys
+
Not loaded
+
+
+
+
+
DAI Version Markers
+
Not loaded
+
Not loaded
+
+
+
+
+
DAI Cache Payloads
+
Not loaded
+
Not loaded
+
+
+
+
+
Expired / Evicted Keys
+
+ Not loaded + / + Not loaded +
+
+
+
+
+
Fragmentation Ratio
+
Not loaded
+
+
+
+
+
Error Replies
+
Not loaded
+
+
+
+
+
Rejected Connections
+
Not loaded
+
+
+
+
+
Redis Version
+
Not loaded
+
+
+
+
+
Monitoring Source
+
Not loaded
+
+
+
+
+
Last Checked
+
Not loaded
+
+
+
+
+
Last Error
+
Not loaded
+
+
+
+
+
+ + + +
+
+
+
+ Conversation Cache +
+

+ Cache conversation list, feed, and advanced-search results per user. Redis is optional; cache misses and disabled cache paths continue using source Cosmos queries. +

+
+ +
+
+
+
+ + +
+
When enabled, list/feed/search payloads are cached by user and version. Disabling this bypasses cache reads and writes without requiring Redis.
+
+
+ + +
Default 120 seconds. User-scoped version invalidation refreshes changed conversations; set to 0 to skip writing new entries.
+
+
+
+
+
+
Runtime Status
+ Not loaded +
+
+
+
+
15m Cache Hit Rate
+
Not loaded
+
+
+
+
+
15m Cache Hits / Misses
+
Not loaded
+
+
+
+
+
15m Cache Bypasses / Errors
+
Not loaded
+
+
+
+
+
15m Writes / Invalidations
+
Not loaded
+
+
+
+
+
15m Operation Mix
+
Not loaded
+
+
+
+
+
Last Cache Event
+
Not loaded
+
+
+
+
+
Last Invalidation
+
Not loaded
+
+
+
+
+ + Conversation cache metrics are lightweight in-process counters for the current app worker. Application Insights logs remain the durable fleet-wide source for cache warnings and fallback behavior. +
+
+
+
+ +
+
+
+
+ Cosmos Document Access Index +
+

+ Monitor the document access projection used to remove expensive cross-partition document access queries. +

+
+
+ + {% if enable_dai_debug %} + + + {% endif %} +
+
+ +
+ + Document access projection maintenance is automatic. The background scheduler repairs fail-open projection records first, then runs bounded backfill batches repeatedly while work remains. Production read metrics below show DAI-served reads, Redis cache hits, source fallbacks, RU, and latency without requiring shadow validation. + {% if enable_dai_debug %}Debug controls and shadow validation diagnostics are visible because enable_dai_debug is enabled in app settings.{% endif %} +
+ +
+ +
+
+
+
Container
+ Not loaded +
+
+
+
+
Write-through
+ Not loaded +
+
+
+
+
Read Path
+ Not loaded +
+
+
+
+
Redis List Cache
+ Not loaded +
+
+ {% if enable_dai_debug %} +
+
+
Shadow Validation
+ Not loaded +
+
+ {% endif %} +
+
+
Auto Maintenance
+ Not loaded +
+
+
+
+
Next Maintenance Action
+
Not loaded
+
+
+
+
+
More Work Pending
+
Not loaded
+
+
+
+
+
Active Loop Interval
+
Not loaded
+
+
+
+ + {% if enable_dai_debug %} +
+
Automatic Maintenance and Diagnostics
+
+
+
+ + +
+
Always on. New and changed documents synchronize into the access index and fail open to repair records if projection updates fail.
+
+
+
+ + +
+
Always on. Maintenance keeps running bounded repair and backfill batches until DAI is healthy.
+
+
+
+ + +
+
Compares source list results to projection rows and logs mismatches without changing reads.
+
+
+ + +
Documents processed per manual or scheduled batch.
+
+
+ + +
Fail-open repair records reconciled before each backfill batch.
+
+
+
+ +
+
Default Read Path
+
+
+
+ + +
+
Always on. DAI-backed document and tag list reads are the normal path; source-container fallback remains automatic when backfill is not ready, repairs are pending, or a DAI query fails.
+
+
+
+ + +
+
Uses Redis read-through caching for DAI document, tag, and legacy-count reads. If Redis is unavailable, reads bypass cache and use DAI directly.
+
+
+ + +
Default 900 seconds. Scope-version invalidation makes document changes visible immediately; TTL clears unreachable old entries.
+
+
+
+ {% endif %} + +
+
+
+
Backfill State
+ Not loaded +
+
+
+
+
Repair Backlog
+
Not loaded
+
+
+
+
+
15m DAI Read Attempts
+
Not loaded
+
+
+
+
+
15m Redis Cache Hit Rate
+
Not loaded
+
+
+
+
+
15m Cache Hits / Misses
+
Not loaded
+
+
+
+
+
15m Cache Bypasses / Errors
+
Not loaded
+
+
+
+
+
15m Cache Invalidations
+
Not loaded
+
+
+
+
+
15m Served from DAI
+
Not loaded
+
+
+
+
+
15m Source Fallbacks
+
Not loaded
+
+
+
+
+
15m Fallback Rate
+
Not loaded
+
+
+
+
+
15m DAI Read RU
+
Not loaded
+
+
+
+
+
15m Avg / P95 Latency
+
Not loaded
+
+
+
+
+
Last Fallback Reason
+
Not loaded
+
+
+
+
+
Last DAI Read Metric
+
Not loaded
+
+
+
+
+
Last Cache Event
+
Not loaded
+
+
+
+
+ + Production read metrics are lightweight in-process counters for the current app worker. Application Insights logs remain the durable fleet-wide source for fallback warnings and query failures. +
+
+ {% if enable_dai_debug %} +
+
+
Last Shadow Result
+ Not run +
+
+
+
+
Shadow Mismatches
+
Not loaded
+
+
+
+
+
Source / Validation RU
+
Not loaded
+
+
+
+
+
Validation Index RU
+
Not loaded
+
+
+
+
+
Candidate Read RU
+
Not loaded
+
+
+
+
+
Estimated Wave 5 Savings
+
Not loaded
+
+
+
+
+
Source / Candidate Latency
+
Not loaded
+
+
+
+
+
Estimated Wave 5 Latency
+
Not loaded
+
+
+
+
+ + Rolling decision metrics aggregate shadow-validation samples over recent windows. Use these totals to compare source container RU with candidate access-index RU before enabling the future read path or Redis document access cache. +
+
+
+
+
5m Source / Candidate RU
+
Not loaded
+
+
+
+
+
5m Estimated Wave 5 Savings
+
Not loaded
+
+
+
+
+
15m Source / Candidate RU
+
Not loaded
+
+
+
+
+
15m Estimated Wave 5 Savings
+
Not loaded
+
+
+
+
+
15m Validation Overhead
+
Not loaded
+
+
+
+
+
15m Shadow Samples
+
Not loaded
+
+
+ {% endif %} +
+
+
Current Scope
+
Not loaded
+
+
+
+
+
Completed Scopes
+
Not loaded
+
+
+
+
+
Total Documents Processed
+
Not loaded
+
+
+
+
+
Total Documents Failed
+
Not loaded
+
+
+
+
+
Rows Upserted
+
Not loaded
+
+
+
+
+
Rows Deleted
+
Not loaded
+
+
+
+
+
Last Batch Completed
+
Not loaded
+
+
+
+
+
Last Error
+
Not loaded
+
+
+
+
+ +
+
+
+
+ Cosmos Maintenance +
+

+ Review expected indexing policies and clean up stale operational cache documents without touching source configuration or user data. +

+
+
+ + + + +
+
+ +
+ + Indexing maintenance only adds missing expected composite indexes and preserves existing policy paths. Composite indexes can increase write-index overhead while improving supported lookup and ordered-query speed. Stale cleanup is allowlisted to obsolete cache artifacts, supports dry-run review, and deletes at most one bounded batch per run. +
+
+ +
+
+
+
Indexing Policy Status
+ Not loaded +
+
+
+
+
Indexing Mode
+
Not loaded
+
+
+
+
+
Containers Checked
+
Not loaded
+
+
+
+
+
Missing Expected Indexes
+
Not loaded
+
+
+
+
+
Updated Containers
+
Not loaded
+
+
+
+
+
Indexing Failures
+
Not loaded
+
+
+
+
+
Last Indexing Evaluation
+
Not loaded
+
+
+
+
+
Stale Cleanup Status
+ Not loaded +
+
+
+
+
Cleanup Mode
+
Not loaded
+
+
+
+
+
Cleanup Candidates
+
Not loaded
+
+
+
+
+
Deleted Docs
+
Not loaded
+
+
+
+
+
Cleanup Failures
+
Not loaded
+
+
+
+
+
More Candidates
+
Not loaded
+
+
+
+
+
Cleanup Categories
+
Not loaded
+
+
+
+
+
Last Cleanup Evaluation
+
Not loaded
+
+
+
+
+ + + + + + {% if enable_dai_debug %} + + {% endif %} + +
+
+
+
+ Cosmos DB Throughput +
+

+ Monitor RU utilization and automatically adjust shared database or dedicated container throughput. +

+
+ +
+ +
+ + + + + + +
+ +
+ + Automation checks Cosmos throughput on the Metrics Window cadence while enabled and saves the last observed database or container view. SimpleChat can scale throughput up or down at 10,000 RU/s or lower. Above 10,000 RU/s, SimpleChat monitors utilization only; use the Azure portal for capacity changes, which can take 4 to 6 hours. Native Cosmos autoscale conversion is separate from SimpleChat scale-up and scale-down automation. +
+ + + +
+ + + +
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
Metrics Window
+
+
+ +
+ + min +
+
+
+
+ +
+
Scale Up Policy
+
+
+
+ + +
+
+
+ +
+ + % +
+
+
+ +
+ + RU/s +
+
+
+ +
+ + min +
+
+
+ + +
SimpleChat-managed scaling stops at 10,000 RU/s. Use the Azure portal above this limit.
+
+
+
+ + +
+
+
+
+ +
+
Scale Down Policy
+
+
+
+ + +
+
+
+ +
+ + % +
+
+
+ +
+ + RU/s +
+
+
+ +
+ + min +
+
+
+ + +
+
+
+ + +
+
+
+
+ + + +
+
+ + + +
+
+ + + +
+
+ Use this when container throughput should follow one standard automation policy instead of per-container overrides. Global policy also applies the Cosmos autoscale conversion setting above to current and future dedicated-throughput containers. +
+
+
+ +
+ +
+
+
+
Mode
+
Not loaded
+
+
+
+
+
Current RU/s
+
Not loaded
+
+
+
+
+
RU Utilization
+
Not loaded
+
+
+
+
+
Last Checked
+
Not loaded
+
+
+
+ +
+
+ +
+ + + + +
+
+
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + +
+ + Mode + + + + + + + + + + Actions
Refresh to load Cosmos metrics.
+
+
+ + + + + + +
+
+
+ Front Door +
+ +
+

Configure Front Door URL for authentication and redirect flows.

+ +
+ + + +
+ +
+ + + + The base URL of your Front Door or load balancer. The system will automatically generate: +
    +
  • Home redirect: https://your-frontdoor.azurefd.net
  • +
  • OAuth2 redirect: https://your-frontdoor.azurefd.net/getAToken
  • +
+
+
+
+
diff --git a/application/single_app/templates/admin/_panes/search-extract.html b/application/single_app/templates/admin/_panes/search-extract.html new file mode 100644 index 000000000..37dfa5574 --- /dev/null +++ b/application/single_app/templates/admin/_panes/search-extract.html @@ -0,0 +1,1698 @@ +
+ +

+ Configure Azure AI Search, Document Intelligence, and multimedia support settings. +

+ +
+
+
Web Search (Azure AI Foundry Agent)
+ +
+

Enable web search by routing queries through an Azure AI Foundry agent configured by admins.

+
+ + +
+ + + +
+ +
+
+
URL Access
+ Shared Policy +
+

Control direct URL content fetching for pasted chat links, workflow prompts, and Deep Research source review.

+
+ + +
+
+ + +
Required app role value: UrlAccessUser. Assign this role to users or groups in the Enterprise App before enabling the requirement. When enabled, only assigned users can use URL Access in chat or enable it for workflows.
+
+ +
+
+ URL Access uses the same server-side URL protections as Deep Research: non-HTTP(S) URLs, credentialed URLs, literal IP targets, localhost, metadata hosts, unsafe redirects, unsupported content types, and oversized pages are blocked before fetch. +
+
+
+ + +
Hard limit: 100 direct URLs per chat message.
+
+
+ + +
Hard limit: 500 direct URLs per workflow prompt.
+
+
+
+ +
+
+
+ + +
+ + +
+
+
+
Leave blank to allow any public domain that passes safety checks. Deep Research uses this same allow list.
+
+
+
+ + +
+ + +
+
+
+
Blocked domains apply to URL Access and Deep Research source-page review.
+
+
+ +
+
+
+
URL Policy Test
+
Check a URL against the current allowed and blocked domain rules before saving.
+
+ +
+
+
+ +
+
+
Deep Research
+
+ Optional + +
+
+

Plan bounded web searches, inspect source pages, and keep an internal research ledger for evidence coverage. Direct pasted URLs use the shared URL Access controls above.

+
+ + +
+ +
+
+ Server-side protections always block non-HTTP(S) URLs, literal IP URL targets, localhost, metadata hosts, credentialed URLs, excessive redirects, unsupported content types, oversized pages, and requests beyond the configured page budget. DNS hostnames that resolve to private/internal addresses require the internal-host opt-in below. +
+ +
+ + +
Required app role value: DeepResearchUser. Assign this role to users or groups in the Enterprise App before enabling the requirement. When enabled, only assigned users can use Deep Research.
+
+ +
+ + +
Allows DNS hostnames that resolve to private/internal addresses. Literal IP URL targets, localhost, metadata hosts, link-local addresses, and reserved addresses remain blocked.
+
+ +
+
+ + + +
Deep Research runs only when the user selects it for the message.
+
+
+ + +
Hard limit: 10 pages.
+
+
+ + +
Limits initial search-result and direct URL pages so budget remains for child pages.
+
+
+ + +
Direct URLs beyond this cap are recorded as omitted in the ledger.
+
+
+ + +
Includes the original current-message query.
+
+
+ + +
Hard limit: 30 seconds.
+
+
+ + +
Every redirect target is revalidated.
+
+
+ + +
Hard limit: 5 MB.
+
+
+ + +
Depth 2 follows selected links from seed and child pages.
+
+
+ +
+
+ {% set js_runtime = source_review_runtime_capabilities or {} %} +
+ + +
+
Deep Research follows only scored, policy-approved links within the page and depth budgets.
+
+
+
+ + +
+
The selected chat model can propose bounded current-message-only query variants before page review.
+
+
+
+ + +
+
Creates a Markdown chat artifact with search queries, reviewed sources, skipped URLs, and coverage.
+
+
+
+ + +
+
When source pages expose candidate links, the selected chat model can rank those candidates before the server fetches them.
+
+
+
+ + +
+
Requires verified Playwright browser runtime support on the app host.
+
+ {{ js_runtime.message or 'Runtime support has not been checked yet.' }} +
+ {% if not js_runtime.js_rendering_available %} +
Install/enable the Playwright Chromium runtime, then reload Admin Settings to enable this option.
+ {% endif %} + {% if js_runtime.sandbox_disabled %} +
Chromium sandbox is disabled by environment configuration.
+ {% endif %} +
+
+ + +
When JavaScript rendering is enabled, Deep Research can click visible Load More controls until this cap is reached.
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+
+ + + + + + + + +
+
Azure AI Search
+

+ Configure Azure AI Search settings. +

+
+ + + +
+ +
+ +
+
+ + +
+
+ + +
+
+ +
+ + +
+
+
+ + +
+
+ + +
+
+ +
+ + +
+
+
+ + + +
+
+
+ + +
+
Document Intelligence
+

+ Configure Azure Document Intelligence settings. Document Intelligence always powers + Standard extraction and is required for workspaces and chat file uploads. +

+
+ + + +
+ +
+ {% if content_understanding_supported %} +
+ Enhanced extraction uses Azure AI Content Understanding, which returns tables, + page structure, checkbox states, and AI-generated descriptions of figures, charts, and images. + Configure the connection below. +
+ {% else %} +
+ Azure AI Content Understanding is not available in the + {{ azure_environment }} cloud, so Enhanced extraction uses + Document Intelligence Layout here. There is nothing more to configure — + Enhanced still captures tables, page structure, forms, and checkbox states. +
+ {% endif %} + +
+ + +
+ Enhanced captures more document detail for PDFs and images, including tables, page structure, and checked or unchecked marks. It adds latency and has a 6X increase for every 1000 pages when selected. +
+
+ + +
+ Auto samples this many first PDF pages with Document Intelligence Layout. If it detects tables, selection marks, or figures, the full PDF uses Enhanced; otherwise it finishes with Standard. Images use Enhanced in Auto mode. +
+
+
+
Standard: Document Intelligence Read. Fastest and lowest-cost path for plain text PDFs and images.
+
Enhanced: best for tables, section structure, page layout, forms, checkbox states, and figure descriptions. Expect more latency and higher cost.
+
Auto: samples the first pages with Document Intelligence Layout, then uses Enhanced only when the sample shows tables, selection marks, or figures.
+
+
+ +
+ + + +
+
+ Captures equations in PDFs and images as LaTeX rather than approximate OCR text. This is a + billed Document Intelligence add-on that adds per-page cost to every Enhanced + extraction, so it is off by default. It applies to the Layout model only, so it has no effect + while extraction is set to Standard. +
+ + {% if content_understanding_supported %} +
+
+
Azure AI Content Understanding
+ +
+

+ Required for Enhanced extraction. Deployed from a Microsoft Foundry resource. + If this is left blank, Enhanced falls back to Document Intelligence Layout. +

+ +
+ + +
+ Your Microsoft Foundry resource endpoint, without a trailing path. +
+
+ +
+ + +
+ Managed identity requires the Cognitive Services User role on the Foundry resource. +
+
+ +
+ +
+ + +
+
+ +
+
+ + +
Default: {{ content_understanding_api_version_default }}
+
+
+ + +
Default: {{ content_understanding_document_analyzer_default }}
+
+
+ + +
Default: {{ content_understanding_image_analyzer_default }}
+
+
+ + +
+
+ {% endif %} +
+ +
+
Images Inside Office Files
+

+ Neither extraction engine describes figures inside Word and PowerPoint files. When this is on, + embedded images are pulled out of the file, analyzed with whichever engine backs the selected + extraction mode, and indexed as their own citable chunks. This works with Standard extraction + too, using Document Intelligence. +

+
+ + +
+
+
+ + +
Images narrower or shorter than this are skipped as icons or spacers.
+
+
+ + +
Caps per-document cost. Duplicate images are analyzed once.
+
+
+
+ + + + +
+ + + +
+ +
+ +
+
+ + +
+
+ + +
+
+ +
+ + +
+
+
+ + +
+
+ + +
+
+ +
+ + +
+
+
+ + + +
+
+
+ +
+
+
Chunk Sizes
+ Cap: {{ chunk_size_cap }} +
+

Custom chunk sizes apply to new uploads only. Existing documents keep their current chunks.

+
+ Heads up: Overrides are capped at {{ chunk_size_cap }} (2x embedding context window, fallback 16,384). +
+
+ + +
+ +
+ + + {% set chunk_settings = chunk_size_settings or {} %} + {% set chunk_defaults = chunk_size_defaults or {} %} +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
Minimum enforced at 50% of target on merge.
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ + +
Applies to new audio transcripts.
+
+
+ + +
Pages per chunk after extraction.
+
+
+ + +
Slides per chunk after extraction.
+
+
+
+
+ + +
+
+
+
+ +
+
+

+ AI Video Intelligence +

+

Extract insights and make video content searchable and interactive

+
+
+ +
+ +
+ + Make Video Content Fully Searchable: Upload videos to your workspace and automatically extract transcripts, identify speakers, detect topics, and generate searchable timestamps. Your AI can then answer questions about video content, cite specific moments, and provide deep insights. +
+ + +
+
+ + +
+

+ Upload video files (MP4, AVI, MOV, etc.) to workspaces where Azure Video Indexer automatically processes them. + Extract spoken content, identify speakers, detect faces, recognize brands, and generate searchable metadata—perfect for meetings, presentations, training videos, and recorded content. +

+
+ + Full-Text Search + + + Speaker ID + + + Timestamped Citations + + + Multi-Language + +
+
+ + {% set public_video_indexer_endpoint = "https://api.videoindexer.ai" %} + {% set government_video_indexer_endpoint = "https://api.videoindexer.ai.azure.us" %} + {% set configured_video_indexer_endpoint = settings.video_indexer_endpoint or default_video_indexer_endpoint %} + {% if configured_video_indexer_endpoint == government_video_indexer_endpoint %} + {% set video_indexer_cloud = "usgovernment" %} + {% elif configured_video_indexer_endpoint == public_video_indexer_endpoint %} + {% set video_indexer_cloud = "public" %} + {% else %} + {% set video_indexer_cloud = "custom" %} + {% endif %} + {% if azure_environment == "usgovernment" %} + {% set azure_environment_label = "Azure Government" %} + {% elif azure_environment == "custom" %} + {% set azure_environment_label = "Custom Cloud" %} + {% else %} + {% set azure_environment_label = "Azure Public / Commercial" %} + {% endif %} + + +
+
+ Azure Video Indexer Configuration +
+

Connect to your Azure Video Indexer resource for advanced video processing and content extraction.

+ +
+ Identity overview: +
    +
  • Video Indexer can use its own managed identity to reach the linked Storage account during account creation.
  • +
  • SimpleChat authenticates to Video Indexer with the App Service system-assigned managed identity.
  • +
  • The App Service managed identity must have Contributor on the Video Indexer resource.
  • +
  • See Azure Video Indexer documentation for setup details
  • +
+
+ +
+ + +
Choose the endpoint family that matches your deployed cloud. Use Custom only when you need a non-standard Video Indexer endpoint.
+
+ +
+ Current App Service environment: {{ azure_environment_label }}. This selector saves the Video Indexer API endpoint. If you are switching between Azure Public, Azure Government, or a custom cloud, update AZURE_ENVIRONMENT in your App Service configuration as well. +
+ +
+ The selected Video Indexer cloud does not match this app's current AZURE_ENVIRONMENT. Update the App Service configuration if you are moving to a different cloud. +
+ + + +
+ + +
Only required when using a custom cloud or non-standard Video Indexer endpoint.
+
+ +
+ + +
Saved endpoint value used for Video Indexer API calls.
+
+ +
+ + +
The Azure resource group containing your Video Indexer account
+
+ +
+ + +
Your Azure subscription ID
+
+ +
+ + +
The name of your Video Indexer account resource
+
+ +
+ + +
Azure region where your Video Indexer account is deployed (e.g., eastus, westus2, northeurope)
+
+ +
+ + +
Found in the Video Indexer account Overview page in Azure Portal
+
+ +
+ + +
Default for {{ azure_environment_label }}: {{ default_video_indexer_arm_api_version }}
+
+ +
+ + +
+
+
+ + +
+
+
+
+ +
+
+

+ AI Voice Conversations +

+

Transform your AI experience with natural voice interactions

+
+
+ +
+ + +
+
+ + +
+

+ Let users opt in to a bundled sound when an AI response finishes outside their active view. + These local cues do not require Azure Speech Service. +

+
+ +
+ + One Speech resource, three features: Audio file uploads, Voice Input, and Voice Responses all use the same Azure Speech Service section below. Configure the Speech resource once, then turn on whichever speech features you need. +
+ + +
+ {% set audio_runtime = audio_runtime_capabilities or {} %} + {% set audio_supported_extensions = audio_runtime.supported_extensions or [] %} + {% set audio_direct_extensions = audio_runtime.direct_transcription_extensions or [] %} +
+ + +
+

+ Upload audio files (MP3, WAV, M4A, etc.) to workspaces where they're automatically transcribed and indexed. + The AI can then search, analyze, and answer questions about audio content—perfect for meetings, interviews, lectures, and podcasts. +

+
+
+ {{ audio_runtime.message or 'Audio runtime support has not been checked yet.' }} +
+
+ Supported audio upload extensions: {{ audio_supported_extensions|join(', ') }} +
+ {% if audio_direct_extensions %} +
+ Without FFmpeg, source-file fallback is best for: {{ audio_direct_extensions|join(', ') }}. Container builds can include FFmpeg for broader codec support. +
+ {% endif %} +
+
+ + +
+
+ + +
+

+ Talk to your AI instead of typing. Record voice messages up to 90 seconds directly in the chat interface. + Azure Speech Service instantly transcribes your speech with high accuracy, supporting multiple languages and accents. +

+
+ + Hands-Free + + + Accessible + + + Fast Input + +
+
+ + +
+
+ + +
+

+ Hear AI responses read aloud in natural, human-like voices powered by Azure's advanced neural Text-to-Speech. + Each message includes a speaker button—click to listen while multitasking, commuting, or whenever reading isn't convenient. +

+
+ + Natural Voices + + + Multitask-Friendly + + + Multi-Language + +
+
+ + +
+
+ Azure Speech Service Configuration +
+

Configure the shared Azure Speech resource used by audio transcription, voice input, and text-to-speech.

+ +
+ Shared configuration: These settings apply to all enabled speech features. If you use managed identity and also enable Voice Responses, fill in the Speech Resource ID field so the Speech SDK can authenticate synthesis requests. +
+ +
+ + +
Use the resource-specific custom-domain endpoint when selecting Managed Identity.
+
+ +
+ + +
Required for speech recognition locale defaults and for text-to-speech when using Managed Identity.
+
+ +
+
+ Resource ID builder: If the full ARM resource ID is hard to find in the portal, enter the subscription ID, resource group, and Speech resource name below. Simple Chat can build the resource ID for you. +
+ +
+
+ + +
+
+ + +
+
+ + +
If you use a custom-domain Speech endpoint, this is usually the first part of that hostname.
+
+
+ +
+
+ +
Provide Subscription ID, Resource Group, and Speech Resource Name to auto-build the ARM resource ID.
+ + + +
Required for Voice Responses when using Managed Identity. You can paste the full value manually, or let the fields above build it for you.
+
+ +
+ + +
+ +
+ + +
+
+ +
+ + +
+
Required only when Authentication Type is set to Key.
+
+ +
+ Managed identity roles: Start with Cognitive Services Speech User. If fast transcription or other write-style speech operations still return authorization errors, add Cognitive Services Speech Contributor. Managed identity also requires a custom-domain endpoint. +
+ +
+

+ + + Enhanced Citations + + will dramatically improve the citation experience for video and audio files. + +

+
+
diff --git a/application/single_app/templates/admin/_panes/security.html b/application/single_app/templates/admin/_panes/security.html new file mode 100644 index 000000000..40d43e1a9 --- /dev/null +++ b/application/single_app/templates/admin/_panes/security.html @@ -0,0 +1,230 @@ +
+

+ Manage security settings for key vault and other security configurations. +

+
+
+
+ Key Vault +
+ +
+ +

+ Configure Key Vault settings. +

+
+ + + +
+ +
+
+ ⚠️ Warning: Once you enable Key Vault, you should NOT disable it. Disabling Key Vault after enabling WILL cause loss of access to secrets and break application functionality. +
+
+ + + +
+
+ + + +
+ +
+
+ +
+
+
+
+ Secret Expiration Reminders +
+

+ Track SimpleChat-owned Key Vault secrets with expiration dates, in-app reminders, and an admin inventory that maps opaque secret names back to their owner and source. +

+
+ +
+ + + +
+ External automation: SimpleChat emits a queryable Application Insights event named + key_vault_expiration_reminder_triggered whenever a reminder notification is created. + Use an Azure Monitor scheduled query alert with an action group, Logic App, Function, or webhook to send external notifications. Enable the routing email option below if downstream automation needs to send directly to the configured reminder contact. +
traces
+| where customDimensions.sc_event_name == 'key_vault_expiration_reminder_triggered'
+| project timestamp,
+          reminder_id = tostring(customDimensions.sc_event_reminder_id),
+          scope = tostring(customDimensions.sc_event_scope),
+          source_type = tostring(customDimensions.sc_event_source_type),
+          days_until_expiry = toint(customDimensions.sc_event_days_until_expiry),
+          expires_on = tostring(customDimensions.sc_event_expires_on),
+          contact_email = tostring(customDimensions.sc_event_contact_email)
+ The contact_email column is populated only when the opt-in below is enabled. For workspace-based Log Analytics queries, use the equivalent Application Insights traces table and dimensions/properties fields available in that workspace. +
+ +
+ + +
+ +
+
+
+ + +
+
+ + +
+
+ + +
Comma-separated roles for global-scope reminder notifications.
+
+
+ + +
+
+
+ + +
+
+
+
+ + +
+ Default off. Enable only when Azure Monitor, Logic Apps, Functions, or webhook automation needs the email address to route notifications directly. +
+
+
+
+
+ +
+
+ + + +
+ + + +
+ + + + + + + + + + + + + + + + + + +
ExpiresScopeSourceFieldContactStatusReminder IDSecret
Refresh inventory to load tracked secrets.
+
+
+
+
+
+
diff --git a/application/single_app/templates/admin/_panes/send-feedback.html b/application/single_app/templates/admin/_panes/send-feedback.html new file mode 100644 index 000000000..1d9cdb365 --- /dev/null +++ b/application/single_app/templates/admin/_panes/send-feedback.html @@ -0,0 +1,72 @@ +
+

+ Prepare a prefilled email draft for bug reports or feature requests. This utility is separate from saved settings and records each submission intent in the activity log before opening your local email client. +

+ + +
+
+
+
+ Report a Bug +
+

Something isn’t working as expected.

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + +
+
+
+ +
+
+
+ Request a Feature +
+

Suggest an improvement or new capability.

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + +
+
+
+
+
diff --git a/application/single_app/templates/admin/_panes/workspace-identities.html b/application/single_app/templates/admin/_panes/workspace-identities.html new file mode 100644 index 000000000..50df99098 --- /dev/null +++ b/application/single_app/templates/admin/_panes/workspace-identities.html @@ -0,0 +1,12 @@ +
+
+
+
+
diff --git a/application/single_app/templates/admin/_panes/workspaces.html b/application/single_app/templates/admin/_panes/workspaces.html new file mode 100644 index 000000000..3bf8d4aa9 --- /dev/null +++ b/application/single_app/templates/admin/_panes/workspaces.html @@ -0,0 +1,1138 @@ +
+

+ Configure workspace settings like personal/group access, multimedia support, metadata, and document classification. +

+ + +
+
+ Personal Workspaces +
+

+ Turn this on to allow access and management of your personal workspace. +

+
+ + + +
+
+ +
+
+ Workflow +
+

+ Control personal and group workflow authoring and execution for users. +

+
+ + + +
+
+ + + +
+

+ Required app role value: WorkflowUser. Assign this role to users or groups in the Enterprise App before enabling the requirement. +

+ +
+ + +
+ Maximum automatic tool or action calls an agent can make during one workflow run. Default is 60; increase for large document sets. +
+ +
+ +
+ + +
+ Maximum ordered instruction tasks users can add to one workflow. Default is 50; supported range is 1-100. +
+
+ +
+ +
+ + + +
+
+ + + +
+
+ + No groups assigned. +
+ +

+ Group workflow authoring is available to group Owners and Admins by default. Enable the owner-only setting below to limit group agent, action, and workflow management to Owners. +

+
+ +
+
+ File Downloads +
+

+ Control when users can download original workspace document files from personal, group, and public workspaces. +

+
+ + + +
+
+
+ + + +
+
+ + + +
+
+ + No groups assigned. +
+ +
+
+ + + +
+
+ + + +
+
+ + No public workspaces assigned. +
+ +
+ + + + + + + + +
+
+ Group Workspaces +
+

+ Turn this on to allow access and management of group workspaces, as well as group collaboration features. +

+
+ + + +
+ + +
+
+ + +
+ + + +
+

+ When enabled, no users will be able to create new groups, regardless of app role assignment. This global setting overrides the Require CreateGroups App Role setting below. +

+ +
+ + + +
+

+ Required app role value: CreateGroups. Assign this role to users or groups in the Enterprise App before enabling the requirement. If disabled, any authenticated user can create groups while group workspaces and group creation are enabled. +

+ +
+ + +
+ + + +
+

+ If enabled, only the group Owner can create, edit, and delete group agents, group actions, and group workflows. Admins and other roles will only be able to view them. +

+
+ +
+ +
+
+ Public Workspaces +
+

+ Turn this on to enable public workspaces that are visible to everyone in the organization. +

+
+ + + +
+ +
+ + +
+ Optional. End users will see this label instead of Public Workspace. Admin settings and internal references continue to use Public Workspace. +
+
+ +
+
+
+ + + +
+

+ Required app role value: CreatePublicWorkspaces. Assign this role to users or groups in the Enterprise App before enabling the requirement. If disabled, any authenticated user can create public workspaces while public workspaces are enabled. +

+
+ +
+ +
+
+ File Sharing +
+

+ Turn this on to enable file sharing capabilities between users and workspaces. +

+
+ + + +
+
+ +
+
+ Chat File Uploads +
+

+ Control whether users can upload files directly into chat conversations. +

+
+ + + +
+ +
+
+ Conversation Contents Drawer +
+

+ Let users navigate long conversations from a list of their prompts. +

+
+ + + +
+

+ When enabled, users can turn the drawer off for their own account from Profile settings. +

+
+
+ + + +
+

+ Required app role value: ChatFileUploadUser. Assign this role to users or groups in the Enterprise App before enabling the requirement. Existing chat attachments remain visible; this only controls new uploads. +

+
+ +
+
+ Metadata Extraction +
+

+ Enable this to automatically parse and store file metadata for advanced indexing and search. +

+
+ + + +
+ +
+ + + +
+ Uses Global Endpoints when multi-endpoint model management is enabled; otherwise uses the legacy GPT/APIM deployment settings. +
+
+
+ + +
+
+ Multi-Modal Vision Analysis +
+

+ Enable AI-powered vision analysis for images uploaded to chat or workspace. When enabled alongside Document Intelligence OCR, images will receive both text extraction (OCR) and semantic understanding (vision AI). +

+ +
+ How it works: +
    +
  • Document Intelligence: Extracts text from images (OCR)
  • +
  • Vision Model: Provides semantic analysis, object detection, and contextual understanding
  • +
  • Both analyses are combined and available in citations when Enhanced Citations is enabled
  • +
+
+ +
+ + + +
+ +
+ + +
Select a GPT model with vision capabilities (for example, gpt-4o or supported GPT 5 and later models). Only vision-capable models are shown.
+ + +
+
+
+ + + +
+
+ Document Classification +
+

+ Enable this feature to allow users to classify documents uploaded to their workspaces using predefined categories. +

+
+ + + +
+ + +
+
Classification Categories
+

Define the labels and corresponding colors for document classification.

+ + + + + + + + + + + +
LabelColorActions
+ + + +
+
+ + +
+
+ Retention Policy +
+

Automatically delete aged conversations and documents based on configurable retention periods. Users, group owners, and public workspace admins can set their own retention policies.

+ + + + +
+
+
+ + + +
+
+
+
+ + + +
+
+
+
+ + + +
+
+
+ + +
+
Default Retention Policies
+

Set organization-wide default retention periods for each workspace type. Users can override these defaults with their own preferences. Setting a default here means new users/workspaces will start with this retention period.

+ + +
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + Force Push: Override all user/group/workspace custom retention policies with the organization defaults above. Users will then use the organization default until they set their own preference. +
+ +
+
+
+ + +
+ + + Retention policy will run once daily at this hour (UTC timezone). +
+ + +
+
+ +
+ {% if settings.retention_policy_last_run %} + {{ settings.retention_policy_last_run }} + {% else %} + Never run + {% endif %} +
+
+
+ +
+ {% if settings.retention_policy_next_run %} + {{ settings.retention_policy_next_run }} + {% else %} + Not scheduled + {% endif %} +
+
+
+ + +
+ + + Trigger retention policy execution immediately for selected workspace types, bypassing the scheduled time. + +
+ + +
+ + +
+
+ Workspace Scope Lock +
+

+ Control whether users can unlock workspace scope in chat conversations. When scope is locked, conversations are restricted to the workspaces that produced search results, preventing accidental cross-contamination with other data sources. +

+
+ + + +
+
+ + +
+
+ User Agreement +
+

+ Configure a user agreement that users must accept before uploading files. + Supports Markdown formatting. +

+ + +
+ + + +
+ + +
+
+ + +
+ +

Select where the user agreement should be shown (at least one required):

+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+ +
+ + +
+ + +
+ 0 / 200 words + +
+
+ + +
+ + + +
+ + +
+ +
+
+
+
diff --git a/application/single_app/templates/admin_settings.html b/application/single_app/templates/admin_settings.html index 947f7f1ba..56e65ad9b 100644 --- a/application/single_app/templates/admin_settings.html +++ b/application/single_app/templates/admin_settings.html @@ -1052,11843 +1052,52 @@

12. Enhanced Citations and Image Generation

{% endif %}
-
-

- Manage security settings for key vault and other security configurations. -

-
-
-
- Key Vault -
- -
- -

- Configure Key Vault settings. -

-
- - - -
- -
-
- ⚠️ Warning: Once you enable Key Vault, you should NOT disable it. Disabling Key Vault after enabling WILL cause loss of access to secrets and break application functionality. -
-
- - - -
-
- - - -
- -
-
- -
-
-
-
- Secret Expiration Reminders -
-

- Track SimpleChat-owned Key Vault secrets with expiration dates, in-app reminders, and an admin inventory that maps opaque secret names back to their owner and source. -

-
- -
- - - -
- External automation: SimpleChat emits a queryable Application Insights event named - key_vault_expiration_reminder_triggered whenever a reminder notification is created. - Use an Azure Monitor scheduled query alert with an action group, Logic App, Function, or webhook to send external notifications. Enable the routing email option below if downstream automation needs to send directly to the configured reminder contact. -
traces
-| where customDimensions.sc_event_name == 'key_vault_expiration_reminder_triggered'
-| project timestamp,
-          reminder_id = tostring(customDimensions.sc_event_reminder_id),
-          scope = tostring(customDimensions.sc_event_scope),
-          source_type = tostring(customDimensions.sc_event_source_type),
-          days_until_expiry = toint(customDimensions.sc_event_days_until_expiry),
-          expires_on = tostring(customDimensions.sc_event_expires_on),
-          contact_email = tostring(customDimensions.sc_event_contact_email)
- The contact_email column is populated only when the opt-in below is enabled. For workspace-based Log Analytics queries, use the equivalent Application Insights traces table and dimensions/properties fields available in that workspace. -
- -
- - -
- -
-
-
- - -
-
- - -
-
- - -
Comma-separated roles for global-scope reminder notifications.
-
-
- - -
-
-
- - -
-
-
-
- - -
- Default off. Enable only when Azure Monitor, Logic Apps, Functions, or webhook automation needs the email address to route notifications directly. -
-
-
-
-
- -
-
- - - -
- - - -
- - - - - - - - - - - - - - - - - - -
ExpiresScopeSourceFieldContactStatusReminder IDSecret
Refresh inventory to load tracked secrets.
-
-
-
-
-
-
- - -
- -

- Configure AI agents and actions for enhanced functionality. Agents provide AI-driven task automation while Actions extend functionality with custom tools and integrations. -

- - - - - - {% set analyze_capability = settings.document_action_capabilities.analyze %} - {% set comparison_capability = settings.document_action_capabilities.comparison %} -
-
-
-
Document Action Capabilities
-

Configure the document actions shown in the Action dropdown in Chat and Workflow. These controls live in the Agents and Actions tab, but remain separate from the global agent and custom action cards below.

-
- Chat: 2-300 | Workflow: 2-1000 -
- -
-
-
-
- - - -
-
-
- - {{ analyze_capability.chat_max_documents }} -
-
-
- -
-
- -
-
-
- 2 - 300 -
-
-
-
- - {{ analyze_capability.workflow_max_documents }} -
-
-
- -
-
- -
-
-
- 2 - 1000 -
-
-
-
-
-
-
- - - -
-
-
- - {{ comparison_capability.chat_max_documents }} -
-
-
- -
-
- -
-
-
- 2 - 300 -
-
-
-
- - {{ comparison_capability.workflow_max_documents }} -
-
-
- -
-
- -
-
-
- 2 - 1000 -
-
-
-
-
-
- - -
-
- Agents Configuration -
-

- Configure AI agents powered by Semantic Kernel for task automation and orchestration. -

-
-
- Agents Page Customization -
-

Customize the public Agents page hero, color treatment, and optional guidance message.

-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
Used when two tone gradient is selected.
-
-
- - -
Shown below the Agents page hero. Use this for contact details, request guidance, or governance reminders.
-
-
-
- - -
-
When disabled, the Agents tab details popup hides instructions and the catalog API response omits them.
-
-
-
-
Promoted Popular Agents
-

Highlight selected agents in the Popular tab even before they have usage counts. Users only see promoted agents that are already visible to them.

- -
-
- - -
-
- - -
-
-
-
- - -
-
When enabled, promoted agents show the configured tag in the Popular tab.
-
-
-
-
-
- - -
-
- -
-
-
- - - - - - - - - -
AgentShow InActions
-
- - -
-
-
-
-
- - - - - -
- -
- {% if not settings.enable_semantic_kernel %} -
- Agents UI is not available while agents are disabled. -
- {% else %} -
-
- - - -
- {% if settings.per_user_semantic_kernel %} - -
-
Workspace Feature Toggles
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - -
-
- - - -
-
- - - {% endif %} - -
-

Agent Orchestration Settings

-
-
-

Configure how the chat system orchestrates agents (single or multi-agent group chat).

-
- - -
- - -
-
-

Global Agents

-
- - -
-
-

Disable a global agent to keep it saved for admins while hiding it from runtime selection until it is re-enabled.

-
- {% if settings.orchestration_type == "default_agent" %} - - {% else %} - - {% endif %} - - -
- - - - - - - {% if settings.orchestration_type == "default_agent" %} - - {% else %} - - {% endif %} - - - - -
NameDisplay NameDescriptionSelected AgentOrchestrator AgentActions
-
- {% endif %} -
- - {% if settings.enable_agent_template_gallery %} -
-
-
-
- - - -
-
-
-
- - - -
-
-
-
-
-
- Agent Template Approvals -
-

Template submissions are now reviewed from the shared approvals page.

-
- - Open Approvals Queue - -
-
- {% endif %} - - -
-
- Global Actions Configuration -
-

- Configure custom actions and tools to extend functionality with integrations and specialized capabilities. -

- - {% if not settings.enable_semantic_kernel %} -
- Actions are not available while agents are disabled. Enable agents above to configure actions. -
- {% else %} -
-
-
Global Actions
- -
-

Disable a global action to keep the configuration without exposing it to runtime action loading until it is re-enabled.

- {% if settings.per_user_semantic_kernel %} - -
-
Workspace Action Feature Toggles
-
- - - - -
-
- - - - -
-
- {% endif %} - - -
-
Core Action Toggles
-
- - - -
-
- - - -
-
- - - -
-
- - - -
-
- - - -
-
- - - -
-
- - - -
-
-
- Tabular Processing Action - -
- Automatically enabled when Enhanced Citations is enabled -
-
- - - - - - - - - -
Display NameDescriptionActions
-
- {% endif %} -
- {% if mcp_ui_enabled %} -
-
-
-
- Inbound MCP Server -
-

- Configure the governed SimpleChat MCP endpoint used by external MCP clients. Personal tools use delegated user tokens by default; tool access remains deny-by-default until authentication, source, client, and governance policy all allow the request. -

-
-
- Preview - -
-
- - - -
-
-
-
Runtime Gate
-
- - - -
-
- - -
Default: DelegatedMcpServerAccess. VS Code and other user clients must present this delegated scope.
-
-
- - -
Default: InboundMCPUserAccess. Governance determines which users/groups can use tools after this Entra role and delegated scope pass.
-
-
- - -
Default: InboundMCPAppAccess. Reserved for future app-only MCP tools and still governed separately.
-
-
-
- -
-
-
Endpoint Contract
-
-
MCP endpoint
-
{{ inbound_mcp_resource_path }}
-
Protected resource metadata
-
{{ inbound_mcp_prm_path }}
-
Transport
-
Streamable HTTP JSON-RPC
-
Current tool surface
-
Personal delegated tools only.
-
Tool identity
-
Delegated user token required for personal data.
-
- -
-
- -
-
-
-
-
Request Size & Throttling
-
Protect the inbound MCP endpoint from oversized payloads and noisy clients. Rate limits are enforced per caller and tool category across app instances.
-
-
- - -
-
-
-
- - -
Default: 65536. Range: 1 KB to 1 MB.
-
-
- - -
Default: 60. Applies to each throttle category.
-
-
- - -
Default: 120.
-
-
- - -
Default: 30.
-
-
- - -
Default: 10.
-
-
- -
-
- -
- - - -
- -
-
-
-
-
Allowed client app IDs
-
Required allowlist. Empty means no MCP clients can connect.
-
- -
-
- - - - - - - - - -
Client app IDDescriptionActions
-
-
-
-
- -
-
-
- - -
-
- Off allows only the configured SimpleChat tenant. Turning this on auto-includes the SimpleChat tenant and lets admins add more tenants. -
-
-
-
Allowed tenant IDs
- -
-
- - - - - - - - - -
Tenant IDDescriptionActions
-
-
-
-
-
- -
-
-
- - -
-
- Default on accepts any source signal at the runtime allowlist layer. Turn off to require explicit source values here. In both modes, admins must still create an inbound MCP source governance policy before tools are returned. -
-
- - -
Default: X-SimpleChat-MCP-Source.
-
- - - -
-
-
Allowed source IDs
- -
-
- - - - - - - - - -
Source valueDescriptionActions
-
-
-
-
-
-
-
- - - - {% endif %} -
- - - -
-
-

- Configure governance enforcement for endpoint, agent, and action creation or usage. Governance is disabled by default and only enforced when toggled on. -

- -
- -
-
- Governance Feature Toggles -
-

Turn on governance checks for each feature area. Save with the main Settings button to persist these toggles.

- -
-
-
Personal Scope
-
- - -
-
- - -
-
- - -
-
-
-
Group Scope
-
- - -
-
- - -
-
- - -
-
-
-
Global Scope
-
- - - Always On -
-
- - -
-
- - -
-
-
-
- -
-
- MCP Action Destination Governance -
-

- Restrict outbound remote MCP servers by destination pattern and scope. Destination policies reuse delegated item policies, so admins can allow all users, specific users, or workspace groups for each approved destination. -

-
-
-
- - -
-
- - -
-
- Save with the main Settings button. Unsafe literal-IP blocking can be enabled even before the allowlist is enforced. -
-
-
-
Supported destination patterns
-
    -
  • * for any remote MCP destination after identity/auth checks.
  • -
  • preconfiguration:microsoft_learn or preset:generic.
  • -
  • *.contoso.com, https://example.com/mcp*, or transport:streamable-http.
  • -
  • group:<group-id>::preconfiguration:github for one group-specific override.
  • -
-
-
-
-
- - -
-
- - -
-
- - -
-
-
-
-
- -
-
- Inbound MCP Source Governance -
-

- Control which delegated users and groups can use SimpleChat as an inbound MCP server through source-scoped policies. -

- -
-
-
Policy required for inbound MCP
-
    -
  • inbound_mcp_source with delegated item *: allows selected users or groups to use any accepted source ID.
  • -
  • inbound_mcp_source with a configured source ID: allows selected users or groups to use only requests with that source value.
  • -
-
- Client, tenant, delegated scope, and Entra role checks remain in Inbound MCP configuration. The source ID comes from the configured header when present and is advisory unless a trusted gateway sets or validates it. -
-
-
-
-
- - -
-
-
-
-
- -
-
-
Feature Policies
- -
-

Set allow-all, explicit allow lists, and block lists for each governed feature. Block lists override allow settings.

-
- - - - - - - - - - -
FeatureAllow AllAllow ListBlock List
-
-
- -
-
-
Delegated Item Policies
-
- - -
-
-

Manage delegated governance for configured global resources and action type entitlements that admins assign to specific users or groups.

- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- -
- - - - - - - - - - - -
PolicyEntity TypeItemAllow AllActions
-
- -
-
-
- - -
-
-
- - -
- - -
-

- Configure logging settings for monitoring, debugging, and auditing purposes. These settings control various types of logging throughout the application including application insights, debug messages, and file processing events. -

- -
-
- Application Insights Logging -
-

Enable global logging to Application Insights for all agents and orchestration events.

-
- - - -
- -
- -
-
- Debug Logging -
-

Control debug print statements across the application for development and troubleshooting.

-
- - - -
- - -
-
- - - -
- -
-
- - -
-
- - -
-
- -
- {% if settings.debug_logging_turnoff_time %} - {% if settings.debug_logging_turnoff_time is string %} - {{ settings.debug_logging_turnoff_time }} - {% else %} - {{ settings.debug_logging_turnoff_time.strftime('%Y-%m-%d %H:%M:%S') }} - {% endif %} - {% else %} - Will be calculated when saved - {% endif %} -
-
-
-
- - -
- -
-
- File Process Logging -
-

Enable logging of file processing events for debugging and auditing purposes. Logs are stored in the file_processing container in Cosmos DB.

-
- - - -
- - -
-
- - - -
- -
-
- - -
-
- - -
-
- -
- {% if settings.file_processing_logs_turnoff_time %} - {% if settings.file_processing_logs_turnoff_time is string %} - {{ settings.file_processing_logs_turnoff_time }} - {% else %} - {{ settings.file_processing_logs_turnoff_time.strftime('%Y-%m-%d %H:%M:%S') }} - {% endif %} - {% else %} - Will be calculated when saved - {% endif %} -
-
-
-
- -
-
-
Delete stored logs
-

- Permanently remove file processing logs from Cosmos DB. One month is treated as 30 days. -

-
-
- - -
- Enter a whole number greater than zero. -
-
-
- - -
-
- - -
-
-
-
-
- -
- -

- Configure general application settings, including the application's title, logo, and landing page text. -

-
-
- Branding -
-

- Configure your application's title, logo, and branding elements. -

-
- - -
-
Logo Settings
-
- - - -
-
- - - -
-
-
- - {{ settings.landing_page_logo_scale_percent | default(100) }}% -
- -
- 50% - 500% -
- - Adjust the logo size on the home page only. This does not change the logo size in the top or sidebar navigation. - -
-
- - - This logo will be displayed in light mode and stored at up to 500px tall so the main page can render it sharply without keeping oversized assets in settings. -
-
- - - This logo will be displayed in dark mode. If not provided, the light mode logo will be used in both themes. Dark logos are also stored at up to 500px tall. -
-
- - - Recommended: 16x16 or 32x32 pixel png or jpg or ico. -
-
- -
-
- Home Page Text -
-

Configure the text content displayed on your application's home page using Markdown formatting.

- -
- - - Choose how the landing page markdown is aligned on the home page. -
- -
- - - -
- - - - -
- -
-
- Appearance -
-

Configure the app appearance and theme settings.

- -
- -
- - - -
-

Users can still toggle dark mode individually from the navigation bar.

-
- -
- -
- - - -
-

Users can still toggle the left navigation sidebar individually from the header.

-
-
- -
-
-
- Health Check -
- -
-

Configure health check endpoint for external monitoring systems.

- -
-
-
-
-
-
Authenticated Endpoint
-

Use when your monitoring system can reach a protected route and you want the endpoint governed by the normal app access boundary.

-
- Protected -
-
- - - -
-

Best for internal monitors or diagnostics tooling that already authenticates to the application.

-
-
-
-
-
-
-
Unauthenticated Endpoint
-

Use when a platform health probe cannot sign in and only needs a lightweight availability response.

-
- No Auth -
-
- - - -
- -
-
-
-
- -
-
-
- API Documentation -
-
- - - Open Swagger UI - -
-
-

Configure automatic OpenAPI/Swagger documentation for API endpoints.

- -
- - - -
-

- Provides interactive API documentation, endpoint testing, and schema validation. - Useful for developers, API integration, and system troubleshooting. -

-
- -
-
- Classification Banner -
-
- - - -
-
- - -
-
- - -
-
- - -
-
- - {{ settings.classification_banner_text or 'Banner Preview' }} - -
-
- -
-
- Chat AI Notice -
-

- Display an administrator-provided reminder directly below the chat input, such as guidance that responses are AI-generated and should be reviewed. -

-
- - -
-
-
- - -
Plain text only. Line breaks are preserved.
-
-
- - -
Changing the notice text or display behavior creates a new message version and shows it again.
-
-
-
- -
-
- Terms of Use -
-

- Require users to accept the Terms of Use before using SimpleChat. Standard Microsoft sign-in users see it before authentication; Teams SSO and other passive sign-in flows are gated immediately after the app session is created. -

- -
- - - -
- -
-
- - -
-
- - - Changing the title, message, or frequency creates a new terms version that users must accept again. -
-
- -
- - - Plain text is shown to users with line breaks preserved. -
- -
- - - Use a local path such as / or an admin-approved HTTP(S) URL. Signed-in users are locally logged out before this redirect. -
- -
-
- - -
-
- - -
-
- - -
- -
-
- Support -
-
- - - -
-

When enabled, users can access Support destinations like Send Feedback and Latest Features directly from navigation.

- -
-
- - - This name will appear in user navigation as the Support menu title. -
- -
- - - -
- -
- - - User Send Feedback drafts will be addressed to this internal email address. -
- -
- - - -
- -
-
User-Facing Latest Features
-

Choose which recent features are shared on the user Latest Features page. Deployment and Redis start unchecked because they are mainly admin-facing rollout and infrastructure topics.

-
- - - - When enabled, user-facing Latest Features cards can show public guide buttons in addition to the direct in-app shortcuts. -
- {% for release_group in support_latest_feature_release_groups %} - {% set checklist_collapse_id = release_group.collapse_id ~ 'Checklist' %} -
-
-
-
-
{{ release_group.label }}{% if release_group.release_version %}v{{ release_group.release_version }}{% endif %}
-

{{ release_group.description }}

-
- {% if not release_group.default_expanded %} - - {% endif %} -
-
-
- {% for feature in release_group.features %} -
-
- - -
-
- {% endfor %} -
-
-
-
- {% endfor %} -
-
-
- - - -
-
- System Settings -
-

- System-level settings that control application behavior, including file size limits, conversation history, - and default prompts. -

-
- - -
-
- - -
-
- - - -
-
-
- - - Users are logged out locally after this many minutes of inactivity. Minimum value: 10 minutes. -
-
- - - Show the warning modal after this many minutes of inactivity. Set this equal to the logout timeout to disable the warning dialog window. -
-
- - - Custom text shown at the top of the idle warning dialog. -
-
-
- - -
-
- - Shown to signed-in users who lack the required roles. Use Enter for line breaks. - -
-
-
- -
-

- Review the main capabilities introduced in v{{ config['VERSION'] }} and use this page as an admin-facing overview for what to highlight with your teams. -

- - - {% for release_group in admin_latest_feature_release_groups %} - {% if release_group.id == 'current_release' %} -
-
-
{{ release_group.label }}
-

{{ release_group.description }}

-
- {% if release_group.release_version %} - v{{ release_group.release_version }} - {% endif %} -
- - {% for feature in release_group.features %} - {% set feature_card_id = 'latest-features-' ~ feature.id|replace('_', '-') ~ '-card' %} - {% set feature_collapse_id = feature_card_id ~ '-details' %} -
-
-
-
{{ feature.title }}
-

{{ feature.summary }}

-
- -
-
-
- {% if feature.details %} -

{{ feature.details }}

- {% endif %} - {% if feature.why %} - - {% endif %} - {% if feature.guidance %} -
{% if feature.images %}Screenshot and rollout notes{% else %}Rollout notes{% endif %}
-
    - {% for tip in feature.guidance %} -
  • {{ tip }}
  • - {% endfor %} -
- {% endif %} - {% if feature.images %} - - {% endif %} - {% if feature.actions %} -
- {% for action in feature.actions %} - - {{ action.label }} - - {% endfor %} -
- {% endif %} -
-
-
- {% endfor %} - {% else %} - {% set release_card_id = 'latest-features-' ~ release_group.id|replace('_', '-') ~ '-card' %} - {% set release_collapse_id = release_group.collapse_id %} -
-
-
-
{{ release_group.label }}
-

{{ release_group.description }}

-
- -
-
-
- {% if release_group.release_version %} - - {% endif %} -
- {% for feature in release_group.features %} -
-
-
-
-
{{ feature.title }}
-

{{ feature.summary }}

-
-

{{ feature.details }}

- {% if feature.guidance %} -
    - {% for tip in feature.guidance %} -
  • {{ tip }}
  • - {% endfor %} -
- {% endif %} - {% if feature.images %} - - {% endif %} - {% if feature.actions %} -
- {% for action in feature.actions %} - - {{ action.label }} - - {% endfor %} -
- {% endif %} -
-
-
- {% endfor %} -
-
-
-
- {% endif %} - {% endfor %} - - {% if false %} -
-
-
-
- Registered / Unregistered Badge -
-

The badge next to the Admin Settings version number shows whether this admin instance is registered for latest release and community call notifications.

-
- -
-
-
- -

Use this badge as the quick status check for whether your environment is already registered to receive SimpleChat release updates.

-
    -
  • Unregistered means this environment has not saved release notification registration details yet.
  • -
  • Registered means the environment already has saved contact details for release and community call notifications.
  • -
  • Clicking the badge opens the registration modal where admins can add or edit the name, email, and organization for this instance.
  • -
  • Submitting the form opens a prefilled email draft to simplechat@microsoft.com and stores the registration details back in Admin Settings.
  • -
  • The badge lives beside the version number at the top of Admin Settings so admins can confirm status without hunting through tabs.
  • -
-
-
-
- -
-
-
-
- Performance Improvements for Apps running Native Python (not containers) -
-

Native Python App Service deployments should use Gunicorn as the startup command. Container deployments already do this for you.

-
- -
-
-
-

If you run the native Python App Service deployment instead of the container deployment, set the App Service Startup command to the following:

-
python -m gunicorn -c gunicorn.conf.py app:app
-

Use container deployments when possible if you want the runtime defaults and startup behavior handled by the shipped image.

- - -
-
-
- -
-
-
-
- Guided Tutorials -
-

New guided walkthroughs help users learn the live chat and personal workspace interfaces without leaving the app.

-
- -
-
-
-

Admins can use these walkthroughs to accelerate onboarding for new users and reduce orientation time across the primary product surfaces.

-
    -
  • Guided Chat Tutorial covers the active chat experience, message actions, search flows, citations, thoughts, and export-related actions.
  • -
  • Personal Workspace Guided Tutorial walks through uploads, search, filters, list and grid views, prompts, agents, actions, and tags.
  • -
- -
-
-
- -
-
-
-
- Background Chat Completion -
-

Personal chats can continue finishing after a user leaves the chat page, and completed responses surface as notifications with unread indicators.

-
- -
-
-
-

This makes long-running responses more reliable for users who navigate into other areas of the app before generation finishes.

-
    -
  • Completion notifications deep-link back into the finished conversation.
  • -
  • Unread markers remain visible until the response is actually opened.
  • -
  • Personal, workspace, and navigation flows now behave more consistently while chat completion continues in the background.
  • -
- -
-
-
- -
-
-
-
- Multi-Endpoint GPT Selection -
-

Admins can expose multiple GPT choices across Azure OpenAI and Azure AI Foundry, set a saved default fallback model, and now review agent rebinding in a modal before applying changes.

-
- -
-
-
-

This is the high-value admin control for teams that want broader GPT choice without locking every workflow to a single deployment.

-
    -
  • Configure multiple model endpoints and choose which GPT deployments are available to users.
  • -
  • Set a default fallback model so summarization, agent requests, and other background flows still resolve a usable GPT when an explicit selection is missing.
  • -
  • Use the admin-only Agent Default Model Review modal to inspect inherited agents, search and filter large agent sets, and explicitly choose which review rows should be overridden to the saved default.
  • -
- - - -
-
-
- -
-
-
-
- Tabular Data Analysis -
-

Enhanced citations now unlock a more consistent tabular analysis experience for normal chat and workspace-backed files, not just dedicated agent scenarios.

-
- -
-
-
-

This is the highest-impact settings area to mirror here because it combines storage-backed citations, tabular previews, and consistent behavior across workspace and chat uploads.

-
    -
  • Tabular files can be analyzed with the same core approach whether they come from chat uploads or any workspace container.
  • -
  • Enhanced citations provide the blob-backed dependency for richer tabular processing and citation transparency.
  • -
  • Admins can right-size tabular preview limits based on the memory profile of their runtime.
  • -
- -
- - -
- -
-
-
All filetypes
-
- - -
- -
- -
- - -
-
- -
- -
- - -
-
-
- -
-
Tabular Preview Limits
-
- - - - Mirror of the Citations setting. Larger values support bigger previews but increase runtime memory pressure. - -
-
-
- - -
-
-
- -
-
-
-
- Citation Improvements -
-

Conversation history citation replay and citation amplification keep more grounded evidence available across follow-up turns without making the chat history unwieldy.

-
- -
-
-
-

These improvements make follow-up questions more reliable because the system can reuse prior grounded evidence instead of relying only on the assistant's last prose response.

-
    -
  • Conversation history citations carry forward prior citation summaries so later prompts can reuse earlier grounded results.
  • -
  • Citation amplification keeps richer supporting payloads available for inspection and export while keeping prompt history compact.
  • -
  • This is especially useful for larger or tabular outputs where users often ask multiple follow-up questions against the same evidence chain.
  • -
- - -
-
-
- -
-
-
-
- Document Revisioning and Management -
-

Uploading a document with the same name now creates a new revision instead of overwriting the previous file, while older versions remain available for traceability and later analysis.

-
- -
-
-
-

This improves both document management and citation integrity for long-lived conversations that span multiple document updates.

-
    -
  • Same-name uploads create a revision family with an incremented current version instead of destroying prior history.
  • -
  • Previous versions are retained for analysis or comparison, while workspace and search views focus on the current revision by default.
  • -
  • Older conversations can still resolve citations against the correct historical document version.
  • -
  • Updated revisions inherit document classifications and tags so admins do not have to reapply metadata after every refresh.
  • -
- -
-
-
- -
-
-
-
- Conversation Summaries and Export -
-

Conversation export now includes persistent summaries, PDF output, and easier per-message reuse and sharing actions.

-
- -
-
-
-
    -
  • Conversation details now surface a summary card with generate and regenerate actions.
  • -
  • Export supports PDF alongside existing export options.
  • -
  • Per-message actions support Markdown export, Word export, open in email, and use as prompt.
  • -
-
-
- -
-
- -
-
-
-
-
- -
-
-
-
- Agent and Action Operations -
-

Recent updates improve manageability for agents and actions with richer views, easier SQL connectivity checks, and better operational tracking.

-
- -
-
-
-
    -
  • List and grid view toggles give admins and users a better way to browse agents and actions.
  • -
  • SQL actions now include an inline Test Connection button before save.
  • -
  • User tracking and activity logging improve auditability for agent and action changes.
  • -
- -
-
-
- -
-
-
-
- AI Transparency -
-

Processing Thoughts provide a clearer view into model activity, duration, and execution flow while responses are being generated.

-
- -
-
-
-

Admins can enable this once and let users see more of the model pipeline, including sending, generating, and responded states with clearer timing information.

- -
- - -
- -
-
-
- -
-
-
-
- Fact Memory -
-

Users can now save Instructions and Facts on their profile so the assistant can apply durable preferences every time and recall relevant personal context only when needed.

-
- -
-
-
-

Fact Memory gives users a compact profile experience for managing two distinct memory types: always-on Instructions and relevance-based Facts.

-
    -
  • Instructions act like stable user preferences and are applied on every prompt unless the current message overrides them.
  • -
  • Facts are recalled only when relevant and now appear as dedicated thought and citation entries in chat.
  • -
  • The profile page includes a compact add flow plus a popup manager for search, paging, edit, retag, and delete actions.
  • -
  • Admins can choose whether this announcement is visible on the user-facing Latest Features page from General > User-Facing Latest Features.
  • -
- - -
-
-
- -
-
-
-
- Redis and Key Vault -
-

Redis configuration now supports using a Key Vault secret name instead of storing the access key directly in settings.

-
- -
-
-
-

This is useful for teams standardizing on Key Vault-backed secret storage while still using Redis for cache and session scenarios.

-
    -
  • Use Redis authentication type Key Vault when you want the stored value to represent a secret name instead of a raw Redis key.
  • -
  • This works best when Key Vault is already configured for the app.
  • -
- -
- - -
- -
-
-
Redis Cache Settings
-
- - -
-
- - -
-
- -
- - -
-
- When using Key Vault, store the secret name here instead of the raw Redis key. -
-
-
-
- -
-
-
- -
-
-
-
- Send Feedback to the SimpleChat Team -
-

Admins now have a dedicated Send Feedback tab for bug reports and feature requests that opens a prefilled email draft and writes an audit record to the activity log.

-
- -
-
-
-

Use the Send Feedback tab when you want to report a bug or suggest a feature directly from Admin Settings without leaving the page.

-
    -
  • Opens a prefilled email draft addressed to simplechat@microsoft.com.
  • -
  • Logs the action to the activity log so admins can audit when feedback emails were prepared.
  • -
  • Keeps the workflow simple by using text-only email content with no confusing pseudo-attachment step.
  • -
- - Open Send Feedback - -
-
-
- -
-
-
-
- Support Menu -
-

Support can now expose user-facing Latest Features and Send Feedback workflows directly in everyday navigation.

-
- -
-
-
- -
    -
  • Admins can enable or disable the Support menu and rename it for user navigation.
  • -
  • Send Feedback now routes users to an internal recipient email address defined by admins.
  • -
  • The user-facing Latest Features page can be curated feature-by-feature so teams only share the items they want visible.
  • -
  • The General tab now includes a dedicated User-Facing Latest Features checklist so admins can quickly confirm which announcements will be visible to end users.
  • -
- - Open General Settings - - -
-
-
- {% endif %} - - {% for release_group in support_latest_feature_release_groups_preview %} - {% if release_group.id != 'current_release' %} - {% set preview_card_id = 'latest-features-user-preview-' ~ release_group.id|replace('_', '-') ~ '-card' %} - {% set preview_collapse_id = 'latestFeaturesUserPreview' ~ release_group.id|replace('_', ' ')|title|replace(' ', '') %} -
-
-
-
- {{ release_group.label }} -
-

{{ release_group.description }}

-
- -
-
-
- -
- {% for feature in release_group.features %} -
-
-
-
-
-
{{ feature.title }}
-

{{ feature.summary }}

-
- - {% if settings.support_latest_features_visibility.get(feature.id, true) %}Shared with Users{% else %}Hidden from Users{% endif %} - -
-

{{ feature.details }}

- {% if feature.guidance %} -
    - {% for tip in feature.guidance %} -
  • {{ tip }}
  • - {% endfor %} -
- {% endif %} - {% if feature.images %} - - {% endif %} - {% if feature.actions %} -
- {% for action in feature.actions %} - - {{ action.label }} - - {% endfor %} -
- {% endif %} -
-
-
- {% endfor %} -
-

Visibility still comes from General > User-Facing Latest Features, so admins can keep previous and archived release content available without promoting every item forever.

-
-
-
- {% endif %} - {% endfor %} -
- -
-

- Enable trusted deployment-time custom pages and manage metadata for simple static pages served from the application custom_pages folders. -

- -
-
-
- Custom Pages -
- -
-
- - - - -
-

When disabled, /custom routes return Not Found before loading custom metadata, files, or Python extensions.

- -
-
- - - This name appears when custom pages are grouped into a menu. -
- -
- - - When disabled, 1-2 custom pages show as top-level nav items and 3+ pages show as a menu. -
- -
-
-
Static Page Metadata
-

Create metadata contracts for simple pages that use files from custom_pages/html, css, js, assets, and json.

-
-
- - -
-
- -
- - - - - - - - - - - - - - - - - - -
SlugTitleTypeAccessRolesStatusNavActions
Loading custom pages...
-
-
-
-
- - -
- -

- Configure all AI model settings including GPT for text generation, embeddings for semantic search, and image generation capabilities. -

- -
-
- Model Endpoints -
-

- Manage multiple AI model endpoints (Azure OpenAI and Azure AI Foundry). When enabled, model selection in chat is driven by these endpoints. -

- - {% if not settings.enable_multi_model_endpoints %} -
- - -
- {% endif %} - - {% if settings.enable_semantic_kernel %} -
-
-
-
Agent Default Model Review
-

- Review local agents in a modal, bulk-bind inherited agents to the saved default model endpoint, and explicitly override selected agent model choices when you need to manage cost or move to newer models. -

-
-
- -
-
-
- {% if settings.enable_multi_model_endpoints %} - Save your AI model settings before reviewing or migrating agents. - {% else %} - Enable multi-endpoint model management to review and rebind agents to a saved default model. - {% endif %} -
- -
- -
-
- Ready: 0 - Needs Default: 0 - Manual Review: 0 - Already Migrated: 0 -
-
-
- Open the review modal to search, filter, and selectively rebind agents to the saved default model. -
-
- - - {% else %} - - {% endif %} - -
-
-
-
Model Endpoint Identity Header
-

- Send a stable HMAC-hashed user key to model endpoint backends for APIM counters, quota policies, or routing policies. -

-
-
- - -
-
-
-
- - -
Reserved auth/protocol headers cannot be used.
-
-
- - -
The selected identity is HMAC-hashed before leaving SimpleChat. Missing identity values omit the header.
-
-
-
- -
- - -
- Used for tasks such as conversation summarization, fallback, and other operations when an agent is selected. -
-
- - - - -
-
-
Global Endpoints
-
- - -
-
-
- - - - - - - - - - - - - -
NameProviderSelected ModelsStatusActions
-
-
- {% if not settings.enable_multi_model_endpoints %} - - {% endif %} -
- - -
-
- Processing Thoughts -
-

When enabled, real-time processing steps are shown to users during chat responses and persisted for later review.

-
- - - -
-
- - -
-
- Shared Conversation File Approvals -
-

Files generated by participants in a shared conversation are saved into the conversation owner's storage. When enabled, those files are held until an approver releases them.

-
- - - -
-
- - -
-
- Embeddings Configuration -
-

- Configure your embeddings settings. These are used for semantic search, knowledge-base lookups, etc. -

- - -
- - - -
- -
-
-
- - -
-
- - -
-
- - -
-
- - -
-
- -
- - -
-
-
-
- -
- Save pending changes to settings before clicking Fetch Embedding Models
- - - - - - - - -
- -
-
-
- - -
-
-
-
-
- -
-
- - -
-
- - -
-
- - -
-
- -
- - -
-
-
- - -
-
- - -
-
- Image Generation Configuration -
-

- Configure image generation settings. Enable/disable, set endpoints, and choose a model. -

- - -
- - - -
-
- -
- - - -
- -
-
-
- - -
-
- - -
-
- - -
-
- - -
-
- -
- - -
-
-
-
- -
- - Save pending changes to settings before clicking Fetch Image Generation Models
- - - - - - - - -
- -
-
-
- - -
-
-
-
-
- -
-
- - -
-
- - -
-
- - -
-
- -
- - -
-
-
-
- - -
-
- - - - {% include '_multiendpoint_modal.html' %} - - - -
- - -
-

- Configure Control Center access and permissions for administrators. -

- -
-
-
-
- -
-
-

- Automatic Data Refresh -

-

Run the Control Center metrics refresh overnight on a daily timezone-aware schedule

-
-
-
- -
- - - -
- -
-
- - - -
- Your browser timezone is local time. -
-
-
- -
- {% if settings.control_center_auto_refresh_next_run %} - Loading local time... - {% else %} - Will be calculated when saved - {% endif %} -
-
-
-
- - -
-
-
-
- -
-
-

- Control Center Access -

-

Manage who can access Control Center features and administrative tools

-
-
- -
- -
- - About Control Center: The Control Center is a powerful administrative dashboard that provides user management, group oversight, public workspace control, and detailed activity monitoring. Use role-based access controls below to delegate administrative responsibilities. -
- - -
-
- - -
-

- Required app role value: ControlCenterAdmin. Assign this role to users or groups in the Enterprise App before enabling the requirement. When enabled, only users assigned this role can access the Control Center and all management features. Regular Admins will lose access. -

-
- - User Management - - - Group Management - - - Public Workspaces - - - Activity Logs - -
-
- - -
-
- - -
-

- Dashboard-only app role value: ControlCenterDashboardReader. Assign this role to users or groups in the Enterprise App before enabling dashboard-only access. Users assigned ControlCenterAdmin can also access the dashboard when Control Center app-role enforcement is enabled. -

-
- - Dashboard Statistics - - - Activity Trends - - - Usage Metrics - -
-
- -
- - Important: Configure these roles in your identity provider (Entra ID). When role requirements are enabled, standard Admins without the specific roles will be denied access to Control Center features. -
- - -
-
- Best Practices -
-
    -
  • ControlCenterAdmin: Grant to IT administrators who need full control over users, groups, and workspaces
  • -
  • ControlCenterDashboardReader: Grant to managers, compliance officers, or stakeholders who need visibility into platform usage without administrative powers
  • -
  • Separation of Duties: Enable role requirements if you need to restrict Control Center access from general application admins
  • -
  • Audit Trail: All Control Center actions are logged in Activity Logs for compliance and security auditing
  • -
-
-
-
- -
-
-
-

Backup, Migrate & Restore

-

Protect SimpleChat data, move selected workspaces to another environment, and stage restore decisions with guided checks.

-
- -
- - - -
- -
-
-
-

Start Here

-

Use these checkpoints before running backup, migration, restore, or advanced repair actions.

-
- -
-
-
-
-
Back up
-

Configure dedicated storage, encryption, schedule, and backup scope before queueing jobs.

- -
-
-
-
-
Migrate
-

Connect a destination, choose who moves, run preflight, then execute a recoverable transfer.

- -
-
-
-
-
Restore
-

Review backup readiness and stage restore decisions from Backup Inventory.

- -
-
-
-
-
RU Boost
-

Temporarily raise eligible Cosmos capacity during approved backup or migration windows.

- -
-
-
-
- -
-
-
- -
-
-

Backup

-

Configure when backups run, where artifacts are stored, and how backup files are encrypted.

-
-
- -
-
-
-
Schedule
-

Full backups run on the selected cadence; partial backups run daily only.

-
-
-
- - -
-
-
- - -
-
- - -
Default is 03:00 UTC.
-
-
- -
- - - -
- -
Automatic cleanup keeps the newest successful full backup as a safety baseline.
-
-
-
-
-
- - -
-
-
-
- - -
-
-
-
- -
-
-
- -
-
-
- - -
Core application records required for meaningful restore and migration.
-
-
-
-
- - -
Search index schemas and retrievable indexed documents.
-
-
-
-
- - -
Original source files used by Enhanced Citations.
-
-
-
-
-
-
-
- -
-
-
-
Storage
-

Store backup artifacts in Azure Blob Storage.

-
- -
-
- - Use a dedicated backup storage account. Data Management will reject storage that matches the Enhanced Citations connection string or Blob endpoint. -
-
-
- - -
-
- - -
-
- - -
-
-
-
- - -
No connection string saved yet.
-
-
- - -
-
-
- -
-
-
-
Encryption
-

Generate a 256-bit backup encryption key.

-
- -
-
- - -
-
-
Key storage
-
Not configured
-
Key reference
-
Not configured
-
-
- -
-
Key Vault is strongly recommended
-
Generated backup encryption keys are stored in the Data Management settings document when Key Vault is not enabled.
- Open Key Vault settings -
-
-
- -
-
-
-
Cosmos Backup Performance
-

Backups stream deterministic checkpoint batches and commit only verified work. Higher concurrency can increase source Cosmos cost and pressure.

-
- -
- -
-
-
Source Blob Backup Performance
-

Source files stream through bounded chunks and durable per-file checkpoints. Peak transfer buffering is bounded by concurrent transfers multiplied by chunk size.

-
-
-
- - -
-
- - -
-
- - -
-
-
Defaults bound application transfer buffering to approximately 32 MiB, excluding Azure SDK overhead. Throttling temporarily reduces active transfers.
-
-
-
- - -
-
- - -
-
- - -
-
-
- - -
-
-
- - -
-
-
The backup records the current source capacity, raises only eligible targets up to 10,000 RU/s, and restores the original setting after completion, cancellation, failure, or recovery. This can increase Cosmos charges and requires source ARM throughput permission.
-
-
-
- -
-
-
-
Backup Operations
-

Queue immediate full or partial backup jobs using the settings above.

-
-
- - -
-
-

Jobs use Cosmos-backed leases so scaled-out App Service workers do not run the same backup twice.

-
-
- - -
-
-
-

- Migration -

-

Move SimpleChat data through a reviewed, recoverable environment transfer.

-
-
- - Not reviewed -
-
- -
- - - - - -
-
-
-
Connect the destination
-

Configure the services this migration will write to. Stored credentials remain redacted.

-
- Destination database: SimpleChat -
- -
-
-
-
-
Target Cosmos Database
-

Required for every migration.

-
- -
-
- Managed identity requires Cosmos DB Data Contributor and target network access. -
-
-
- - -
-
- - -
-
- - -
Fixed app contract.
-
-
-
- - -
-
- -
-
-
-
Target Search
-

Required when AI Search documents are included.

-
- -
-
-
- - -
-
- - -
-
- - -
-
-
- -
-
-
-
Target Enhanced Citation Storage
-

Required only when source document blobs are included.

-
- -
-
-
- - -
-
- - -
-
- - -
-
-
-
-
- - -
-
-
-
Choose who and what moves
-

Selections persist while you search and page. “All” always uses the exhaustive server count.

-
-
0 principal scopes selected
-
- -
- - - -
- -
- -
- Migration mode -
- - - -
-
- -
-
-
-
-
Available users
-

Search the server catalog.

-
- -
-
-
- - Page 1 - -
-
- -
- -
- -
- Loading exhaustive count… - Every current user record will be resolved by the server when the job starts. -
-
- -
- - -
-
- -
- -
- Migration mode -
- - - -
-
-
-
-
-
-
Available groups
-

Search the server catalog.

-
- -
-
-
- - Page 1 - -
-
- -
-
- -
- Loading exhaustive count… - Every current group record will be resolved by the server when the job starts. -
-
-
- - -
-
- -
- -
- Migration mode -
- - - -
-
-
-
-
-
-
Available public workspaces
-

Search the server catalog.

-
- -
-
-
- - Page 1 - -
-
- -
-
- -
- Loading exhaustive count… - Every current public workspace record will be resolved by the server when the job starts. -
-
-
- - -
-
-
- - -
-
-
-
Choose what happens at the destination
-

Choose whether to copy only missing items, catch up changes, or make migrated destination data match the source.

-
-
- -
- Destination behavior -
- - - - - - -
-

- Copies source items that are absent from the destination. Existing destination data is never updated or deleted. -

-
-
- - -
Leave blank to let SimpleChat choose the latest compatible completed migration as the starting point for this catch-up run.
-
-
-
- -
- Data surfaces -
-
-
- - -
-
- - -
SimpleChat pauses its own target indexing. Freeze other writers before review.
-
-
-
-
- - -
Requires Enhanced Citation storage at both source and destination.
-
-
-
-
- -
- Performance and resume -

Migration uses durable resource checkpoints and retains the same migration ID after Retry or Resume.

-
-
- - -
-
- - -
-
- - -
-
-
- - - -
-
-
- - -
-
- - -
-
- - -
-
- -
-
-
RU Boost validates Azure management-plane throughput permissions separately from Cosmos data-copy access. Eligible capacity is raised only up to 10,000 RU/s during execution and restored after completion or failure.
-
-
-
-
- - -
-
-
-
Prove the plan is ready
-

Preflight runs server-owned access probes and inventory. Any earlier change makes this review stale.

-
-
- - -
-
-
- -
- Review has not run. - Run preflight to verify target access, counts, collisions, locks, and capacity policy. -
-
-
-
- -
- - -
-
-
-
Confirm execution
-

Review the final server-normalized plan. Submission is guarded against duplicate requests.

-
-
-
-

Complete preflight review before confirmation.

-
- -
- - -
- -
- - -
-
-
-
Operate the migration
-

Progress comes from the durable job record. Cancel, Retry, and Resume retain verified checkpoints.

-
-
- - -
-
-
- -
- No migration is attached to this workflow yet. - After execution, this stage follows the queued job and exposes its recovery actions. -
-
-
-
-
-
-
-
- -
-
- - -
- Step 1 of 6 -
-
-
- -
-
-
-

Cosmos DB JSON Editor

-

Query SimpleChat Cosmos DB containers, inspect one document, and save JSON changes with ETag protection.

-
- -
- -
- The Cosmos DB JSON editor is locked. Acknowledge the danger prompt before querying or editing data. -
-
-
-
- - -
Choose a known SimpleChat Cosmos DB container.
-
-
- - -
Max 100 per request.
-
-
- - -
Empty query returns only the first 100 documents. Custom SELECT queries can page beyond 100 with Next Page.
-
-
-
- - No query has run yet. -
-
- Query results and the JSON editor open in a modal so the Data Management page stays compact. -
-
-
- -
-
-
-

Backup Inventory

-

Track completed full and partial backups created by Data Management jobs.

-
-
- - - -
-
-
-
-
What does Run Retention Cleanup do?
-

- It permanently deletes backups whose age exceeds the retention period configured in Data Management settings, - and removes their stored artifacts from the backup container. Backups newer than the retention cutoff are left alone. -

-
    -
  • Only backups in a finished state are eligible; running or queued jobs are skipped.
  • -
  • When Keep latest full backup is enabled, the most recent successful full backup is protected even if it is past the cutoff.
  • -
  • Each run deletes at most 25 backups, so very large cleanups may need several runs.
  • -
  • Cleanup also runs automatically on the configured schedule; this button just runs it now.
  • -
-

- Seeing “found no expired backups to delete” means every backup is still inside the retention window. That is expected, not an error. -

-
-
-
-
- -
-
- -
-
- -
-
-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
-
- - - - - - - - - - - - - - - - - -
BackupCompletedContentsStorageProtectionWarningsActions
Backup inventory has not loaded yet.
-
- -
- - - -
-
-

Job History

- -
-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
-
- - - - - - - - - - - - - - - - -
CreatedOperationStatusProgressMessageActions
Job history has not loaded yet.
-
- -
- - - - - - - - - - - - - - - - - - - - -
- -
- {% set enable_dai_debug = settings.enable_dai_debug | default(false) %} -

- Configure Redis cache to improve enterprise scale and performance by caching session data. Enabling Redis allows you to horizontally scale your application across multiple instances without losing session data. -

-
-
- Redis Cache -
-

- Enable Redis Cache to store session data in a distributed cache. This is recommended for production and multi-instance deployments. -

-
- - - -
-
-
- -

(example: simple-chat.redis.cache.windows.net)

- -
-
- - -
-
- -
- - -
-
- Enter the full Key Vault secret name. - Enable Key Vault for Agent and Action Secrets - must be enabled and configured. -
-
- -
-
-
-
-
-
- Redis Monitoring -
-

- Monitor Redis availability, memory pressure, hit rate, evictions, and runtime cache usage before Redis-backed document list caching is enabled. -

-
-
- - -
-
-
-
-
-
-
Configuration
- Not loaded -
-
-
-
-
Health
- Not loaded -
-
-
-
-
App Cache Runtime
- Not loaded -
-
-
-
-
Session Runtime
- Not loaded -
-
-
-
-
-
-
Ping Latency
-
Not loaded
-
-
-
-
-
Memory Usage
-
Not loaded
-
Not loaded
-
-
-
-
-
Connected Clients
-
Not loaded
-
-
-
-
-
Ops/sec
-
Not loaded
-
-
-
-
-
Keyspace Hit Rate
-
Not loaded
-
-
-
-
-
Tracked Keys
-
Not loaded
-
-
-
-
-
DAI Version Markers
-
Not loaded
-
Not loaded
-
-
-
-
-
DAI Cache Payloads
-
Not loaded
-
Not loaded
-
-
-
-
-
Expired / Evicted Keys
-
- Not loaded - / - Not loaded -
-
-
-
-
-
Fragmentation Ratio
-
Not loaded
-
-
-
-
-
Error Replies
-
Not loaded
-
-
-
-
-
Rejected Connections
-
Not loaded
-
-
-
-
-
Redis Version
-
Not loaded
-
-
-
-
-
Monitoring Source
-
Not loaded
-
-
-
-
-
Last Checked
-
Not loaded
-
-
-
-
-
Last Error
-
Not loaded
-
-
-
-
-
- - - -
-
-
-
- Conversation Cache -
-

- Cache conversation list, feed, and advanced-search results per user. Redis is optional; cache misses and disabled cache paths continue using source Cosmos queries. -

-
- -
-
-
-
- - -
-
When enabled, list/feed/search payloads are cached by user and version. Disabling this bypasses cache reads and writes without requiring Redis.
-
-
- - -
Default 120 seconds. User-scoped version invalidation refreshes changed conversations; set to 0 to skip writing new entries.
-
-
-
-
-
-
Runtime Status
- Not loaded -
-
-
-
-
15m Cache Hit Rate
-
Not loaded
-
-
-
-
-
15m Cache Hits / Misses
-
Not loaded
-
-
-
-
-
15m Cache Bypasses / Errors
-
Not loaded
-
-
-
-
-
15m Writes / Invalidations
-
Not loaded
-
-
-
-
-
15m Operation Mix
-
Not loaded
-
-
-
-
-
Last Cache Event
-
Not loaded
-
-
-
-
-
Last Invalidation
-
Not loaded
-
-
-
-
- - Conversation cache metrics are lightweight in-process counters for the current app worker. Application Insights logs remain the durable fleet-wide source for cache warnings and fallback behavior. -
-
-
-
- -
-
-
-
- Cosmos Document Access Index -
-

- Monitor the document access projection used to remove expensive cross-partition document access queries. -

-
-
- - {% if enable_dai_debug %} - - - {% endif %} -
-
- -
- - Document access projection maintenance is automatic. The background scheduler repairs fail-open projection records first, then runs bounded backfill batches repeatedly while work remains. Production read metrics below show DAI-served reads, Redis cache hits, source fallbacks, RU, and latency without requiring shadow validation. - {% if enable_dai_debug %}Debug controls and shadow validation diagnostics are visible because enable_dai_debug is enabled in app settings.{% endif %} -
- -
- -
-
-
-
Container
- Not loaded -
-
-
-
-
Write-through
- Not loaded -
-
-
-
-
Read Path
- Not loaded -
-
-
-
-
Redis List Cache
- Not loaded -
-
- {% if enable_dai_debug %} -
-
-
Shadow Validation
- Not loaded -
-
- {% endif %} -
-
-
Auto Maintenance
- Not loaded -
-
-
-
-
Next Maintenance Action
-
Not loaded
-
-
-
-
-
More Work Pending
-
Not loaded
-
-
-
-
-
Active Loop Interval
-
Not loaded
-
-
-
- - {% if enable_dai_debug %} -
-
Automatic Maintenance and Diagnostics
-
-
-
- - -
-
Always on. New and changed documents synchronize into the access index and fail open to repair records if projection updates fail.
-
-
-
- - -
-
Always on. Maintenance keeps running bounded repair and backfill batches until DAI is healthy.
-
-
-
- - -
-
Compares source list results to projection rows and logs mismatches without changing reads.
-
-
- - -
Documents processed per manual or scheduled batch.
-
-
- - -
Fail-open repair records reconciled before each backfill batch.
-
-
-
- -
-
Default Read Path
-
-
-
- - -
-
Always on. DAI-backed document and tag list reads are the normal path; source-container fallback remains automatic when backfill is not ready, repairs are pending, or a DAI query fails.
-
-
-
- - -
-
Uses Redis read-through caching for DAI document, tag, and legacy-count reads. If Redis is unavailable, reads bypass cache and use DAI directly.
-
-
- - -
Default 900 seconds. Scope-version invalidation makes document changes visible immediately; TTL clears unreachable old entries.
-
-
-
- {% endif %} - -
-
-
-
Backfill State
- Not loaded -
-
-
-
-
Repair Backlog
-
Not loaded
-
-
-
-
-
15m DAI Read Attempts
-
Not loaded
-
-
-
-
-
15m Redis Cache Hit Rate
-
Not loaded
-
-
-
-
-
15m Cache Hits / Misses
-
Not loaded
-
-
-
-
-
15m Cache Bypasses / Errors
-
Not loaded
-
-
-
-
-
15m Cache Invalidations
-
Not loaded
-
-
-
-
-
15m Served from DAI
-
Not loaded
-
-
-
-
-
15m Source Fallbacks
-
Not loaded
-
-
-
-
-
15m Fallback Rate
-
Not loaded
-
-
-
-
-
15m DAI Read RU
-
Not loaded
-
-
-
-
-
15m Avg / P95 Latency
-
Not loaded
-
-
-
-
-
Last Fallback Reason
-
Not loaded
-
-
-
-
-
Last DAI Read Metric
-
Not loaded
-
-
-
-
-
Last Cache Event
-
Not loaded
-
-
-
-
- - Production read metrics are lightweight in-process counters for the current app worker. Application Insights logs remain the durable fleet-wide source for fallback warnings and query failures. -
-
- {% if enable_dai_debug %} -
-
-
Last Shadow Result
- Not run -
-
-
-
-
Shadow Mismatches
-
Not loaded
-
-
-
-
-
Source / Validation RU
-
Not loaded
-
-
-
-
-
Validation Index RU
-
Not loaded
-
-
-
-
-
Candidate Read RU
-
Not loaded
-
-
-
-
-
Estimated Wave 5 Savings
-
Not loaded
-
-
-
-
-
Source / Candidate Latency
-
Not loaded
-
-
-
-
-
Estimated Wave 5 Latency
-
Not loaded
-
-
-
-
- - Rolling decision metrics aggregate shadow-validation samples over recent windows. Use these totals to compare source container RU with candidate access-index RU before enabling the future read path or Redis document access cache. -
-
-
-
-
5m Source / Candidate RU
-
Not loaded
-
-
-
-
-
5m Estimated Wave 5 Savings
-
Not loaded
-
-
-
-
-
15m Source / Candidate RU
-
Not loaded
-
-
-
-
-
15m Estimated Wave 5 Savings
-
Not loaded
-
-
-
-
-
15m Validation Overhead
-
Not loaded
-
-
-
-
-
15m Shadow Samples
-
Not loaded
-
-
- {% endif %} -
-
-
Current Scope
-
Not loaded
-
-
-
-
-
Completed Scopes
-
Not loaded
-
-
-
-
-
Total Documents Processed
-
Not loaded
-
-
-
-
-
Total Documents Failed
-
Not loaded
-
-
-
-
-
Rows Upserted
-
Not loaded
-
-
-
-
-
Rows Deleted
-
Not loaded
-
-
-
-
-
Last Batch Completed
-
Not loaded
-
-
-
-
-
Last Error
-
Not loaded
-
-
-
-
- -
-
-
-
- Cosmos Maintenance -
-

- Review expected indexing policies and clean up stale operational cache documents without touching source configuration or user data. -

-
-
- - - - -
-
- -
- - Indexing maintenance only adds missing expected composite indexes and preserves existing policy paths. Composite indexes can increase write-index overhead while improving supported lookup and ordered-query speed. Stale cleanup is allowlisted to obsolete cache artifacts, supports dry-run review, and deletes at most one bounded batch per run. -
-
- -
-
-
-
Indexing Policy Status
- Not loaded -
-
-
-
-
Indexing Mode
-
Not loaded
-
-
-
-
-
Containers Checked
-
Not loaded
-
-
-
-
-
Missing Expected Indexes
-
Not loaded
-
-
-
-
-
Updated Containers
-
Not loaded
-
-
-
-
-
Indexing Failures
-
Not loaded
-
-
-
-
-
Last Indexing Evaluation
-
Not loaded
-
-
-
-
-
Stale Cleanup Status
- Not loaded -
-
-
-
-
Cleanup Mode
-
Not loaded
-
-
-
-
-
Cleanup Candidates
-
Not loaded
-
-
-
-
-
Deleted Docs
-
Not loaded
-
-
-
-
-
Cleanup Failures
-
Not loaded
-
-
-
-
-
More Candidates
-
Not loaded
-
-
-
-
-
Cleanup Categories
-
Not loaded
-
-
-
-
-
Last Cleanup Evaluation
-
Not loaded
-
-
-
-
- - - - - - {% if enable_dai_debug %} - - {% endif %} - -
-
-
-
- Cosmos DB Throughput -
-

- Monitor RU utilization and automatically adjust shared database or dedicated container throughput. -

-
- -
- -
- - - - - - -
- -
- - Automation checks Cosmos throughput on the Metrics Window cadence while enabled and saves the last observed database or container view. SimpleChat can scale throughput up or down at 10,000 RU/s or lower. Above 10,000 RU/s, SimpleChat monitors utilization only; use the Azure portal for capacity changes, which can take 4 to 6 hours. Native Cosmos autoscale conversion is separate from SimpleChat scale-up and scale-down automation. -
- - - -
- - - -
- -
-
-
- - -
-
- - -
-
- - -
-
- - -
-
- -
-
Metrics Window
-
-
- -
- - min -
-
-
-
- -
-
Scale Up Policy
-
-
-
- - -
-
-
- -
- - % -
-
-
- -
- - RU/s -
-
-
- -
- - min -
-
-
- - -
SimpleChat-managed scaling stops at 10,000 RU/s. Use the Azure portal above this limit.
-
-
-
- - -
-
-
-
- -
-
Scale Down Policy
-
-
-
- - -
-
-
- -
- - % -
-
-
- -
- - RU/s -
-
-
- -
- - min -
-
-
- - -
-
-
- - -
-
-
-
- - - -
-
- - - -
-
- - - -
-
- Use this when container throughput should follow one standard automation policy instead of per-container overrides. Global policy also applies the Cosmos autoscale conversion setting above to current and future dedicated-throughput containers. -
-
-
- -
- -
-
-
-
Mode
-
Not loaded
-
-
-
-
-
Current RU/s
-
Not loaded
-
-
-
-
-
RU Utilization
-
Not loaded
-
-
-
-
-
Last Checked
-
Not loaded
-
-
-
- -
-
- -
- - - - -
-
-
-
- -
-
- -
- - - - - - - - - - - - - - - - - -
- - Mode - - - - - - - - - - Actions
Refresh to load Cosmos metrics.
-
-
- - - - - - -
-
-
- Front Door -
- -
-

Configure Front Door URL for authentication and redirect flows.

- -
- - - -
- -
- - - - The base URL of your Front Door or load balancer. The system will automatically generate: -
    -
  • Home redirect: https://your-frontdoor.azurefd.net
  • -
  • OAuth2 redirect: https://your-frontdoor.azurefd.net/getAToken
  • -
-
-
-
-
- - -
-
-
-
-
- - -
-

- Configure file synchronization for personal, group, and public workspaces. -

- -
-
-
- File Sync -
- -
-

- Enable File Sync globally, set conservative run limits, and choose whether workspace managers or SimpleChat admins manage sync sources. -

- - - - {% if not settings.file_sync_redis_ready %} - - {% elif settings.requested_enable_file_sync and not settings.file_sync_effective_enabled %} - - {% endif %} - -
- - - -
- -
-
-
- - -
-
- - -
-
- - -
-
- - -
-
- -
-
- - -
-
-
- - -
-
-
- - {% set file_sync_visible_source_types = settings.file_sync_visible_source_types|default(['smb', 'azure_files']) %} -
-
Visible Source Types
-

- Choose which source types appear in the Add Source workflow. OneDrive, SharePoint, and Google Workspace connectors are coming soon while validation continues. -

-
-
-
- - -
Available now.
-
-
-
-
- - -
Available now.
-
-
-
-
- - -
Available now.
-
-
-
-
- - -
Coming Soon.
-
-
-
-
- - -
Coming Soon.
-
-
-
-
- - -
Coming Soon.
-
-
-
-
- - - -
-
-
-
Personal Workspace Sync
-
- - -
-
- - -
-
- - -
-
Required app role value: PersonalFileSyncUser. Assign this role to users or groups in the Enterprise App before enabling the requirement for personal sync.
-
-
- -
- - -
-
-
- - -
-
No user selected.
-
-
-
- -
-
-
Group Workspace Sync
-
- - -
-
- - -
-
- - -
-
- - No groups assigned. -
- -
-
- -
- - -
-
-
- - -
-
No group selected.
-
-
-
- -
-
-
Public Workspace Sync
-
- - -
-
- - -
-
- - -
-
- - No public workspaces assigned. -
- -
-
- -
- - -
-
-
- - -
-
No public workspace selected.
-
-
-
-
- -
- SMB source credentials are stored in Azure Key Vault when Key Vault secret storage is enabled; otherwise they use the app's existing encrypted settings path. -
-
-
- - - - - - - - -
- -
-

- Configure workspace settings like personal/group access, multimedia support, metadata, and document classification. -

- - -
-
- Personal Workspaces -
-

- Turn this on to allow access and management of your personal workspace. -

-
- - - -
-
- -
-
- Workflow -
-

- Control personal and group workflow authoring and execution for users. -

-
- - - -
-
- - - -
-

- Required app role value: WorkflowUser. Assign this role to users or groups in the Enterprise App before enabling the requirement. -

- -
- - -
- Maximum automatic tool or action calls an agent can make during one workflow run. Default is 60; increase for large document sets. -
- -
- -
- - -
- Maximum ordered instruction tasks users can add to one workflow. Default is 50; supported range is 1-100. -
-
- -
- -
- - - -
-
- - - -
-
- - No groups assigned. -
- -

- Group workflow authoring is available to group Owners and Admins by default. Enable the owner-only setting below to limit group agent, action, and workflow management to Owners. -

-
- -
-
- File Downloads -
-

- Control when users can download original workspace document files from personal, group, and public workspaces. -

-
- - - -
-
-
- - - -
-
- - - -
-
- - No groups assigned. -
- -
-
- - - -
-
- - - -
-
- - No public workspaces assigned. -
- -
- - - - - - - - -
-
- Group Workspaces -
-

- Turn this on to allow access and management of group workspaces, as well as group collaboration features. -

-
- - - -
- - -
-
- - -
- - - -
-

- When enabled, no users will be able to create new groups, regardless of app role assignment. This global setting overrides the Require CreateGroups App Role setting below. -

- -
- - - -
-

- Required app role value: CreateGroups. Assign this role to users or groups in the Enterprise App before enabling the requirement. If disabled, any authenticated user can create groups while group workspaces and group creation are enabled. -

- -
- - -
- - - -
-

- If enabled, only the group Owner can create, edit, and delete group agents, group actions, and group workflows. Admins and other roles will only be able to view them. -

-
- -
- -
-
- Public Workspaces -
-

- Turn this on to enable public workspaces that are visible to everyone in the organization. -

-
- - - -
- -
- - -
- Optional. End users will see this label instead of Public Workspace. Admin settings and internal references continue to use Public Workspace. -
-
- -
-
-
- - - -
-

- Required app role value: CreatePublicWorkspaces. Assign this role to users or groups in the Enterprise App before enabling the requirement. If disabled, any authenticated user can create public workspaces while public workspaces are enabled. -

-
- -
- -
-
- File Sharing -
-

- Turn this on to enable file sharing capabilities between users and workspaces. -

-
- - - -
-
- -
-
- Chat File Uploads -
-

- Control whether users can upload files directly into chat conversations. -

-
- - - -
- -
-
- Conversation Contents Drawer -
-

- Let users navigate long conversations from a list of their prompts. -

-
- - - -
-

- When enabled, users can turn the drawer off for their own account from Profile settings. -

-
-
- - - -
-

- Required app role value: ChatFileUploadUser. Assign this role to users or groups in the Enterprise App before enabling the requirement. Existing chat attachments remain visible; this only controls new uploads. -

-
- -
-
- Metadata Extraction -
-

- Enable this to automatically parse and store file metadata for advanced indexing and search. -

-
- - - -
- -
- - - -
- Uses Global Endpoints when multi-endpoint model management is enabled; otherwise uses the legacy GPT/APIM deployment settings. -
-
-
- - -
-
- Multi-Modal Vision Analysis -
-

- Enable AI-powered vision analysis for images uploaded to chat or workspace. When enabled alongside Document Intelligence OCR, images will receive both text extraction (OCR) and semantic understanding (vision AI). -

- -
- How it works: -
    -
  • Document Intelligence: Extracts text from images (OCR)
  • -
  • Vision Model: Provides semantic analysis, object detection, and contextual understanding
  • -
  • Both analyses are combined and available in citations when Enhanced Citations is enabled
  • -
-
- -
- - - -
- -
- - -
Select a GPT model with vision capabilities (for example, gpt-4o or supported GPT 5 and later models). Only vision-capable models are shown.
- - -
-
-
- - - -
-
- Document Classification -
-

- Enable this feature to allow users to classify documents uploaded to their workspaces using predefined categories. -

-
- - - -
- - -
-
Classification Categories
-

Define the labels and corresponding colors for document classification.

- - - - - - - - - - - -
LabelColorActions
- - - -
-
- - -
-
- Retention Policy -
-

Automatically delete aged conversations and documents based on configurable retention periods. Users, group owners, and public workspace admins can set their own retention policies.

- - - - -
-
-
- - - -
-
-
-
- - - -
-
-
-
- - - -
-
-
- - -
-
Default Retention Policies
-

Set organization-wide default retention periods for each workspace type. Users can override these defaults with their own preferences. Setting a default here means new users/workspaces will start with this retention period.

- - -
-
- -
-
- - -
-
- - -
-
- - -
-
- -
-
- - -
-
- - -
-
- - -
-
- -
-
- - -
-
- - -
-
- - -
-
-
- - Force Push: Override all user/group/workspace custom retention policies with the organization defaults above. Users will then use the organization default until they set their own preference. -
- -
-
-
- - -
- - - Retention policy will run once daily at this hour (UTC timezone). -
- - -
-
- -
- {% if settings.retention_policy_last_run %} - {{ settings.retention_policy_last_run }} - {% else %} - Never run - {% endif %} -
-
-
- -
- {% if settings.retention_policy_next_run %} - {{ settings.retention_policy_next_run }} - {% else %} - Not scheduled - {% endif %} -
-
-
- - -
- - - Trigger retention policy execution immediately for selected workspace types, bypassing the scheduled time. - -
- - -
- - -
-
- Workspace Scope Lock -
-

- Control whether users can unlock workspace scope in chat conversations. When scope is locked, conversations are restricted to the workspaces that produced search results, preventing accidental cross-contamination with other data sources. -

-
- - - -
-
- - -
-
- User Agreement -
-

- Configure a user agreement that users must accept before uploading files. - Supports Markdown formatting. -

- - -
- - - -
- - -
-
- - -
- -

Select where the user agreement should be shown (at least one required):

-
-
-
- - -
-
- - -
-
-
-
- - -
-
- - -
-
-
- -
- - -
- - -
- 0 / 200 words - -
-
- - -
- - - -
- - -
- -
-
-
-
- - -
- -

- Configure standard and enhanced citations features for your and group workspaces. -

- - -
-
- Standard Citations -
-

- Standard citations is always enabled for both Your Workspace and Group Workspace. -

-

- Users can see text content of the source/citation for documents. -

-
- - -
-
- Enhanced Citations -
-

- Enable Enhanced Citation to store files in a Storage Account, - and show direct references (Preview feature, files are saved to storage, presentation - layer will be available in a future release). -

- - {% set ec_storage_status = settings.enhanced_citations_storage_status or {} %} - {% set ec_storage_state = ec_storage_status.state or 'not_initialized' %} - {% if settings.enable_enhanced_citations %} -
-
Enhanced Citations storage startup status
-
{{ ec_storage_status.message or 'Enhanced Citations storage status is not available for this process.' }}
-
- Startup skips live storage container checks so storage outages do not block application boot. - Use the connection test to validate current storage reachability and container access. -
- -
-
- {% endif %} - - -
- - - -
- - - -
-
- - -
- -

- Configure content safety, archiving, and user feedback settings. If Content Safety is enabled, user - messages will be sent to the safety endpoint for analysis. If User Feedback is enabled, users will see - thumbs up/down to provide feedback on AI responses. -

-
-
- Content Safety -
-

Enable content safety to filter out inappropriate content.

-
- - - -
- - -
- - -
- - - -
- - -
-
- - -
-
- - -
-
- -
- - -
-
-
- - -
-
- - -
-
- -
- - -
-
-
- - -
- - -
Displayed when Content Safety blocks a chat message.
-
- -
- - - -
-

Disable this option to show only the configured message.

- - -
-
-
- - -
-
- User Feedback -
-

Enable user feedback (thumbs up/down) for AI responses.

-
- - - -
-
- -
-
- Desktop Conversation Notifications -
-

- Allow users to receive an operating system notification when an AI response finishes while SimpleChat is open in a hidden or unfocused browser tab. -

-
- - -
-

- Users can turn notifications off from Profile. Browser permission is required, and notifications stop when the SimpleChat tab is closed. -

-
- - -
-
- Permissions -
-

Control which users can access specific administrative views related to safety and feedback.

- - -
- - - -
-

- Required app role value: SafetyViolationAdmin. Assign this role to users or groups in the Enterprise App before enabling the requirement. If disabled, any user with the general Admin app role can access the Safety Violations admin page. -

- - -
- - - -
-

- Required app role value: FeedbackAdmin. Assign this role to users or groups in the Enterprise App before enabling the requirement. If disabled, any user with the general Admin app role can access the User Feedback admin page. Requires Enable User Feedback to be active. -

-
- - - -
-
- Conversation Archiving -
-

When enabled, conversation deletions will be archived instead of permanently deleted.

-
- - - -
-
- -
- - -
- -

- Configure Azure AI Search, Document Intelligence, and multimedia support settings. -

- -
-
-
Web Search (Azure AI Foundry Agent)
- -
-

Enable web search by routing queries through an Azure AI Foundry agent configured by admins.

-
- - -
- - - -
- -
-
-
URL Access
- Shared Policy -
-

Control direct URL content fetching for pasted chat links, workflow prompts, and Deep Research source review.

-
- - -
-
- - -
Required app role value: UrlAccessUser. Assign this role to users or groups in the Enterprise App before enabling the requirement. When enabled, only assigned users can use URL Access in chat or enable it for workflows.
-
- -
-
- URL Access uses the same server-side URL protections as Deep Research: non-HTTP(S) URLs, credentialed URLs, literal IP targets, localhost, metadata hosts, unsafe redirects, unsupported content types, and oversized pages are blocked before fetch. -
-
-
- - -
Hard limit: 100 direct URLs per chat message.
-
-
- - -
Hard limit: 500 direct URLs per workflow prompt.
-
-
-
- -
-
-
- - -
- - -
-
-
-
Leave blank to allow any public domain that passes safety checks. Deep Research uses this same allow list.
-
-
-
- - -
- - -
-
-
-
Blocked domains apply to URL Access and Deep Research source-page review.
-
-
- -
-
-
-
URL Policy Test
-
Check a URL against the current allowed and blocked domain rules before saving.
-
- -
-
-
- -
-
-
Deep Research
-
- Optional - -
-
-

Plan bounded web searches, inspect source pages, and keep an internal research ledger for evidence coverage. Direct pasted URLs use the shared URL Access controls above.

-
- - -
- -
-
- Server-side protections always block non-HTTP(S) URLs, literal IP URL targets, localhost, metadata hosts, credentialed URLs, excessive redirects, unsupported content types, oversized pages, and requests beyond the configured page budget. DNS hostnames that resolve to private/internal addresses require the internal-host opt-in below. -
- -
- - -
Required app role value: DeepResearchUser. Assign this role to users or groups in the Enterprise App before enabling the requirement. When enabled, only assigned users can use Deep Research.
-
- -
- - -
Allows DNS hostnames that resolve to private/internal addresses. Literal IP URL targets, localhost, metadata hosts, link-local addresses, and reserved addresses remain blocked.
-
- -
-
- - - -
Deep Research runs only when the user selects it for the message.
-
-
- - -
Hard limit: 10 pages.
-
-
- - -
Limits initial search-result and direct URL pages so budget remains for child pages.
-
-
- - -
Direct URLs beyond this cap are recorded as omitted in the ledger.
-
-
- - -
Includes the original current-message query.
-
-
- - -
Hard limit: 30 seconds.
-
-
- - -
Every redirect target is revalidated.
-
-
- - -
Hard limit: 5 MB.
-
-
- - -
Depth 2 follows selected links from seed and child pages.
-
-
- -
-
- {% set js_runtime = source_review_runtime_capabilities or {} %} -
- - -
-
Deep Research follows only scored, policy-approved links within the page and depth budgets.
-
-
-
- - -
-
The selected chat model can propose bounded current-message-only query variants before page review.
-
-
-
- - -
-
Creates a Markdown chat artifact with search queries, reviewed sources, skipped URLs, and coverage.
-
-
-
- - -
-
When source pages expose candidate links, the selected chat model can rank those candidates before the server fetches them.
-
-
-
- - -
-
Requires verified Playwright browser runtime support on the app host.
-
- {{ js_runtime.message or 'Runtime support has not been checked yet.' }} -
- {% if not js_runtime.js_rendering_available %} -
Install/enable the Playwright Chromium runtime, then reload Admin Settings to enable this option.
- {% endif %} - {% if js_runtime.sandbox_disabled %} -
Chromium sandbox is disabled by environment configuration.
- {% endif %} -
-
- - -
When JavaScript rendering is enabled, Deep Research can click visible Load More controls until this cap is reached.
-
-
-
- - -
-
-
-
- - -
-
-
-
-
- - - - - - - - -
-
Azure AI Search
-

- Configure Azure AI Search settings. -

-
- - - -
- -
- -
-
- - -
-
- - -
-
- -
- - -
-
-
- - -
-
- - -
-
- -
- - -
-
-
- + {% include "admin/_panes/security.html" %} - -
-
-
- - -
-
Document Intelligence
-

- Configure Azure Document Intelligence settings. Document Intelligence always powers - Standard extraction and is required for workspaces and chat file uploads. -

-
- - - -
- -
- {% if content_understanding_supported %} -
- Enhanced extraction uses Azure AI Content Understanding, which returns tables, - page structure, checkbox states, and AI-generated descriptions of figures, charts, and images. - Configure the connection below. -
- {% else %} -
- Azure AI Content Understanding is not available in the - {{ azure_environment }} cloud, so Enhanced extraction uses - Document Intelligence Layout here. There is nothing more to configure — - Enhanced still captures tables, page structure, forms, and checkbox states. -
- {% endif %} - -
- - -
- Enhanced captures more document detail for PDFs and images, including tables, page structure, and checked or unchecked marks. It adds latency and has a 6X increase for every 1000 pages when selected. -
-
- - -
- Auto samples this many first PDF pages with Document Intelligence Layout. If it detects tables, selection marks, or figures, the full PDF uses Enhanced; otherwise it finishes with Standard. Images use Enhanced in Auto mode. -
-
-
-
Standard: Document Intelligence Read. Fastest and lowest-cost path for plain text PDFs and images.
-
Enhanced: best for tables, section structure, page layout, forms, checkbox states, and figure descriptions. Expect more latency and higher cost.
-
Auto: samples the first pages with Document Intelligence Layout, then uses Enhanced only when the sample shows tables, selection marks, or figures.
-
-
- -
- - - -
-
- Captures equations in PDFs and images as LaTeX rather than approximate OCR text. This is a - billed Document Intelligence add-on that adds per-page cost to every Enhanced - extraction, so it is off by default. It applies to the Layout model only, so it has no effect - while extraction is set to Standard. -
- - {% if content_understanding_supported %} -
-
-
Azure AI Content Understanding
- -
-

- Required for Enhanced extraction. Deployed from a Microsoft Foundry resource. - If this is left blank, Enhanced falls back to Document Intelligence Layout. -

- -
- - -
- Your Microsoft Foundry resource endpoint, without a trailing path. -
-
- -
- - -
- Managed identity requires the Cognitive Services User role on the Foundry resource. -
-
- -
- -
- - -
-
- -
-
- - -
Default: {{ content_understanding_api_version_default }}
-
-
- - -
Default: {{ content_understanding_document_analyzer_default }}
-
-
- - -
Default: {{ content_understanding_image_analyzer_default }}
-
-
- - -
-
- {% endif %} -
- -
-
Images Inside Office Files
-

- Neither extraction engine describes figures inside Word and PowerPoint files. When this is on, - embedded images are pulled out of the file, analyzed with whichever engine backs the selected - extraction mode, and indexed as their own citable chunks. This works with Standard extraction - too, using Document Intelligence. -

-
- - -
-
-
- - -
Images narrower or shorter than this are skipped as icons or spacers.
-
-
- - -
Caps per-document cost. Duplicate images are analyzed once.
-
-
-
- - - - -
- - - -
- -
- -
-
- - -
-
- - -
-
- -
- - -
-
-
- - -
-
- - -
-
- -
- - -
-
-
- - - -
-
-
- -
-
-
Chunk Sizes
- Cap: {{ chunk_size_cap }} -
-

Custom chunk sizes apply to new uploads only. Existing documents keep their current chunks.

-
- Heads up: Overrides are capped at {{ chunk_size_cap }} (2x embedding context window, fallback 16,384). -
-
- - -
- -
- - - {% set chunk_settings = chunk_size_settings or {} %} - {% set chunk_defaults = chunk_size_defaults or {} %} -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
Minimum enforced at 50% of target on merge.
-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
- -
- - -
Applies to new audio transcripts.
-
-
- - -
Pages per chunk after extraction.
-
-
- - -
Slides per chunk after extraction.
-
-
-
-
- - -
-
-
-
- -
-
-

- AI Video Intelligence -

-

Extract insights and make video content searchable and interactive

-
-
- -
- -
- - Make Video Content Fully Searchable: Upload videos to your workspace and automatically extract transcripts, identify speakers, detect topics, and generate searchable timestamps. Your AI can then answer questions about video content, cite specific moments, and provide deep insights. -
- - -
-
- - -
-

- Upload video files (MP4, AVI, MOV, etc.) to workspaces where Azure Video Indexer automatically processes them. - Extract spoken content, identify speakers, detect faces, recognize brands, and generate searchable metadata—perfect for meetings, presentations, training videos, and recorded content. -

-
- - Full-Text Search - - - Speaker ID - - - Timestamped Citations - - - Multi-Language - -
-
- - {% set public_video_indexer_endpoint = "https://api.videoindexer.ai" %} - {% set government_video_indexer_endpoint = "https://api.videoindexer.ai.azure.us" %} - {% set configured_video_indexer_endpoint = settings.video_indexer_endpoint or default_video_indexer_endpoint %} - {% if configured_video_indexer_endpoint == government_video_indexer_endpoint %} - {% set video_indexer_cloud = "usgovernment" %} - {% elif configured_video_indexer_endpoint == public_video_indexer_endpoint %} - {% set video_indexer_cloud = "public" %} - {% else %} - {% set video_indexer_cloud = "custom" %} - {% endif %} - {% if azure_environment == "usgovernment" %} - {% set azure_environment_label = "Azure Government" %} - {% elif azure_environment == "custom" %} - {% set azure_environment_label = "Custom Cloud" %} - {% else %} - {% set azure_environment_label = "Azure Public / Commercial" %} - {% endif %} - - -
-
- Azure Video Indexer Configuration -
-

Connect to your Azure Video Indexer resource for advanced video processing and content extraction.

- -
- Identity overview: -
    -
  • Video Indexer can use its own managed identity to reach the linked Storage account during account creation.
  • -
  • SimpleChat authenticates to Video Indexer with the App Service system-assigned managed identity.
  • -
  • The App Service managed identity must have Contributor on the Video Indexer resource.
  • -
  • See Azure Video Indexer documentation for setup details
  • -
-
- -
- - -
Choose the endpoint family that matches your deployed cloud. Use Custom only when you need a non-standard Video Indexer endpoint.
-
- -
- Current App Service environment: {{ azure_environment_label }}. This selector saves the Video Indexer API endpoint. If you are switching between Azure Public, Azure Government, or a custom cloud, update AZURE_ENVIRONMENT in your App Service configuration as well. -
- -
- The selected Video Indexer cloud does not match this app's current AZURE_ENVIRONMENT. Update the App Service configuration if you are moving to a different cloud. -
- - - -
- - -
Only required when using a custom cloud or non-standard Video Indexer endpoint.
-
- -
- - -
Saved endpoint value used for Video Indexer API calls.
-
- -
- - -
The Azure resource group containing your Video Indexer account
-
- -
- - -
Your Azure subscription ID
-
- -
- - -
The name of your Video Indexer account resource
-
- -
- - -
Azure region where your Video Indexer account is deployed (e.g., eastus, westus2, northeurope)
-
-
- - -
Found in the Video Indexer account Overview page in Azure Portal
-
+ {% include "admin/_panes/agents.html" %} -
- - -
Default for {{ azure_environment_label }}: {{ default_video_indexer_arm_api_version }}
-
-
- - -
-
-
+ + {% include "admin/_panes/governance.html" %} - -
-
-
-
- -
-
-

- AI Voice Conversations -

-

Transform your AI experience with natural voice interactions

-
-
- -
+ + {% include "admin/_panes/logging.html" %} - -
-
- - -
-

- Let users opt in to a bundled sound when an AI response finishes outside their active view. - These local cues do not require Azure Speech Service. -

-
+ {% include "admin/_panes/general.html" %} -
- - One Speech resource, three features: Audio file uploads, Voice Input, and Voice Responses all use the same Azure Speech Service section below. Configure the Speech resource once, then turn on whichever speech features you need. -
+ {% include "admin/_panes/latest-features.html" %} - -
- {% set audio_runtime = audio_runtime_capabilities or {} %} - {% set audio_supported_extensions = audio_runtime.supported_extensions or [] %} - {% set audio_direct_extensions = audio_runtime.direct_transcription_extensions or [] %} -
- - -
-

- Upload audio files (MP3, WAV, M4A, etc.) to workspaces where they're automatically transcribed and indexed. - The AI can then search, analyze, and answer questions about audio content—perfect for meetings, interviews, lectures, and podcasts. -

-
-
- {{ audio_runtime.message or 'Audio runtime support has not been checked yet.' }} -
-
- Supported audio upload extensions: {{ audio_supported_extensions|join(', ') }} -
- {% if audio_direct_extensions %} -
- Without FFmpeg, source-file fallback is best for: {{ audio_direct_extensions|join(', ') }}. Container builds can include FFmpeg for broader codec support. -
- {% endif %} -
-
+ {% include "admin/_panes/custom-pages.html" %} - -
-
- - -
-

- Talk to your AI instead of typing. Record voice messages up to 90 seconds directly in the chat interface. - Azure Speech Service instantly transcribes your speech with high accuracy, supporting multiple languages and accents. -

-
- - Hands-Free - - - Accessible - - - Fast Input - -
-
- -
-
- - -
-

- Hear AI responses read aloud in natural, human-like voices powered by Azure's advanced neural Text-to-Speech. - Each message includes a speaker button—click to listen while multitasking, commuting, or whenever reading isn't convenient. -

-
- - Natural Voices - - - Multitask-Friendly - - - Multi-Language - -
-
+ {% include "admin/_panes/ai-models.html" %} - -
-
- Azure Speech Service Configuration -
-

Configure the shared Azure Speech resource used by audio transcription, voice input, and text-to-speech.

+ + {% include "admin/_panes/control-center-config.html" %} -
- Shared configuration: These settings apply to all enabled speech features. If you use managed identity and also enable Voice Responses, fill in the Speech Resource ID field so the Speech SDK can authenticate synthesis requests. -
+ {% include "admin/_panes/data-management.html" %} -
- - -
Use the resource-specific custom-domain endpoint when selecting Managed Identity.
-
+ {% include "admin/_panes/scale.html" %} -
- - -
Required for speech recognition locale defaults and for text-to-speech when using Managed Identity.
-
-
-
- Resource ID builder: If the full ARM resource ID is hard to find in the portal, enter the subscription ID, resource group, and Speech resource name below. Simple Chat can build the resource ID for you. -
+ {% include "admin/_panes/workspace-identities.html" %} -
-
- - -
-
- - -
-
- - -
If you use a custom-domain Speech endpoint, this is usually the first part of that hostname.
-
-
- -
-
-
Provide Subscription ID, Resource Group, and Speech Resource Name to auto-build the ARM resource ID.
+ {% include "admin/_panes/file-sync.html" %} - - -
Required for Voice Responses when using Managed Identity. You can paste the full value manually, or let the fields above build it for you.
-
+ {% include "admin/_panes/workspaces.html" %} -
- - -
-
- - -
-
- -
- - -
-
Required only when Authentication Type is set to Key.
-
+ {% include "admin/_panes/citation.html" %} -
- Managed identity roles: Start with Cognitive Services Speech User. If fast transcription or other write-style speech operations still return authorization errors, add Cognitive Services Speech Contributor. Managed identity also requires a custom-domain endpoint. -
-
-

- - - Enhanced Citations - - will dramatically improve the citation experience for video and audio files. - -

-
-
+ {% include "admin/_panes/safety.html" %} -
-

- Prepare a prefilled email draft for bug reports or feature requests. This utility is separate from saved settings and records each submission intent in the activity log before opening your local email client. -

- -
-
-
-
- Report a Bug -
-

Something isn’t working as expected.

-
-
- - -
-
- - -
-
- - -
-
- - -
- - -
-
-
+ {% include "admin/_panes/search-extract.html" %} -
-
-
- Request a Feature -
-

Suggest an improvement or new capability.

-
-
- - -
-
- - -
-
- - -
-
- - -
- - -
-
-
-
-
+ {% include "admin/_panes/send-feedback.html" %}
diff --git a/functional_tests/test_admin_agent_default_model_migration.py b/functional_tests/test_admin_agent_default_model_migration.py index 99e99afc8..ead0a33fd 100644 --- a/functional_tests/test_admin_agent_default_model_migration.py +++ b/functional_tests/test_admin_agent_default_model_migration.py @@ -11,6 +11,7 @@ """ import os +from test_support.templates import compose_if_admin_settings def parse_version(version_text): @@ -25,7 +26,7 @@ def parse_version(version_text): def read_file_text(file_path): with open(file_path, "r", encoding="utf-8") as file: - return file.read() + return compose_if_admin_settings(file_path, file.read()) def test_admin_agent_default_model_migration_wiring(): diff --git a/functional_tests/test_admin_app_role_settings_consistency.py b/functional_tests/test_admin_app_role_settings_consistency.py index 698b3b79a..07d611b8f 100644 --- a/functional_tests/test_admin_app_role_settings_consistency.py +++ b/functional_tests/test_admin_app_role_settings_consistency.py @@ -17,6 +17,7 @@ import sys import traceback from test_support.versioning import assert_app_version_at_least +from test_support.templates import compose_if_admin_settings REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -92,7 +93,7 @@ def read_file(path): """Read a UTF-8 text file from the repo.""" with open(path, "r", encoding="utf-8") as file_handle: - return file_handle.read() + return compose_if_admin_settings(path, file_handle.read()) def read_current_version(): diff --git a/functional_tests/test_admin_document_action_capabilities_location.py b/functional_tests/test_admin_document_action_capabilities_location.py index 91d09914f..f7f586f2e 100644 --- a/functional_tests/test_admin_document_action_capabilities_location.py +++ b/functional_tests/test_admin_document_action_capabilities_location.py @@ -12,6 +12,7 @@ from pathlib import Path import traceback +from test_support.templates import resolve_template_includes from test_support.versioning import assert_app_version_at_least @@ -19,7 +20,14 @@ def read_text(relative_path: str) -> str: - return (ROOT / relative_path).read_text(encoding="utf-8") + path = ROOT / relative_path + content = path.read_text(encoding="utf-8") + + # Admin Settings is composed from per-tab partials, so structural + # assertions have to see the fully composed markup. + if path.name == "admin_settings.html": + return resolve_template_includes(content, path.parent) + return content def test_admin_document_action_capabilities_card_location() -> None: diff --git a/functional_tests/test_admin_global_item_enabled_state.py b/functional_tests/test_admin_global_item_enabled_state.py index 88fad4e7f..a6195d5a7 100644 --- a/functional_tests/test_admin_global_item_enabled_state.py +++ b/functional_tests/test_admin_global_item_enabled_state.py @@ -11,6 +11,7 @@ """ from pathlib import Path +from test_support.templates import read_admin_settings_template from test_support.versioning import assert_app_version_at_least @@ -38,7 +39,7 @@ def test_admin_global_item_enabled_state(): admin_plugins_js = ADMIN_PLUGINS_JS.read_text(encoding="utf-8") admin_agents_js = ADMIN_AGENTS_JS.read_text(encoding="utf-8") plugin_common_js = PLUGIN_COMMON_JS.read_text(encoding="utf-8") - admin_settings_template = ADMIN_SETTINGS_TEMPLATE.read_text(encoding="utf-8") + admin_settings_template = read_admin_settings_template() agent_schema = AGENT_SCHEMA_FILE.read_text(encoding="utf-8") plugin_schema = PLUGIN_SCHEMA_FILE.read_text(encoding="utf-8") diff --git a/functional_tests/test_admin_latest_features_tab.py b/functional_tests/test_admin_latest_features_tab.py index f3e311c69..9ccb83478 100644 --- a/functional_tests/test_admin_latest_features_tab.py +++ b/functional_tests/test_admin_latest_features_tab.py @@ -14,6 +14,10 @@ import os import sys +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +from test_support.templates import resolve_template_includes + CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) REPO_ROOT = os.path.abspath(os.path.join(CURRENT_DIR, '..')) @@ -131,7 +135,13 @@ def read_text(path): with open(path, 'r', encoding='utf-8') as file_handle: - return file_handle.read() + content = file_handle.read() + + # Admin Settings is composed from per-tab partials, so structural + # assertions have to see the fully composed markup. + if os.path.basename(str(path)) == 'admin_settings.html': + return resolve_template_includes(content, os.path.dirname(str(path))) + return content def load_module(path, module_name): diff --git a/functional_tests/test_admin_send_feedback_tab.py b/functional_tests/test_admin_send_feedback_tab.py index fafbd6290..7b06dc812 100644 --- a/functional_tests/test_admin_send_feedback_tab.py +++ b/functional_tests/test_admin_send_feedback_tab.py @@ -15,6 +15,10 @@ from bs4 import BeautifulSoup +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +from test_support.templates import resolve_template_includes + CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) REPO_ROOT = os.path.abspath(os.path.join(CURRENT_DIR, '..')) @@ -29,7 +33,13 @@ def read_text(path): with open(path, 'r', encoding='utf-8') as file_handle: - return file_handle.read() + content = file_handle.read() + + # Admin Settings is composed from per-tab partials, so structural + # assertions have to see the fully composed markup. + if os.path.basename(str(path)) == 'admin_settings.html': + return resolve_template_includes(content, os.path.dirname(str(path))) + return content def test_send_feedback_template_structure(): diff --git a/functional_tests/test_admin_settings_sidebar_card_parity.py b/functional_tests/test_admin_settings_sidebar_card_parity.py index d051c67cf..59854e349 100644 --- a/functional_tests/test_admin_settings_sidebar_card_parity.py +++ b/functional_tests/test_admin_settings_sidebar_card_parity.py @@ -15,6 +15,7 @@ from bs4 import BeautifulSoup +from test_support.templates import resolve_template_includes from test_support.versioning import assert_app_version_at_least @@ -88,7 +89,9 @@ def _card_title(card): def _navigation_contract(): """Parse the admin template, sidebar template, and section alias map.""" - admin_source = _read(ADMIN_TEMPLATE) + admin_source = resolve_template_includes( + _read(ADMIN_TEMPLATE), ADMIN_TEMPLATE.parent + ) sidebar_source = _read(SIDEBAR_TEMPLATE) script_source = _read(SIDEBAR_SCRIPT) admin_soup = BeautifulSoup(admin_source, "html.parser") diff --git a/functional_tests/test_admin_settings_template_composition.py b/functional_tests/test_admin_settings_template_composition.py new file mode 100644 index 000000000..342d210d0 --- /dev/null +++ b/functional_tests/test_admin_settings_template_composition.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python3 +# test_admin_settings_template_composition.py +""" +Functional test for the Admin Settings composed-template contract. +Version: 0.260.003 +Implemented in: 0.260.003 + +Admin Settings is assembled from per-tab partials under templates/admin/, so +reading admin_settings.html straight from disk only yields the parent shell. +This test pins that contract: + + 1. The parent template delegates its tab panes to partials. + 2. Every partial is reachable from the parent. + 3. The composed template still exposes every configuration card. + 4. No functional test reads admin_settings.html without composing it first, + which would silently assert against markup that is no longer there. +""" + +import re +import sys +from pathlib import Path + +from test_support.templates import ( + ADMIN_SETTINGS_TEMPLATE, + TEMPLATE_DIR, + read_admin_settings_template, + resolve_template_includes, +) +from test_support.versioning import assert_app_version_at_least + + +TESTS_DIR = Path(__file__).resolve().parent +PARTIAL_DIR = TEMPLATE_DIR / "admin" + +# Tests that legitimately reference the template path without reading markup, +# or that already compose it through the shared helpers. +COMPOSITION_HELPERS = ( + "read_admin_settings_template", + "compose_if_admin_settings", + "resolve_template_includes", + "read_composed_template", +) + + +def test_parent_template_delegates_panes_to_partials(): + """The parent template should include partials rather than inline panes.""" + print("Testing Admin Settings partial delegation...") + + assert_app_version_at_least("0.260.003") + parent = ADMIN_SETTINGS_TEMPLATE.read_text(encoding="utf-8") + + includes = re.findall(r'\{%\s*include\s+"(admin/[^"]+)"\s*%\}', parent) + assert includes, "Parent template should include Admin Settings partials" + + missing = [ + target for target in includes if not (TEMPLATE_DIR / target).is_file() + ] + assert not missing, f"Parent includes missing partials: {missing}" + + print(f"Parent delegates to {len(includes)} partial(s).") + + +def test_every_partial_is_reachable_from_the_parent(): + """Orphaned partials would silently drop settings from the page.""" + print("Testing Admin Settings partial reachability...") + + parent = ADMIN_SETTINGS_TEMPLATE.read_text(encoding="utf-8") + composed = read_admin_settings_template() + + referenced = set() + for target in re.findall(r'\{%\s*include\s+"(admin/[^"]+)"\s*%\}', parent): + referenced.add((TEMPLATE_DIR / target).resolve()) + for target in re.findall(r'\{%\s*include\s+"(admin/[^"]+)"\s*%\}', composed): + referenced.add((TEMPLATE_DIR / target).resolve()) + + on_disk = {path.resolve() for path in PARTIAL_DIR.rglob("*.html")} + orphans = sorted(path.name for path in on_disk - referenced) + + assert not orphans, f"Partials are never included: {orphans}" + print(f"All {len(on_disk)} partial(s) are reachable.") + + +def test_composed_template_exposes_configuration_cards(): + """Composition must restore the cards that live inside the partials.""" + print("Testing composed Admin Settings card visibility...") + + parent = ADMIN_SETTINGS_TEMPLATE.read_text(encoding="utf-8") + composed = read_admin_settings_template() + + card_pattern = re.compile(r'
]*\sid="([^"]+)"') + parent_cards = set(card_pattern.findall(parent)) + composed_cards = set(card_pattern.findall(composed)) + + assert len(composed_cards) > len(parent_cards), ( + "Composing the template should reveal cards held in partials " + f"(parent={len(parent_cards)}, composed={len(composed_cards)})" + ) + + # Spot-check cards that are known to live in partials. + for card_id in ("keyvault-section", "branding-section", "content-safety-section"): + assert card_id in composed_cards, ( + f"Composed template is missing '{card_id}'" + ) + + print(f"Composed template exposes {len(composed_cards)} card(s).") + + +def test_no_functional_test_reads_the_template_uncomposed(): + """Guard the convention so new tests do not assert on the parent shell. + + Only tests that actually reference a card living exclusively inside a + partial are flagged. Tests that assert on markup still held by the parent + (the nav, the form, the modals) are reading exactly what they expect. + """ + print("Testing Admin Settings read convention across functional tests...") + + parent = ADMIN_SETTINGS_TEMPLATE.read_text(encoding="utf-8") + composed = read_admin_settings_template() + + card_pattern = re.compile(r'
]*\sid="([^"]+)"') + parent_cards = set(card_pattern.findall(parent)) + partial_only_cards = set(card_pattern.findall(composed)) - parent_cards + assert partial_only_cards, "Expected some cards to live only in partials" + + offenders = [] + for path in sorted(TESTS_DIR.glob("test_*.py")): + if path.name == Path(__file__).name: + continue + + source = path.read_text(encoding="utf-8") + if "admin_settings.html" not in source: + continue + if any(helper in source for helper in COMPOSITION_HELPERS): + continue + + referenced = sorted( + card_id for card_id in partial_only_cards if card_id in source + ) + if referenced: + offenders.append(f"{path.name} -> {', '.join(referenced[:3])}") + + assert not offenders, ( + "These tests reference cards that live inside Admin Settings partials " + "but read admin_settings.html without composing it, so they assert " + "against an incomplete template. Use " + "test_support.templates.read_admin_settings_template() or " + "compose_if_admin_settings():\n " + "\n ".join(offenders) + ) + + print("All functional tests compose the template before asserting.") + + +if __name__ == "__main__": + tests = [ + test_parent_template_delegates_panes_to_partials, + test_every_partial_is_reachable_from_the_parent, + test_composed_template_exposes_configuration_cards, + test_no_functional_test_reads_the_template_uncomposed, + ] + + results = [] + for test in tests: + try: + test() + results.append(True) + except Exception as exc: + print(f"FAILED {test.__name__}: {exc}") + import traceback + traceback.print_exc() + results.append(False) + + print(f"\nResults: {sum(results)}/{len(results)} passed") + sys.exit(0 if all(results) else 1) diff --git a/functional_tests/test_admin_source_review_depth_save.py b/functional_tests/test_admin_source_review_depth_save.py index d7e74b15c..fa7a602cd 100644 --- a/functional_tests/test_admin_source_review_depth_save.py +++ b/functional_tests/test_admin_source_review_depth_save.py @@ -12,6 +12,7 @@ import os import sys +from test_support.templates import compose_if_admin_settings REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -27,7 +28,7 @@ def read_admin_template(): """Read the Admin Settings template for static regression checks.""" with open(ADMIN_TEMPLATE, "r", encoding="utf-8") as template_file: - return template_file.read() + return compose_if_admin_settings(ADMIN_TEMPLATE, template_file.read()) def test_source_review_depth_has_single_form_field(): diff --git a/functional_tests/test_agents_catalog_feature.py b/functional_tests/test_agents_catalog_feature.py index dc7282e9c..9c313714a 100644 --- a/functional_tests/test_agents_catalog_feature.py +++ b/functional_tests/test_agents_catalog_feature.py @@ -13,6 +13,7 @@ from pathlib import Path import sys from types import ModuleType, SimpleNamespace +from test_support.templates import compose_if_admin_settings REPO_ROOT = Path(__file__).resolve().parents[1] @@ -21,7 +22,10 @@ def read_repo_file(relative_path): - return (REPO_ROOT / relative_path).read_text(encoding="utf-8") + _path = REPO_ROOT / relative_path + return compose_if_admin_settings( + _path, _path.read_text(encoding="utf-8") + ) def assert_contains(text, expected, label): diff --git a/functional_tests/test_ai_models_tab_embedding_image_location.py b/functional_tests/test_ai_models_tab_embedding_image_location.py index 21f60c9e0..c1878cce6 100644 --- a/functional_tests/test_ai_models_tab_embedding_image_location.py +++ b/functional_tests/test_ai_models_tab_embedding_image_location.py @@ -10,11 +10,12 @@ """ import os +from test_support.templates import compose_if_admin_settings def read_file_text(file_path): - with open(file_path, 'r', encoding='utf-8') as file: - return file.read() + with open(file_path, "r", encoding="utf-8") as file: + return compose_if_admin_settings(file_path, file.read()) def test_ai_models_tab_embedding_image_location(): diff --git a/functional_tests/test_audio_ffmpeg_fallback.py b/functional_tests/test_audio_ffmpeg_fallback.py index 400e0b795..b27dbb692 100644 --- a/functional_tests/test_audio_ffmpeg_fallback.py +++ b/functional_tests/test_audio_ffmpeg_fallback.py @@ -17,6 +17,7 @@ import shutil import subprocess import sys +from test_support.templates import compose_if_admin_settings ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -31,8 +32,8 @@ def read_file(path): - with open(path, 'r', encoding='utf-8') as file_handle: - return file_handle.read() + with open(path, "r", encoding="utf-8") as file_handle: + return compose_if_admin_settings(path, file_handle.read()) def parse_functions_documents(): diff --git a/functional_tests/test_azure_openai_identity_split.py b/functional_tests/test_azure_openai_identity_split.py index 380a3a966..e182fd30c 100644 --- a/functional_tests/test_azure_openai_identity_split.py +++ b/functional_tests/test_azure_openai_identity_split.py @@ -13,6 +13,7 @@ from pathlib import Path import re import sys +from test_support.templates import compose_if_admin_settings REPO_ROOT = Path(__file__).resolve().parents[1] @@ -20,7 +21,10 @@ def read_workspace_file(relative_path): """Read a workspace file as UTF-8 text.""" - return (REPO_ROOT / relative_path).read_text(encoding="utf-8") + _path = REPO_ROOT / relative_path + return compose_if_admin_settings( + _path, _path.read_text(encoding="utf-8") + ) def test_backend_and_admin_guidance_identity_split(): diff --git a/functional_tests/test_chat_completion_audio_cues.py b/functional_tests/test_chat_completion_audio_cues.py index 966c4d562..9daea0dfa 100644 --- a/functional_tests/test_chat_completion_audio_cues.py +++ b/functional_tests/test_chat_completion_audio_cues.py @@ -15,6 +15,7 @@ import sys import wave from pathlib import Path +from test_support.templates import compose_if_admin_settings ROOT_DIR = Path(__file__).resolve().parents[1] @@ -49,7 +50,9 @@ def read_text(path): """Read a UTF-8 source file.""" - return path.read_text(encoding="utf-8") + return compose_if_admin_settings( + path, path.read_text(encoding="utf-8") + ) def test_local_audio_assets(): diff --git a/functional_tests/test_chat_file_upload_access_control.py b/functional_tests/test_chat_file_upload_access_control.py index 06f3139e4..0da714ea6 100644 --- a/functional_tests/test_chat_file_upload_access_control.py +++ b/functional_tests/test_chat_file_upload_access_control.py @@ -15,6 +15,7 @@ import re import sys import traceback +from test_support.templates import compose_if_admin_settings REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -51,7 +52,7 @@ def read_file(path): """Read a UTF-8 text file from the repo.""" with open(path, "r", encoding="utf-8") as file_handle: - return file_handle.read() + return compose_if_admin_settings(path, file_handle.read()) def read_version(): diff --git a/functional_tests/test_content_safety_violation_message.py b/functional_tests/test_content_safety_violation_message.py index d16ba8165..46ed92922 100644 --- a/functional_tests/test_content_safety_violation_message.py +++ b/functional_tests/test_content_safety_violation_message.py @@ -23,6 +23,8 @@ ADMIN_SETTINGS_TEMPLATE_PATH = Path(APPLICATION_PATH) / 'templates' / 'admin_settings.html' sys.path.insert(0, APPLICATION_PATH) +from test_support.templates import read_admin_settings_template + from functions_content_safety import ( CONTENT_SAFETY_VIOLATION_MESSAGE_DEFAULT, CONTENT_SAFETY_VIOLATION_MESSAGE_MAX_LENGTH, @@ -59,7 +61,7 @@ def test_message_normalization_preserves_safe_admin_defaults(): def test_admin_settings_uses_the_markdown_editor_toolbar(): """The Content Safety message field uses the standard local Markdown toolbar.""" - admin_settings_template = ADMIN_SETTINGS_TEMPLATE_PATH.read_text(encoding='utf-8') + admin_settings_template = read_admin_settings_template() assert '' in admin_settings_template assert 'id="content_safety_violation_message"' in admin_settings_template diff --git a/functional_tests/test_content_understanding_extraction_engine.py b/functional_tests/test_content_understanding_extraction_engine.py index 28246e0a4..3b6b5c706 100644 --- a/functional_tests/test_content_understanding_extraction_engine.py +++ b/functional_tests/test_content_understanding_extraction_engine.py @@ -22,6 +22,7 @@ sys.path.insert(0, str(Path(__file__).resolve().parent)) from test_support.versioning import assert_app_version_at_least # noqa: E402 +from test_support.templates import compose_if_admin_settings def load_content_understanding_module(azure_environment="public"): @@ -305,7 +306,10 @@ def test_government_cloud_blocks_content_understanding(): def read_repo_file(relative_path): """Read a repository file as UTF-8 text.""" - return (REPO_ROOT / relative_path).read_text(encoding="utf-8") + _path = REPO_ROOT / relative_path + return compose_if_admin_settings( + _path, _path.read_text(encoding="utf-8") + ) def assert_contains(content, expected_text, description): diff --git a/functional_tests/test_control_center_auto_refresh_schedule.py b/functional_tests/test_control_center_auto_refresh_schedule.py index bbe47b5d3..c4497c601 100644 --- a/functional_tests/test_control_center_auto_refresh_schedule.py +++ b/functional_tests/test_control_center_auto_refresh_schedule.py @@ -17,6 +17,8 @@ from datetime import datetime, timezone from pathlib import Path +from test_support.templates import read_admin_settings_template + ROOT_DIR = Path(__file__).resolve().parents[1] APP_DIR = ROOT_DIR / "application" / "single_app" @@ -165,7 +167,7 @@ def test_integration_wiring(): background_source = BACKGROUND_TASKS_FILE.read_text(encoding="utf-8") admin_route_source = ADMIN_SETTINGS_ROUTE_FILE.read_text(encoding="utf-8") control_center_route_source = CONTROL_CENTER_ROUTE_FILE.read_text(encoding="utf-8") - template_source = ADMIN_TEMPLATE_FILE.read_text(encoding="utf-8") + template_source = read_admin_settings_template() javascript_source = CONTROL_CENTER_JS_FILE.read_text(encoding="utf-8") expected_settings = [ diff --git a/functional_tests/test_conversation_contents_drawer_settings.py b/functional_tests/test_conversation_contents_drawer_settings.py index 200b00b80..70be7c0f9 100644 --- a/functional_tests/test_conversation_contents_drawer_settings.py +++ b/functional_tests/test_conversation_contents_drawer_settings.py @@ -13,6 +13,7 @@ from pathlib import Path from flask import Flask, jsonify, request +from test_support.templates import compose_if_admin_settings REPO_ROOT = Path(__file__).resolve().parents[1] @@ -27,7 +28,10 @@ def _read(relative_path): - return (REPO_ROOT / relative_path).read_text(encoding="utf-8") + _path = REPO_ROOT / relative_path + return compose_if_admin_settings( + _path, _path.read_text(encoding="utf-8") + ) def test_admin_setting_defaults_on_and_persists(): diff --git a/functional_tests/test_cosmos_wave4a1_admin_document_access_ui.py b/functional_tests/test_cosmos_wave4a1_admin_document_access_ui.py index 588107e6d..fb8a5e6ae 100644 --- a/functional_tests/test_cosmos_wave4a1_admin_document_access_ui.py +++ b/functional_tests/test_cosmos_wave4a1_admin_document_access_ui.py @@ -20,6 +20,7 @@ import os import sys from test_support.versioning import assert_app_version_at_least +from test_support.templates import compose_if_admin_settings ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -27,8 +28,9 @@ def _read(relative_path): - with open(os.path.join(ROOT_DIR, relative_path), "r", encoding="utf-8") as file: - return file.read() + _path = os.path.join(ROOT_DIR, relative_path) + with open(_path, "r", encoding="utf-8") as file: + return compose_if_admin_settings(_path, file.read()) def test_admin_template_exposes_safe_document_access_controls(): diff --git a/functional_tests/test_custom_pages_wiring.py b/functional_tests/test_custom_pages_wiring.py index 702be5e97..d44ac9fc7 100644 --- a/functional_tests/test_custom_pages_wiring.py +++ b/functional_tests/test_custom_pages_wiring.py @@ -12,6 +12,7 @@ from pathlib import Path import sys +from test_support.templates import compose_if_admin_settings REPO_ROOT = Path(__file__).resolve().parents[1] @@ -20,7 +21,10 @@ def read_text(relative_path): """Read a repository file as UTF-8 text.""" - return (REPO_ROOT / relative_path).read_text(encoding="utf-8") + _path = REPO_ROOT / relative_path + return compose_if_admin_settings( + _path, _path.read_text(encoding="utf-8") + ) def assert_contains(content, expected, description): diff --git a/functional_tests/test_data_management_history_pagination.py b/functional_tests/test_data_management_history_pagination.py index 9233477e4..7fe626979 100644 --- a/functional_tests/test_data_management_history_pagination.py +++ b/functional_tests/test_data_management_history_pagination.py @@ -35,6 +35,7 @@ sys.path.insert(0, str(Path(__file__).resolve().parent)) from test_support.versioning import compare_simplechat_versions +from test_support.templates import read_admin_settings_template class FakeHistoryContainer: @@ -823,9 +824,7 @@ def test_deployers_apply_the_data_management_history_index(): def test_retention_cleanup_button_explains_what_it_does(): """Give admins hover and expandable guidance before deleting expired backups.""" - template = ( - APP_ROOT / "templates" / "admin_settings.html" - ).read_text(encoding="utf-8") + template = read_admin_settings_template() cleanup_button_start = template.index("data-management-run-retention-cleanup-btn") cleanup_button = template[cleanup_button_start:cleanup_button_start + 600] diff --git a/functional_tests/test_data_management_security_patterns.py b/functional_tests/test_data_management_security_patterns.py index 9df41f807..682d8aa90 100644 --- a/functional_tests/test_data_management_security_patterns.py +++ b/functional_tests/test_data_management_security_patterns.py @@ -34,6 +34,7 @@ import re from pathlib import Path from test_support.versioning import assert_app_version_at_least +from test_support.templates import compose_if_admin_settings REPO_ROOT = Path(__file__).resolve().parents[1] @@ -50,7 +51,9 @@ def read_text(path): - return path.read_text(encoding="utf-8") + return compose_if_admin_settings( + path, path.read_text(encoding="utf-8") + ) def route_functions_with_decorators(): diff --git a/functional_tests/test_deep_research_explicit_toggle.py b/functional_tests/test_deep_research_explicit_toggle.py index 9476e424f..d88dedbca 100644 --- a/functional_tests/test_deep_research_explicit_toggle.py +++ b/functional_tests/test_deep_research_explicit_toggle.py @@ -22,12 +22,13 @@ sys.path.insert(0, APP_ROOT) from functions_source_review import get_source_review_config, should_auto_enable_source_review # noqa: E402 +from test_support.templates import compose_if_admin_settings def read_text(path): """Read text content for static regression checks.""" with open(path, "r", encoding="utf-8") as file_handle: - return file_handle.read() + return compose_if_admin_settings(path, file_handle.read()) def test_web_search_does_not_auto_enable_deep_research(): diff --git a/functional_tests/test_default_model_selection_fallback.py b/functional_tests/test_default_model_selection_fallback.py index 76ec94002..9faa7a0f1 100644 --- a/functional_tests/test_default_model_selection_fallback.py +++ b/functional_tests/test_default_model_selection_fallback.py @@ -12,11 +12,12 @@ """ import os +from test_support.templates import compose_if_admin_settings def read_file_text(file_path): with open(file_path, "r", encoding="utf-8") as file: - return file.read() + return compose_if_admin_settings(file_path, file.read()) def test_default_model_selection_wiring(): diff --git a/functional_tests/test_desktop_notification_settings.py b/functional_tests/test_desktop_notification_settings.py index df21c9374..0b2085673 100644 --- a/functional_tests/test_desktop_notification_settings.py +++ b/functional_tests/test_desktop_notification_settings.py @@ -9,13 +9,17 @@ """ from pathlib import Path +from test_support.templates import compose_if_admin_settings REPO_ROOT = Path(__file__).resolve().parents[1] def _read(relative_path): - return (REPO_ROOT / relative_path).read_text(encoding="utf-8") + _path = REPO_ROOT / relative_path + return compose_if_admin_settings( + _path, _path.read_text(encoding="utf-8") + ) def test_desktop_notification_settings_are_wired_end_to_end(): diff --git a/functional_tests/test_document_action_capability_settings.py b/functional_tests/test_document_action_capability_settings.py index 8ff9114f9..d8ba9e6d8 100644 --- a/functional_tests/test_document_action_capability_settings.py +++ b/functional_tests/test_document_action_capability_settings.py @@ -12,13 +12,17 @@ from pathlib import Path from test_support.versioning import assert_app_version_at_least +from test_support.templates import compose_if_admin_settings ROOT = Path(__file__).resolve().parents[1] def read_text(relative_path: str) -> str: - return (ROOT / relative_path).read_text(encoding="utf-8") + _path = ROOT / relative_path + return compose_if_admin_settings( + _path, _path.read_text(encoding="utf-8") + ) def test_document_action_capability_settings_wiring() -> None: diff --git a/functional_tests/test_document_intelligence_pdf_image_extraction_mode.py b/functional_tests/test_document_intelligence_pdf_image_extraction_mode.py index 155f66c29..abfe5ad61 100644 --- a/functional_tests/test_document_intelligence_pdf_image_extraction_mode.py +++ b/functional_tests/test_document_intelligence_pdf_image_extraction_mode.py @@ -16,6 +16,7 @@ import sys from pathlib import Path +from test_support.templates import compose_if_admin_settings REPO_ROOT = Path(__file__).resolve().parents[1] @@ -23,7 +24,10 @@ def read_repo_file(relative_path): """Read a repository file as UTF-8 text.""" - return (REPO_ROOT / relative_path).read_text(encoding="utf-8") + _path = REPO_ROOT / relative_path + return compose_if_admin_settings( + _path, _path.read_text(encoding="utf-8") + ) def assert_contains(content, expected_text, description): diff --git a/functional_tests/test_endpoints_tab_order_visibility.py b/functional_tests/test_endpoints_tab_order_visibility.py index 90eed4254..aa653af6c 100644 --- a/functional_tests/test_endpoints_tab_order_visibility.py +++ b/functional_tests/test_endpoints_tab_order_visibility.py @@ -19,6 +19,7 @@ """ import os +from test_support.templates import compose_if_admin_settings REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) @@ -30,7 +31,7 @@ def read_file_text(file_path): with open(file_path, "r", encoding="utf-8") as file: - return file.read() + return compose_if_admin_settings(file_path, file.read()) def test_workspace_endpoints_tab_order_visibility(): diff --git a/functional_tests/test_enhanced_citations_startup_storage_degradation.py b/functional_tests/test_enhanced_citations_startup_storage_degradation.py index 21b585b8c..29d6dff54 100644 --- a/functional_tests/test_enhanced_citations_startup_storage_degradation.py +++ b/functional_tests/test_enhanced_citations_startup_storage_degradation.py @@ -14,6 +14,7 @@ from pathlib import Path from test_support.versioning import assert_app_version_at_least +from test_support.templates import compose_if_admin_settings REPO_ROOT = Path(__file__).resolve().parents[1] @@ -27,7 +28,9 @@ def read_text(path): """Read a repository file as UTF-8 text.""" - return path.read_text(encoding="utf-8") + return compose_if_admin_settings( + path, path.read_text(encoding="utf-8") + ) def get_function_source(path, function_name): diff --git a/functional_tests/test_file_sync_azure_blob_storage.py b/functional_tests/test_file_sync_azure_blob_storage.py index 45255d435..67ffd15be 100644 --- a/functional_tests/test_file_sync_azure_blob_storage.py +++ b/functional_tests/test_file_sync_azure_blob_storage.py @@ -27,6 +27,7 @@ from typing import Any, Dict, List, Optional, Tuple from urllib.parse import parse_qsl, quote, unquote, urlparse from test_support.versioning import assert_app_version_at_least +from test_support.templates import compose_if_admin_settings REPO_ROOT = Path(__file__).resolve().parents[1] @@ -34,7 +35,10 @@ def read_text(relative_path): """Read a repository file as UTF-8 text.""" - return (REPO_ROOT / relative_path).read_text(encoding="utf-8") + _path = REPO_ROOT / relative_path + return compose_if_admin_settings( + _path, _path.read_text(encoding="utf-8") + ) def parse_app(relative_path): diff --git a/functional_tests/test_file_sync_azure_files_identity.py b/functional_tests/test_file_sync_azure_files_identity.py index e3af89e90..b7fbc7398 100644 --- a/functional_tests/test_file_sync_azure_files_identity.py +++ b/functional_tests/test_file_sync_azure_files_identity.py @@ -19,6 +19,7 @@ import sys from pathlib import Path from test_support.versioning import assert_app_version_at_least +from test_support.templates import compose_if_admin_settings REPO_ROOT = Path(__file__).resolve().parents[1] @@ -27,7 +28,10 @@ def read_text(relative_path): """Read a repository file as UTF-8 text.""" - return (REPO_ROOT / relative_path).read_text(encoding="utf-8") + _path = REPO_ROOT / relative_path + return compose_if_admin_settings( + _path, _path.read_text(encoding="utf-8") + ) def parse_app(relative_path): diff --git a/functional_tests/test_file_sync_capability.py b/functional_tests/test_file_sync_capability.py index fc3fd1321..86644fd9c 100644 --- a/functional_tests/test_file_sync_capability.py +++ b/functional_tests/test_file_sync_capability.py @@ -15,6 +15,7 @@ import sys from pathlib import Path from test_support.versioning import assert_app_version_at_least +from test_support.templates import compose_if_admin_settings REPO_ROOT = Path(__file__).resolve().parents[1] @@ -22,7 +23,10 @@ def read_text(relative_path): - return (REPO_ROOT / relative_path).read_text(encoding="utf-8") + _path = REPO_ROOT / relative_path + return compose_if_admin_settings( + _path, _path.read_text(encoding="utf-8") + ) def test_config_version_and_containers(): diff --git a/functional_tests/test_file_sync_onedrive_personal.py b/functional_tests/test_file_sync_onedrive_personal.py index bdf0a8f9a..828ceeccc 100644 --- a/functional_tests/test_file_sync_onedrive_personal.py +++ b/functional_tests/test_file_sync_onedrive_personal.py @@ -19,6 +19,7 @@ import sys from pathlib import Path from test_support.versioning import assert_app_version_at_least +from test_support.templates import compose_if_admin_settings REPO_ROOT = Path(__file__).resolve().parents[1] @@ -26,7 +27,10 @@ def read_text(relative_path): """Read a repository file as UTF-8 text.""" - return (REPO_ROOT / relative_path).read_text(encoding="utf-8") + _path = REPO_ROOT / relative_path + return compose_if_admin_settings( + _path, _path.read_text(encoding="utf-8") + ) def parse_app(relative_path): diff --git a/functional_tests/test_governance_admin_scope_toggle_visibility.py b/functional_tests/test_governance_admin_scope_toggle_visibility.py index 9e1547430..d0647a527 100644 --- a/functional_tests/test_governance_admin_scope_toggle_visibility.py +++ b/functional_tests/test_governance_admin_scope_toggle_visibility.py @@ -11,6 +11,7 @@ import os import sys +from test_support.templates import compose_if_admin_settings ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -19,7 +20,7 @@ def _read(*parts): path = os.path.join(ROOT_DIR, *parts) with open(path, "r", encoding="utf-8") as handle: - return handle.read() + return compose_if_admin_settings(path, handle.read()) GOVERNANCE_SCOPE_CONTROLS = { diff --git a/functional_tests/test_governance_enforcement_logic.py b/functional_tests/test_governance_enforcement_logic.py index 8f7c88686..0b826e492 100644 --- a/functional_tests/test_governance_enforcement_logic.py +++ b/functional_tests/test_governance_enforcement_logic.py @@ -53,6 +53,7 @@ def delete_item(self, item, partition_key): sys.modules.setdefault("functions_public_workspaces", functions_public_workspaces_stub) import functions_governance as governance +from test_support.templates import compose_if_admin_settings ROOT_DIR = os.path.dirname(CURRENT_DIR) @@ -61,7 +62,7 @@ def delete_item(self, item, partition_key): def _read_repo_file(*parts): path = os.path.join(ROOT_DIR, *parts) with open(path, "r", encoding="utf-8") as handle: - return handle.read() + return compose_if_admin_settings(path, handle.read()) def test_ensure_governance_access_allows_when_feature_toggle_disabled(): diff --git a/functional_tests/test_governance_route_and_wiring_coverage.py b/functional_tests/test_governance_route_and_wiring_coverage.py index c784ba466..a10b08ac6 100644 --- a/functional_tests/test_governance_route_and_wiring_coverage.py +++ b/functional_tests/test_governance_route_and_wiring_coverage.py @@ -12,6 +12,7 @@ import os import sys +from test_support.templates import compose_if_admin_settings ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -23,7 +24,7 @@ def _read(*parts): path = os.path.join(ROOT_DIR, *parts) with open(path, "r", encoding="utf-8") as handle: - return handle.read() + return compose_if_admin_settings(path, handle.read()) def test_governance_route_registration_and_guards(): diff --git a/functional_tests/test_idle_logout_timeout.py b/functional_tests/test_idle_logout_timeout.py index 4dda5e7f8..10d388baf 100644 --- a/functional_tests/test_idle_logout_timeout.py +++ b/functional_tests/test_idle_logout_timeout.py @@ -14,6 +14,8 @@ import ast import traceback +from test_support.templates import compose_if_admin_settings + sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -24,7 +26,7 @@ def _read_file(*path_parts): *path_parts ) with open(file_path, 'r', encoding='utf-8') as file_handle: - return file_handle.read() + return compose_if_admin_settings(file_path, file_handle.read()) def _parse_python_file(*path_parts): diff --git a/functional_tests/test_inbound_mcp_admin_ui.py b/functional_tests/test_inbound_mcp_admin_ui.py index 6f7bfaaa2..2717e5751 100644 --- a/functional_tests/test_inbound_mcp_admin_ui.py +++ b/functional_tests/test_inbound_mcp_admin_ui.py @@ -39,6 +39,7 @@ import sys from pathlib import Path from test_support.versioning import assert_app_version_at_least +from test_support.templates import compose_if_admin_settings ROOT_DIR = Path(__file__).resolve().parents[1] @@ -46,7 +47,10 @@ def read_repo_file(relative_path): """Read a repository file for source-level contract validation.""" - return (ROOT_DIR / relative_path).read_text(encoding="utf-8") + _path = ROOT_DIR / relative_path + return compose_if_admin_settings( + _path, _path.read_text(encoding="utf-8") + ) def test_inbound_mcp_runtime_settings_are_app_settings(): diff --git a/functional_tests/test_latest_features_action_links.py b/functional_tests/test_latest_features_action_links.py index 3019b1575..0e8807a6b 100644 --- a/functional_tests/test_latest_features_action_links.py +++ b/functional_tests/test_latest_features_action_links.py @@ -13,6 +13,7 @@ from pathlib import Path import sys from test_support.versioning import assert_app_version_at_least +from test_support.templates import compose_if_admin_settings REPO_ROOT = Path(__file__).resolve().parents[1] @@ -29,7 +30,9 @@ def read_text(path: Path) -> str: - return path.read_text(encoding="utf-8") + return compose_if_admin_settings( + path, path.read_text(encoding="utf-8") + ) def assert_markers(file_path: Path, markers: list[str]) -> None: diff --git a/functional_tests/test_multimedia_support_reorganization.py b/functional_tests/test_multimedia_support_reorganization.py index 68bda79ea..93e96dc0d 100644 --- a/functional_tests/test_multimedia_support_reorganization.py +++ b/functional_tests/test_multimedia_support_reorganization.py @@ -15,6 +15,8 @@ import os import sys +from test_support.templates import compose_if_admin_settings + sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -29,8 +31,9 @@ def test_multimedia_support_move(): ) with open(admin_settings_path, 'r', encoding='utf-8') as file_handle: - content = file_handle.read() - + content = compose_if_admin_settings( + admin_settings_path, file_handle.read() + ) search_extract_section = content.find('id="search-extract" role="tabpanel"') multimedia_support_section = content.find('id="video-intelligence-section"') diff --git a/functional_tests/test_multimodal_vision_multi_endpoint_connection.py b/functional_tests/test_multimodal_vision_multi_endpoint_connection.py index 526599f7b..58b2294d7 100644 --- a/functional_tests/test_multimodal_vision_multi_endpoint_connection.py +++ b/functional_tests/test_multimodal_vision_multi_endpoint_connection.py @@ -13,6 +13,8 @@ import sys from pathlib import Path +from test_support.templates import read_admin_settings_template + REPO_ROOT = Path(__file__).resolve().parents[1] ADMIN_SETTINGS_JS = REPO_ROOT / "application" / "single_app" / "static" / "js" / "admin" / "admin_settings.js" @@ -31,7 +33,7 @@ def test_multimodal_vision_multi_endpoint_wiring(): print("Testing multi-endpoint Vision test connection wiring...") js_content = ADMIN_SETTINGS_JS.read_text(encoding="utf-8") - template_content = ADMIN_SETTINGS_TEMPLATE.read_text(encoding="utf-8") + template_content = read_admin_settings_template() backend_content = ROUTE_BACKEND_SETTINGS.read_text(encoding="utf-8") assert_contains(js_content, "opt.dataset.endpointId = ep.id || '';", "vision endpoint id option metadata") diff --git a/functional_tests/test_personal_workflows_feature.py b/functional_tests/test_personal_workflows_feature.py index 43044feb8..e722495b4 100644 --- a/functional_tests/test_personal_workflows_feature.py +++ b/functional_tests/test_personal_workflows_feature.py @@ -10,13 +10,17 @@ from pathlib import Path from test_support.versioning import assert_app_version_at_least +from test_support.templates import compose_if_admin_settings ROOT = Path(__file__).resolve().parents[1] def read_text(relative_path): - return (ROOT / relative_path).read_text(encoding="utf-8") + _path = ROOT / relative_path + return compose_if_admin_settings( + _path, _path.read_text(encoding="utf-8") + ) def test_personal_workflows_feature_wiring(): diff --git a/functional_tests/test_public_workspace_display_name_settings.py b/functional_tests/test_public_workspace_display_name_settings.py index e36ef529d..f8fc46824 100644 --- a/functional_tests/test_public_workspace_display_name_settings.py +++ b/functional_tests/test_public_workspace_display_name_settings.py @@ -16,6 +16,7 @@ from types import SimpleNamespace from jinja2 import Environment +from test_support.templates import compose_if_admin_settings sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -29,7 +30,7 @@ def read_source(path): with open(path, "r", encoding="utf-8") as source_file: - return source_file.read() + return compose_if_admin_settings(path, source_file.read()) def assert_contains(source, needle, description): diff --git a/functional_tests/test_support/templates.py b/functional_tests/test_support/templates.py new file mode 100644 index 000000000..3b88eea8c --- /dev/null +++ b/functional_tests/test_support/templates.py @@ -0,0 +1,96 @@ +# templates.py +"""Shared helpers for reading composed SimpleChat templates in functional tests. + +Admin Settings is assembled from per-tab partials under +``templates/admin/``. Any test that asserts on the rendered structure has to +read the fully composed markup instead of the parent template alone, so these +helpers inline ``{% include %}`` directives before the markup is parsed. +""" + +import re +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] +TEMPLATE_DIR = REPO_ROOT / "application" / "single_app" / "templates" +ADMIN_SETTINGS_TEMPLATE = TEMPLATE_DIR / "admin_settings.html" + +INCLUDE_PATTERN = re.compile( + r'^(?P[ \t]*)\{%-?\s*include\s+["\'](?P[^"\']+)["\'].*?%\}', + flags=re.MULTILINE, +) + +# Only the Admin Settings tab partials are inlined by default. Unrelated +# includes (info modals and shared widgets) carry their own tab markup, and +# pulling them in would silently widen assertions that are meant to describe +# the Admin Settings panes alone. +DEFAULT_INCLUDE_PREFIX = "admin/" + + +def resolve_template_includes( + source, + template_dir=None, + include_prefix=DEFAULT_INCLUDE_PREFIX, + _seen=None, +): + """Return ``source`` with resolvable ``{% include %}`` directives inlined. + + Only includes whose target starts with ``include_prefix`` are expanded; + pass ``include_prefix=""`` to inline every resolvable include. Includes + that cannot be found on disk, and includes that would recurse into a + template already being expanded, are left untouched so the original markup + is still visible to the caller. + """ + directory = Path(template_dir) if template_dir else TEMPLATE_DIR + seen = set() if _seen is None else _seen + + def _replace(match): + target = match.group("target") + partial = directory / target + if include_prefix and not target.startswith(include_prefix): + return match.group(0) + if target in seen or not partial.is_file(): + return match.group(0) + + nested = resolve_template_includes( + partial.read_text(encoding="utf-8"), + directory, + include_prefix, + seen | {target}, + ) + indent = match.group("indent") + return "\n".join( + f"{indent}{line}" if line.strip() else line + for line in nested.split("\n") + ) + + return INCLUDE_PATTERN.sub(_replace, source) + + +def read_composed_template(path, template_dir=None, include_prefix=DEFAULT_INCLUDE_PREFIX): + """Read a template file with its Admin Settings includes inlined.""" + template_path = Path(path) + directory = Path(template_dir) if template_dir else template_path.parent + return resolve_template_includes( + template_path.read_text(encoding="utf-8"), + directory, + include_prefix, + ) + + +def compose_if_admin_settings(path, content): + """Inline Admin Settings partials when ``path`` is the admin template. + + Functional tests read many repository files through a single helper. Only + admin_settings.html is composed from partials, so this leaves every other + file untouched and keeps those tests reading exactly what is on disk. + """ + template_path = Path(path) + if template_path.name != "admin_settings.html": + return content + return resolve_template_includes(content, template_path.parent) + + +def read_admin_settings_template(): + """Read the fully composed Admin Settings template.""" + return read_composed_template(ADMIN_SETTINGS_TEMPLATE, TEMPLATE_DIR) diff --git a/functional_tests/test_support_menu_user_feature.py b/functional_tests/test_support_menu_user_feature.py index 0ea36578a..236a26d0e 100644 --- a/functional_tests/test_support_menu_user_feature.py +++ b/functional_tests/test_support_menu_user_feature.py @@ -15,6 +15,7 @@ import os import sys import importlib.util +from test_support.templates import compose_if_admin_settings CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) @@ -39,8 +40,8 @@ def read_text(path): - with open(path, 'r', encoding='utf-8') as file_handle: - return file_handle.read() + with open(path, "r", encoding="utf-8") as file_handle: + return compose_if_admin_settings(path, file_handle.read()) def load_module(path, module_name): diff --git a/functional_tests/test_tabular_processing_enhanced_citations_dependency.py b/functional_tests/test_tabular_processing_enhanced_citations_dependency.py index 7762d8565..7a94e262c 100644 --- a/functional_tests/test_tabular_processing_enhanced_citations_dependency.py +++ b/functional_tests/test_tabular_processing_enhanced_citations_dependency.py @@ -11,6 +11,7 @@ """ import os +from test_support.templates import compose_if_admin_settings CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) @@ -27,8 +28,8 @@ def read_text(path): - with open(path, 'r', encoding='utf-8') as file_handle: - return file_handle.read() + with open(path, "r", encoding="utf-8") as file_handle: + return compose_if_admin_settings(path, file_handle.read()) def test_functions_settings_derives_tabular_enablement(): diff --git a/functional_tests/test_workflow_access_controls.py b/functional_tests/test_workflow_access_controls.py index 562ee7b2c..544e858da 100644 --- a/functional_tests/test_workflow_access_controls.py +++ b/functional_tests/test_workflow_access_controls.py @@ -12,13 +12,17 @@ import json from pathlib import Path from test_support.versioning import assert_app_version_at_least +from test_support.templates import compose_if_admin_settings ROOT = Path(__file__).resolve().parents[1] def read_text(relative_path): - return (ROOT / relative_path).read_text(encoding="utf-8") + _path = ROOT / relative_path + return compose_if_admin_settings( + _path, _path.read_text(encoding="utf-8") + ) def test_workflow_access_control_wiring(): diff --git a/functional_tests/test_workflow_auto_invoke_attempt_settings.py b/functional_tests/test_workflow_auto_invoke_attempt_settings.py index 0f3d80b17..23365d588 100644 --- a/functional_tests/test_workflow_auto_invoke_attempt_settings.py +++ b/functional_tests/test_workflow_auto_invoke_attempt_settings.py @@ -13,13 +13,17 @@ from pathlib import Path from test_support.versioning import assert_app_version_at_least +from test_support.templates import compose_if_admin_settings ROOT = Path(__file__).resolve().parents[1] def read_text(relative_path: str) -> str: - return (ROOT / relative_path).read_text(encoding="utf-8") + _path = ROOT / relative_path + return compose_if_admin_settings( + _path, _path.read_text(encoding="utf-8") + ) def test_workflow_auto_invoke_attempt_settings_wiring() -> None: From 36496dc1d3c0487c7f90e8196db969593f94f837 Mon Sep 17 00:00:00 2001 From: Paul Lizer Date: Wed, 19 Aug 2026 09:57:14 -0400 Subject: [PATCH 02/25] Move Latest Features to the end of Admin Settings navigation Latest Features was pinned first in both the top-tab strip and the admin sidebar, and its pane was hard-coded as the default active tab, so it opened on every single visit to Admin Settings. That made a curated release-notes tab behave like the landing page. It now sits last in both navigations, after Send Feedback, and General is the default landing tab instead. The Latest Features markup itself is unchanged, including its New badge and the hide/unhide dropdown. Three places drove the old behaviour and all three are updated: the top-tab button ordering and active class, the tab-pane show active class, and the sidebar bootstrap default in admin_sidebar_nav.js. Test assertions that pinned the old ordering are inverted to pin the new intent, so a future change cannot silently restore the landing behaviour. Field names and card ids remain byte-identical, and the 75 functional test files covering admin_settings.html show the same 32 pre-existing failures as before the change. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../static/js/admin/admin_sidebar_nav.js | 7 +- .../single_app/templates/_sidebar_nav.html | 107 +++++++++--------- .../templates/admin/_panes/general.html | 2 +- .../admin/_panes/latest-features.html | 2 +- .../single_app/templates/admin_settings.html | 22 ++-- .../test_admin_latest_features_tab.py | 20 +++- .../test_admin_send_feedback_tab.py | 4 +- 7 files changed, 89 insertions(+), 75 deletions(-) diff --git a/application/single_app/static/js/admin/admin_sidebar_nav.js b/application/single_app/static/js/admin/admin_sidebar_nav.js index ff628ec79..07c95f5bc 100644 --- a/application/single_app/static/js/admin/admin_sidebar_nav.js +++ b/application/single_app/static/js/admin/admin_sidebar_nav.js @@ -135,13 +135,14 @@ function initAdminSidebarNav() { }); }); - // Set the initial active tab (General) - but only if no tab is already active + // Set the initial active tab - but only if no tab is already active. + // Latest Features is deliberately excluded so it never opens by default. const activeTab = document.querySelector('.admin-nav-tab.active, .admin-nav-section.active'); if (!activeTab) { - const firstTab = document.querySelector('.admin-nav-tab[data-tab="latest-features"]'); + const firstTab = document.querySelector('.admin-nav-tab[data-tab="general"]'); if (firstTab) { firstTab.classList.add('active'); - showAdminTab('latest-features'); + showAdminTab('general'); } } else { console.log('initAdminSidebarNav - Found existing active tab, preserving current state:', activeTab.getAttribute('data-tab')); diff --git a/application/single_app/templates/_sidebar_nav.html b/application/single_app/templates/_sidebar_nav.html index 198eab56b..3269220f2 100644 --- a/application/single_app/templates/_sidebar_nav.html +++ b/application/single_app/templates/_sidebar_nav.html @@ -442,59 +442,6 @@
+ {# Latest Features stays last so it never opens by default #} + {% if not latest_features_nav_is_hidden %} + + {% endif %}
diff --git a/application/single_app/templates/admin/_panes/general.html b/application/single_app/templates/admin/_panes/general.html index 1275b3c22..bd3d5be40 100644 --- a/application/single_app/templates/admin/_panes/general.html +++ b/application/single_app/templates/admin/_panes/general.html @@ -1,4 +1,4 @@ -
+

Configure general application settings, including the application's title, logo, and landing page text. diff --git a/application/single_app/templates/admin/_panes/latest-features.html b/application/single_app/templates/admin/_panes/latest-features.html index 1ee42a66a..81d30da64 100644 --- a/application/single_app/templates/admin/_panes/latest-features.html +++ b/application/single_app/templates/admin/_panes/latest-features.html @@ -1,4 +1,4 @@ -

+

Review the main capabilities introduced in v{{ config['VERSION'] }} and use this page as an admin-facing overview for what to highlight with your teams.

diff --git a/application/single_app/templates/admin_settings.html b/application/single_app/templates/admin_settings.html index 56e65ad9b..e132c6d49 100644 --- a/application/single_app/templates/admin_settings.html +++ b/application/single_app/templates/admin_settings.html @@ -931,14 +931,8 @@

12. Enhanced Citations and Image Generation

{% endif %} @@ -1066,8 +1067,6 @@

12. Enhanced Citations and Image Generation

{% include "admin/_panes/general.html" %} - {% include "admin/_panes/latest-features.html" %} - {% include "admin/_panes/custom-pages.html" %} @@ -1098,6 +1097,9 @@

12. Enhanced Citations and Image Generation

{% include "admin/_panes/search-extract.html" %} {% include "admin/_panes/send-feedback.html" %} + + {# Latest Features stays last so it never opens by default #} + {% include "admin/_panes/latest-features.html" %}
diff --git a/functional_tests/test_admin_latest_features_tab.py b/functional_tests/test_admin_latest_features_tab.py index 9ccb83478..6f947d4ac 100644 --- a/functional_tests/test_admin_latest_features_tab.py +++ b/functional_tests/test_admin_latest_features_tab.py @@ -337,7 +337,9 @@ def test_latest_features_sidebar_navigation(): latest_features_index = sidebar_content.index('data-tab="latest-features"') general_index = sidebar_content.index('data-tab="general"') - assert latest_features_index < general_index, 'Latest Features should appear before General in the admin sidebar' + send_feedback_index = sidebar_content.index('data-tab="send-feedback"') + assert latest_features_index > general_index, 'Latest Features should appear after General in the admin sidebar' + assert latest_features_index > send_feedback_index, 'Latest Features should be the last admin sidebar destination, after Send Feedback' assert 'New' in sidebar_content, 'Sidebar Latest Features item should include a New badge' print('Latest Features sidebar navigation is present') @@ -345,20 +347,26 @@ def test_latest_features_sidebar_navigation(): def test_latest_features_top_nav_priority(): - """Latest Features should be the first top-nav tab and default active pane.""" - print('Testing Latest Features top-nav priority...') + """Latest Features should be the last top-nav tab and never default active.""" + print('Testing Latest Features top-nav placement...') template_content = read_text(ADMIN_TEMPLATE) latest_features_tab_index = template_content.index('id="latest-features-tab"') general_tab_index = template_content.index('id="general-tab"') - assert latest_features_tab_index < general_tab_index, 'Latest Features tab should appear before General in top nav' + send_feedback_tab_index = template_content.index('id="send-feedback-tab"') + assert latest_features_tab_index > general_tab_index, 'Latest Features tab should appear after General in top nav' + assert latest_features_tab_index > send_feedback_tab_index, 'Latest Features tab should be the last top-nav tab, after Send Feedback' assert 'id="latest-features-tab" data-bs-toggle="tab" data-bs-target="#latest-features"' in template_content, 'Latest Features top-nav tab missing' assert 'Latest Features New' in template_content, 'Latest Features top-nav tab should include a New badge' - assert 'class="tab-pane fade show active" id="latest-features" role="tabpanel" aria-labelledby="latest-features-tab"' in template_content, 'Latest Features pane should be the default active tab' - print('Latest Features is prioritized in top navigation') + # Latest Features opened on every visit to Admin Settings, which is why it + # now sits last and General is the landing tab instead. + assert 'class="tab-pane fade" id="latest-features" role="tabpanel" aria-labelledby="latest-features-tab"' in template_content, 'Latest Features pane should not be the default active tab' + assert 'class="tab-pane fade show active" id="general" role="tabpanel" aria-labelledby="general-tab"' in template_content, 'General pane should be the default active tab' + + print('Latest Features is last in top navigation and not default active') return True diff --git a/functional_tests/test_admin_send_feedback_tab.py b/functional_tests/test_admin_send_feedback_tab.py index 7b06dc812..89e46fd4e 100644 --- a/functional_tests/test_admin_send_feedback_tab.py +++ b/functional_tests/test_admin_send_feedback_tab.py @@ -107,7 +107,9 @@ def test_send_feedback_navigation_order(): send_feedback_sidebar_index = sidebar_content.index('data-tab="send-feedback"') search_extract_sidebar_index = sidebar_content.index('data-tab="search-extract"') - assert send_feedback_sidebar_index > search_extract_sidebar_index, 'Send Feedback should be the last admin sidebar item' + latest_features_sidebar_index = sidebar_content.index('data-tab="latest-features"') + assert send_feedback_sidebar_index > search_extract_sidebar_index, 'Send Feedback should come after Search and Extract in the admin sidebar' + assert send_feedback_sidebar_index < latest_features_sidebar_index, 'Send Feedback should come before Latest Features, which is pinned last' assert 'data-section="latest-features-send-feedback-card"' in sidebar_content, 'Latest Features submenu should expose the Send Feedback callout' From f8147d50bf5b635db85709e8a0aa09526d2a1289 Mon Sep 17 00:00:00 2001 From: Paul Lizer Date: Wed, 19 Aug 2026 10:03:17 -0400 Subject: [PATCH 03/25] Give Global Identities and File Sync proper Admin Settings surfaces Two Admin Settings features were rendered as afterthoughts compared to every other tab. Global Identities was a bare JavaScript mount point: no heading, no description, and no intro copy, so the tab rendered as an unlabelled widget. It now has a heading and explains that identities are deployment-wide and that secrets are held in Key Vault when Key Vault storage is configured, which matches the behaviour in functions_workspace_identities.py. File Sync is one of the larger settings surfaces at roughly 400 lines and 32 inputs, but it was the only tab with no sidebar submenu, so none of its sub-areas could be reached or searched from the navigation. Its source-type and per-workspace-type areas now carry ids and are exposed as sidebar destinations. The existing data-testid hooks are left in place. The composition guard added with the template split is extended to cover form field names as well as card ids. That immediately surfaced seven more tests that read admin_settings.html without composing its partials, including ones asserting on fields rather than cards, and those are now routed through the shared helper. Field names and card ids remain byte-identical, and the 75 functional test files covering admin_settings.html show the same 32 pre-existing failures. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../single_app/templates/_sidebar_nav.html | 27 +++++++++++++++++++ .../templates/admin/_panes/file-sync.html | 8 +++--- .../admin/_panes/workspace-identities.html | 9 +++++++ .../test_access_denied_message_feature.py | 3 ++- ...est_admin_settings_template_composition.py | 17 +++++++++--- ...ave4b_document_access_shadow_validation.py | 7 ++--- ...smos_wave5a_document_access_read_switch.py | 7 ++--- .../test_disable_group_creation_fix.py | 5 ++-- ...t_external_health_check_duplication_fix.py | 5 ++-- ...st_file_sync_workspace_assignment_gates.py | 6 ++++- functional_tests/test_thoughts_feature.py | 3 ++- 11 files changed, 73 insertions(+), 24 deletions(-) diff --git a/application/single_app/templates/_sidebar_nav.html b/application/single_app/templates/_sidebar_nav.html index 3269220f2..d3419b8d0 100644 --- a/application/single_app/templates/_sidebar_nav.html +++ b/application/single_app/templates/_sidebar_nav.html @@ -830,6 +830,33 @@ File Sync +
{% set file_sync_visible_source_types = settings.file_sync_visible_source_types|default(['smb', 'azure_files']) %} -
+
Visible Source Types

Choose which source types appear in the Add Source workflow. OneDrive, SharePoint, and Google Workspace connectors are coming soon while validation continues. @@ -136,7 +136,7 @@

Visible Source Types
-
+
Personal Workspace Sync
@@ -169,7 +169,7 @@
Personal Workspace Sync
-
+
Group Workspace Sync
@@ -208,7 +208,7 @@
Group Workspace Sync
-
+
Public Workspace Sync
diff --git a/application/single_app/templates/admin/_panes/workspace-identities.html b/application/single_app/templates/admin/_panes/workspace-identities.html index 50df99098..da77b91b5 100644 --- a/application/single_app/templates/admin/_panes/workspace-identities.html +++ b/application/single_app/templates/admin/_panes/workspace-identities.html @@ -1,5 +1,14 @@
+

+ Manage the global authentication profiles that File Sync sources and Actions reuse, so credentials stay scoped and are resolved server-side. +

+
+ Global Identities +
+

+ Identities defined here are available across the deployment. Each one stores its secret in Key Vault when Key Vault is configured, and is referenced by name rather than by value wherever it is used. +

]*\sid="([^"]+)"') + field_pattern = re.compile(r'\sname="([^"]+)"') + parent_cards = set(card_pattern.findall(parent)) partial_only_cards = set(card_pattern.findall(composed)) - parent_cards assert partial_only_cards, "Expected some cards to live only in partials" + parent_fields = set(field_pattern.findall(parent)) + partial_only_fields = set(field_pattern.findall(composed)) - parent_fields + assert partial_only_fields, "Expected some fields to live only in partials" + offenders = [] for path in sorted(TESTS_DIR.glob("test_*.py")): if path.name == Path(__file__).name: @@ -136,6 +142,11 @@ def test_no_functional_test_reads_the_template_uncomposed(): referenced = sorted( card_id for card_id in partial_only_cards if card_id in source ) + referenced += sorted( + f'name="{field}"' + for field in partial_only_fields + if f'name="{field}"' in source + ) if referenced: offenders.append(f"{path.name} -> {', '.join(referenced[:3])}") diff --git a/functional_tests/test_cosmos_wave4b_document_access_shadow_validation.py b/functional_tests/test_cosmos_wave4b_document_access_shadow_validation.py index 817d17dbe..303cbae35 100644 --- a/functional_tests/test_cosmos_wave4b_document_access_shadow_validation.py +++ b/functional_tests/test_cosmos_wave4b_document_access_shadow_validation.py @@ -28,6 +28,7 @@ from contextlib import contextmanager from datetime import datetime, timezone from test_support.versioning import assert_app_version_at_least +from test_support.templates import read_admin_settings_template ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -673,11 +674,7 @@ def test_wave4b_admin_routes_and_version_are_wired(): "r", encoding="utf-8", ).read() - admin_template = open( - os.path.join(SINGLE_APP_DIR, "templates", "admin_settings.html"), - "r", - encoding="utf-8", - ).read() + admin_template = read_admin_settings_template() personal_route = open(os.path.join(SINGLE_APP_DIR, "route_backend_documents.py"), "r", encoding="utf-8").read() group_route = open(os.path.join(SINGLE_APP_DIR, "route_backend_group_documents.py"), "r", encoding="utf-8").read() public_route = open(os.path.join(SINGLE_APP_DIR, "route_external_public_documents.py"), "r", encoding="utf-8").read() diff --git a/functional_tests/test_cosmos_wave5a_document_access_read_switch.py b/functional_tests/test_cosmos_wave5a_document_access_read_switch.py index badcfafcc..a0eb1acd0 100644 --- a/functional_tests/test_cosmos_wave5a_document_access_read_switch.py +++ b/functional_tests/test_cosmos_wave5a_document_access_read_switch.py @@ -24,6 +24,7 @@ import types from contextlib import contextmanager from test_support.versioning import assert_app_version_at_least +from test_support.templates import read_admin_settings_template ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -799,11 +800,7 @@ def test_wave5b_route_and_admin_contract_are_wired(): "r", encoding="utf-8", ).read() - admin_template = open( - os.path.join(SINGLE_APP_DIR, "templates", "admin_settings.html"), - "r", - encoding="utf-8", - ).read() + admin_template = read_admin_settings_template() admin_route = open( os.path.join(SINGLE_APP_DIR, "route_frontend_admin_settings.py"), "r", diff --git a/functional_tests/test_disable_group_creation_fix.py b/functional_tests/test_disable_group_creation_fix.py index 4faffe706..451a8eb2f 100644 --- a/functional_tests/test_disable_group_creation_fix.py +++ b/functional_tests/test_disable_group_creation_fix.py @@ -20,6 +20,7 @@ import sys import os +from test_support.templates import compose_if_admin_settings # Add parent directory to path for imports sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) @@ -37,7 +38,7 @@ def test_form_field_name_matches_backend(): ) with open(template_path, 'r', encoding='utf-8') as f: - template_content = f.read() + template_content = compose_if_admin_settings(template_path, f.read()) # Verify the form field exists with name="disable_group_creation" assert 'name="disable_group_creation"' in template_content, \ @@ -88,7 +89,7 @@ def test_control_center_toggle_exists(): ) with open(template_path, 'r', encoding='utf-8') as f: - template_content = f.read() + template_content = compose_if_admin_settings(template_path, f.read()) # Verify the toggle exists assert 'id="disableGroupCreation"' in template_content, \ diff --git a/functional_tests/test_external_health_check_duplication_fix.py b/functional_tests/test_external_health_check_duplication_fix.py index fabfe15e8..aa750b418 100644 --- a/functional_tests/test_external_health_check_duplication_fix.py +++ b/functional_tests/test_external_health_check_duplication_fix.py @@ -11,6 +11,7 @@ import sys import os import re +from test_support.templates import compose_if_admin_settings def test_external_health_check_duplication(): """Test that there is only one External Health Check section in admin settings.""" @@ -27,7 +28,7 @@ def test_external_health_check_duplication(): raise FileNotFoundError(f"Template file not found: {template_path}") with open(template_path, 'r', encoding='utf-8') as f: - content = f.read() + content = compose_if_admin_settings(template_path, f.read()) # Count occurrences of "External Health Check" headers header_pattern = r'
External Health Check
' @@ -81,7 +82,7 @@ def test_ui_structure_integrity(): ) with open(template_path, 'r', encoding='utf-8') as f: - content = f.read() + content = compose_if_admin_settings(template_path, f.read()) # Check for proper card structure card_open_count = len(re.findall(r'
', content)) diff --git a/functional_tests/test_file_sync_workspace_assignment_gates.py b/functional_tests/test_file_sync_workspace_assignment_gates.py index 431f439bf..b5bb09f9d 100644 --- a/functional_tests/test_file_sync_workspace_assignment_gates.py +++ b/functional_tests/test_file_sync_workspace_assignment_gates.py @@ -13,6 +13,7 @@ import ast import sys from pathlib import Path +from test_support.templates import compose_if_admin_settings REPO_ROOT = Path(__file__).resolve().parents[1] @@ -21,7 +22,10 @@ def read_text(relative_path): """Read a repository file as UTF-8 text.""" - return (REPO_ROOT / relative_path).read_text(encoding="utf-8") + _path = REPO_ROOT / relative_path + return compose_if_admin_settings( + _path, _path.read_text(encoding="utf-8") + ) def get_function_source(relative_path, function_name): diff --git a/functional_tests/test_thoughts_feature.py b/functional_tests/test_thoughts_feature.py index cc295159c..aea6c11a1 100644 --- a/functional_tests/test_thoughts_feature.py +++ b/functional_tests/test_thoughts_feature.py @@ -11,6 +11,7 @@ import sys import os +from test_support.templates import compose_if_admin_settings sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'application', 'single_app')) @@ -289,7 +290,7 @@ def test_thoughts_admin_settings(): ) with open(html_file, 'r', encoding='utf-8') as f: - content = f.read() + content = compose_if_admin_settings(html_file, f.read()) checks = { 'enable_thoughts checkbox': 'id="enable_thoughts"' in content, From 236b7326fb718986bfe3c5bd8f3bfa8ac62d51fd Mon Sep 17 00:00:00 2001 From: Paul Lizer Date: Wed, 19 Aug 2026 10:06:21 -0400 Subject: [PATCH 04/25] Fix classification banner preview and document Admin Settings changes The classification banner live preview never worked. Its script sat between {% endblock %} and {% block scripts %} in a child template, and Jinja drops content outside blocks in a child template, so the code was never rendered. Moving it inside the scripts block makes the preview respond to banner text, background colour, and text colour changes. Bumps VERSION to 0.260.004 and adds the fix write-up plus release notes for this delivery stage: the template split, the Latest Features reposition, the Global Identities and File Sync surfaces, and this preview fix. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- application/single_app/config.py | 2 +- .../single_app/templates/admin_settings.html | 9 +- ...S_NAVIGATION_AND_TEMPLATE_STRUCTURE_FIX.md | 165 ++++++++++++++++++ docs/explanation/release_notes.md | 35 ++++ ...est_admin_settings_template_composition.py | 6 +- 5 files changed, 210 insertions(+), 7 deletions(-) create mode 100644 docs/explanation/fixes/ADMIN_SETTINGS_NAVIGATION_AND_TEMPLATE_STRUCTURE_FIX.md diff --git a/application/single_app/config.py b/application/single_app/config.py index 179e25894..39d9a8a90 100644 --- a/application/single_app/config.py +++ b/application/single_app/config.py @@ -96,7 +96,7 @@ EXECUTOR_TYPE = 'thread' EXECUTOR_MAX_WORKERS = 30 SESSION_TYPE = 'filesystem' -VERSION = "0.260.006" +VERSION = "0.260.007" IS_DEVELOPMENT = is_development_env_enabled() SESSION_COOKIE_SAMESITE = os.getenv('SESSION_COOKIE_SAMESITE', 'Lax') diff --git a/application/single_app/templates/admin_settings.html b/application/single_app/templates/admin_settings.html index e132c6d49..46d2e49dc 100644 --- a/application/single_app/templates/admin_settings.html +++ b/application/single_app/templates/admin_settings.html @@ -1614,8 +1614,13 @@
{% endblock %} + +{% block scripts %} + -{% block scripts %} - + {% if settings.enable_semantic_kernel %} @@ -2532,14 +2541,11 @@
Summary:
// Render markdown to HTML const previewContent = document.getElementById('userAgreementPreviewContent'); if (previewContent) { - // Use marked if available, otherwise show raw text + // marked and DOMPurify are both loaded globally from base.html, + // so sanitize inline at the sink rather than through a guarded + // reassignment, which reads as unsanitized to static analysis. if (typeof marked !== 'undefined') { - let html = marked.parse(agreementText); - // Sanitize if DOMPurify is available - if (typeof DOMPurify !== 'undefined') { - html = DOMPurify.sanitize(html); - } - previewContent.innerHTML = html; + previewContent.innerHTML = DOMPurify.sanitize(marked.parse(agreementText)); } else { previewContent.textContent = agreementText; } diff --git a/docs/explanation/fixes/ADMIN_SETTINGS_CARD_TARGETED_LINKS_FIX.md b/docs/explanation/fixes/ADMIN_SETTINGS_CARD_TARGETED_LINKS_FIX.md new file mode 100644 index 000000000..ced456655 --- /dev/null +++ b/docs/explanation/fixes/ADMIN_SETTINGS_CARD_TARGETED_LINKS_FIX.md @@ -0,0 +1,144 @@ +# Admin Settings Card-Targeted Navigation Links + +**Implemented in version:** 0.260.008 +**Area:** Admin Settings information architecture, stage A +**Related:** `ADMIN_SETTINGS_NAVIGATION_AND_TEMPLATE_STRUCTURE_FIX.md` + +## Issue + +Admin Settings contains cross-references between related settings, for example +Citations pointing at the video and audio file-support toggles, or the Redis +Key Vault hint pointing at the Key Vault card. Each of those links named a tab +button directly: + +```html + +``` + +`switchTab` looked up `document.getElementById('workspaces-tab')`. That couples +every link to a tab id, with two consequences. + +### 1. Links break silently on any tab change + +If the tab id changes, the lookup returns `null`, `switchTab` falls through to +`showAdminTab('workspaces')`, no pane matches, and the only symptom is a console +warning plus a URL hash pointing at nothing. Nothing fails loudly, so the +breakage survives review. + +This was about to happen at scale: the information architecture rework renames +and regroups most tabs. + +### 2. Two links were already wrong + +The Citations tab referenced **Enable Video File Support** and **Enable Audio +File Support** with `#workspaces`, and the surrounding prose said "see the +Workspaces tab". Both settings actually live in **Search and Extract**: + +| Setting | Card | Pane | +|---|---|---| +| `enable_video_file_support` | `video-intelligence-section` | `search-extract` | +| `enable_audio_file_support` | `ai-voice-chat-section` | `search-extract` | + +So an admin following either link landed on the wrong tab with no indication +why. + +## Root cause + +The link contract pointed at *navigation structure* (a tab button id) rather +than at *content* (the setting being referenced). Navigation structure changes; +the identity of a settings card does not. + +## Fix + +Links now declare the card they want: + +```html + + Enable Video File Support + +``` + +`static/js/admin/admin_card_links.js` resolves the owning tab from the DOM at +click time: + +```js +const card = document.getElementById(cardId); +const pane = card.closest('.tab-pane'); +window.showAdminTab(pane.id); +``` + +Because the tab is derived rather than declared, cards can move between tabs and +tabs can be renamed or regrouped without touching a single link. Card ids are +the only contract, and they are already stable — the template split preserved +all 110 of them byte-identically. + +Behaviour on click: + +1. Resolve the card and activate its tab, in either the top-tab or sidebar layout. +2. Mirror the active state in the sidebar when that layout is in use. +3. Scroll the card into view. +4. Highlight it for 1.6 seconds so the destination is obvious after the jump. + +Clicks are handled by delegation, so links rendered later still work. + +`switchTab` had no remaining callers and was removed, which makes the old +coupling impossible to reintroduce by copy-paste. + +## Files modified + +| File | Change | +|---|---| +| `application/single_app/static/js/admin/admin_card_links.js` | New resolver module | +| `application/single_app/static/js/admin/admin_settings.js` | Removed the dead `switchTab` helper | +| `application/single_app/templates/admin_settings.html` | Loads the resolver; adds the highlight style; sanitizes the User Agreement preview at the sink | +| `application/single_app/templates/admin/_panes/citation.html` | Two corrected links plus corrected prose | +| `application/single_app/templates/admin/_panes/latest-features.html` | Eight links converted | +| `application/single_app/templates/admin/_panes/scale.html` | Key Vault link converted | +| `application/single_app/templates/admin/_panes/search-extract.html` | Enhanced Citations link converted | +| `functional_tests/test_admin_card_links.py` | New contract test | +| `application/single_app/config.py` | Version to 0.260.008 | + +## Link inventory + +| Source | Target card | +|---|---| +| Citations → video support | `video-intelligence-section` *(was wrong)* | +| Citations → audio support | `ai-voice-chat-section` *(was wrong)* | +| Latest Features → model endpoints | `multi-endpoint-configuration` | +| Latest Features → citations ×2 | `enhanced-citations-section` | +| Latest Features → processing thoughts | `processing-thoughts-section` | +| Latest Features → support menu ×2 | `support-menu-section` | +| Latest Features → Redis | `redis-cache-section` | +| Latest Features → send feedback | `send-feedback-overview-card` | +| Scale → Key Vault | `keyvault-section` | +| Search and Extract → enhanced citations | `enhanced-citations-section` | + +## Validation + +`functional_tests/test_admin_card_links.py` pins four properties: + +1. Every `data-admin-link` target is a real element id in the composed template. +2. Every such link carries a matching `href="#"`, so it degrades + sensibly without JavaScript and stays copy-pasteable. +3. No link reintroduces the tab-coupled `switchTab` pattern. +4. The resolver module exists, is loaded by the page, and derives the tab from + the DOM rather than from a hardcoded map. + +Property 1 is the one that matters: it fails the build if a card is renamed or +removed without updating the links that point at it, which is precisely the +failure mode that went unnoticed before. + +Regression evidence: + +- Form field names and card ids unchanged: **462 names, 110 card ids identical**. +- 75 functional test files covering `admin_settings.html`: **33 pre-existing + failures before and after, identical sets**. +- All 20 admin templates parse under Jinja. +- `scripts/check_xss_sinks.py` passes a full-file scan of every touched file, + which it did not before the User Agreement preview change. + +## Follow-up + +Stage B introduces declarative dependency gating, where a setting that requires +another one announces it inline with a mirror control and a link to the +prerequisite. That link uses the resolver added here. diff --git a/docs/explanation/release_notes.md b/docs/explanation/release_notes.md index a68676b5c..b63d46cdf 100644 --- a/docs/explanation/release_notes.md +++ b/docs/explanation/release_notes.md @@ -2,6 +2,21 @@ For feature-focused and fix-focused drill-downs by version, see [Features by Version](/explanation/features/) and [Fixes by Version](/explanation/fixes/). +### **(v0.260.008)** + +#### Bug Fixes + +* **Cross-Tab Links In Admin Settings Now Point At The Right Place** + * Links that send you from one Admin Settings tab to a related setting used to name a tab button directly, so they broke silently whenever a tab was renamed or reorganised: no tab opened, and the address bar was left pointing at nothing. + * Two were already wrong. The **Video File Support** and **Audio File Support** references in Citations sent you to the **Workspaces** tab, but those settings live under **Search and Extract**. Both now open the correct card. + * All twelve cross-tab links now name the card they want, and the owning tab is worked out when you click. The destination card is briefly highlighted so it is obvious where you landed. + * (Ref: `admin_card_links.js`, `data-admin-link`, `openAdminCard`, `test_admin_card_links.py`) + +* **User Agreement Preview Sanitized At The Sink** + * The User Agreement preview rendered Markdown through a guarded reassignment, which reads as unsanitized to static analysis and matched the pattern already corrected for the Home Page Text preview. + * Now sanitized inline with `DOMPurify.sanitize(...)` at the point of rendering. `marked` and DOMPurify are both loaded globally, so the availability guards were redundant. + * (Ref: User Agreement, `admin_settings.html` preview handler, DOMPurify) + ### **(v0.260.007)** #### User Interface Enhancements diff --git a/functional_tests/test_admin_card_links.py b/functional_tests/test_admin_card_links.py new file mode 100644 index 000000000..740c2e731 --- /dev/null +++ b/functional_tests/test_admin_card_links.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 +# test_admin_card_links.py +""" +Functional test for Admin Settings card-targeted navigation links. +Version: 0.260.008 +Implemented in: 0.260.008 + +Cross-tab links used to name a tab button directly, for example +switchTab(event, 'workspaces-tab'). That couples every link to a tab id, so an +information-architecture change silently breaks it: the button no longer +exists, no pane is activated, and the URL hash points at nothing. Two links +were already wrong this way, pointing at the Workspaces tab for video and audio +settings that live under Search and Extract. + +Links now name the card they want with data-admin-link, and the owning tab is +resolved from the DOM at click time. This test pins that contract: + + 1. Every data-admin-link target is a real element id in the composed template. + 2. Every such link carries a matching href fragment, so it degrades sensibly + without JavaScript and remains copy-pasteable. + 3. No link reintroduces the tab-coupled switchTab pattern. + 4. The resolver module is wired into the page. +""" + +import re +import sys +from pathlib import Path + +from test_support.templates import ( + ADMIN_SETTINGS_TEMPLATE, + read_admin_settings_template, +) +from test_support.versioning import assert_app_version_at_least + + +REPO_ROOT = Path(__file__).resolve().parents[1] +CARD_LINKS_JS = ( + REPO_ROOT + / "application" + / "single_app" + / "static" + / "js" + / "admin" + / "admin_card_links.js" +) + +LINK_RE = re.compile(r']*\bdata-admin-link="(?P[^"]+)"[^>]*>') +HREF_RE = re.compile(r'\bhref="#(?P[^"]+)"') + + +def _links(markup): + """Return every anchor tag that declares a card target.""" + return [(m.group("target"), m.group(0)) for m in LINK_RE.finditer(markup)] + + +def test_every_card_link_target_exists(): + """A link that resolves to nothing is a dead end for the admin.""" + print("Testing Admin Settings card link targets...") + + assert_app_version_at_least("0.260.008") + composed = read_admin_settings_template() + element_ids = set(re.findall(r'\sid="([^"]+)"', composed)) + + links = _links(composed) + assert links, "Expected Admin Settings to declare card-targeted links" + + missing = sorted({target for target, _ in links if target not in element_ids}) + assert not missing, ( + "data-admin-link targets do not exist in the composed template: " + f"{missing}" + ) + + print(f"All {len(links)} card link(s) resolve to real elements.") + + +def test_card_links_keep_a_matching_href(): + """The href is the no-JavaScript fallback and should match the target.""" + print("Testing Admin Settings card link href fallbacks...") + + composed = read_admin_settings_template() + mismatched = [] + + for target, tag in _links(composed): + href = HREF_RE.search(tag) + if href is None: + mismatched.append(f"{target} (no href)") + elif href.group("fragment") != target: + mismatched.append(f"{target} (href points at {href.group('fragment')})") + + assert not mismatched, ( + "Card links should carry href=\"#\": " f"{mismatched}" + ) + + print("Every card link href matches its data-admin-link target.") + + +def test_no_tab_coupled_links_remain(): + """switchTab links break silently whenever a tab is renamed or regrouped.""" + print("Testing that no tab-coupled admin links remain...") + + composed = read_admin_settings_template() + offenders = re.findall(r'onclick="switchTab\(event,\s*\'([^\']+)\'\)"', composed) + + assert not offenders, ( + "These links still name a tab button, so they break when the tab id " + "changes. Use data-admin-link with a card id instead: " + f"{sorted(set(offenders))}" + ) + + print("No tab-coupled links remain.") + + +def test_card_link_resolver_is_wired_in(): + """The markup contract is useless if the resolver never loads.""" + print("Testing Admin Settings card link resolver wiring...") + + assert CARD_LINKS_JS.is_file(), f"Missing resolver module at {CARD_LINKS_JS}" + + parent = ADMIN_SETTINGS_TEMPLATE.read_text(encoding="utf-8") + assert "js/admin/admin_card_links.js" in parent, ( + "admin_card_links.js is not loaded by admin_settings.html" + ) + + source = CARD_LINKS_JS.read_text(encoding="utf-8") + for marker in ( + "export function openAdminCard", + "data-admin-link", + "closest('.tab-pane')", + ): + assert marker in source, f"Resolver is missing expected logic: {marker}" + + # The resolver must derive the tab from the DOM rather than hardcode a map, + # which is the whole reason the links survive an IA change. Comments are + # stripped first so the explanation of the old pattern does not trip this. + code = re.sub(r"^\s*//.*$", "", source, flags=re.MULTILINE) + assert "switchTab(" not in code, ( + "Resolver should not depend on the tab-coupled switchTab helper" + ) + + print("Card link resolver is present and wired into the page.") + + +if __name__ == "__main__": + tests = [ + test_every_card_link_target_exists, + test_card_links_keep_a_matching_href, + test_no_tab_coupled_links_remain, + test_card_link_resolver_is_wired_in, + ] + + results = [] + for test in tests: + try: + test() + results.append(True) + except Exception as exc: + print(f"FAILED {test.__name__}: {exc}") + import traceback + traceback.print_exc() + results.append(False) + + print(f"\nResults: {sum(results)}/{len(results)} passed") + sys.exit(0 if all(results) else 1) From 8c85fd9cb11c19d5bf0266ba4f0347140b9faacd Mon Sep 17 00:00:00 2001 From: Paul Lizer Date: Wed, 19 Aug 2026 13:52:09 -0400 Subject: [PATCH 10/25] Announce Admin Settings options that require another option Several settings only take effect when a different setting is enabled, and the two often live in different tabs. That was communicated four different ways, none of them actionable: a static server-rendered alert, a sentence of prose, a tooltip, or a flash message only after saving. An admin could switch something on and have nothing happen with no visible reason, and no indication of where the prerequisite lived. A dependent card now declares what it needs with data-requires, and a shared module renders an inline notice with two ways to act: a mirror of the prerequisite switch, so it can be satisfied without leaving the tab, and a link to its card using the stage A resolver. The mirror deliberately carries no name attribute. Admin Settings posts one form read by field name, so a second named control would submit the value twice; the mirror drives the canonical input instead and exactly one named control per setting is ever submitted. File Sync warns rather than blocks, because the backend already accepts it as requested and reconciles once Redis is ready. Removing that would take away a deferred-intent path the backend supports on purpose. The Permissions dependency is scoped to the FeedbackAdmin toggle so the unrelated SafetyViolationAdmin toggle in the same card stays usable. Three candidate dependencies are deliberately not wired: backup source blobs are already gated in admin_data_management.js, multi-modal vision falls back to the legacy GPT deployment rather than requiring multi-endpoint, and the Redis Key Vault hint is conditional on a select value and already links correctly. The File Sync server alert is narrowed to the case the client cannot detect, Redis enabled but missing URL or credentials, so the two messages no longer overlap. Client-side gating is advisory. The backend still validates and flashes, and a test asserts that enforcement is still in place. Field names and card ids unchanged at 462 and 110, and the 75 functional test files covering admin_settings.html show the same 33 pre-existing failures. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../js/admin/admin_settings_dependencies.js | 220 ++++++++++++++++++ .../templates/admin/_panes/file-sync.html | 11 +- .../templates/admin/_panes/safety.html | 7 +- .../single_app/templates/admin_settings.html | 1 + ...ADMIN_SETTINGS_DEPENDENCY_ANNOUNCEMENTS.md | 160 +++++++++++++ docs/explanation/release_notes.md | 10 + .../test_admin_settings_dependencies.py | 182 +++++++++++++++ 7 files changed, 587 insertions(+), 4 deletions(-) create mode 100644 application/single_app/static/js/admin/admin_settings_dependencies.js create mode 100644 docs/explanation/features/ADMIN_SETTINGS_DEPENDENCY_ANNOUNCEMENTS.md create mode 100644 functional_tests/test_admin_settings_dependencies.py diff --git a/application/single_app/static/js/admin/admin_settings_dependencies.js b/application/single_app/static/js/admin/admin_settings_dependencies.js new file mode 100644 index 000000000..266490a18 --- /dev/null +++ b/application/single_app/static/js/admin/admin_settings_dependencies.js @@ -0,0 +1,220 @@ +// admin_settings_dependencies.js +// Announces and enforces Admin Settings options that require another option. +// +// Some settings only work when a different setting is enabled, and those two +// settings often live in different tabs. Previously that was communicated in +// prose, or in a tooltip, or only by a flash message after saving, so an admin +// could turn something on and have nothing happen with no visible reason. +// +// A dependent card declares what it needs: +// +//
+// +// When the prerequisite is off, a notice is inserted at the top of the card +// containing a mirror of the prerequisite control and a link to its card, so +// the admin can satisfy it inline or jump to the full configuration. +// +// Modes: +// block (default) disables the dependent inputs until the prerequisite is on +// warn leaves inputs usable, for prerequisites the backend already +// accepts as intent and reconciles later +// +// This is a usability layer only. The backend remains authoritative and still +// refuses or flashes on unmet prerequisites. + +const NOTICE_CLASS = 'admin-dependency-notice'; +const DISABLED_FLAG = 'data-dependency-disabled'; + +/** + * Read every dependency declared in the document. + * @returns {Array} Parsed dependency descriptors. + */ +function collectDependencies() { + return Array.from(document.querySelectorAll('[data-requires]')).map((card) => ({ + card, + prerequisiteId: card.getAttribute('data-requires'), + label: card.getAttribute('data-requires-label') || 'another setting', + targetCardId: card.getAttribute('data-requires-target') || '', + mode: card.getAttribute('data-requires-mode') === 'warn' ? 'warn' : 'block', + description: card.getAttribute('data-requires-description') || '', + // Optional selector limiting which controls the dependency guards, for + // cards that hold a mix of dependent and independent settings. + scope: card.getAttribute('data-requires-scope') || '', + })); +} + +/** + * Whether a prerequisite control is currently satisfied. + * @param {HTMLElement|null} control Prerequisite input. + * @returns {boolean} + */ +function isSatisfied(control) { + if (!control) { + return true; + } + if (control.type === 'checkbox' || control.type === 'radio') { + return control.checked; + } + return Boolean(control.value); +} + +/** + * Build the notice shown when a prerequisite is unmet. + * @param {object} dependency Dependency descriptor. + * @param {HTMLElement} prerequisite Prerequisite control. + * @returns {HTMLElement} + */ +function buildNotice(dependency, prerequisite) { + const notice = document.createElement('div'); + notice.className = `alert ${dependency.mode === 'warn' ? 'alert-warning' : 'alert-info'} ${NOTICE_CLASS}`; + notice.setAttribute('role', 'note'); + + const heading = document.createElement('div'); + heading.className = 'fw-semibold mb-1'; + const icon = document.createElement('i'); + icon.className = 'bi bi-info-circle me-2'; + icon.setAttribute('aria-hidden', 'true'); + heading.append(icon, document.createTextNode(`This needs ${dependency.label}`)); + notice.appendChild(heading); + + const body = document.createElement('p'); + body.className = 'mb-2'; + body.textContent = dependency.description + || (dependency.mode === 'warn' + ? `These settings are saved, but stay inactive until ${dependency.label} is enabled and configured.` + : `These settings are unavailable until ${dependency.label} is enabled.`); + notice.appendChild(body); + + const actions = document.createElement('div'); + actions.className = 'd-flex flex-wrap align-items-center gap-3'; + + // Inline mirror: satisfy the prerequisite without leaving this tab. It + // carries no name attribute, so only the canonical input is submitted. + if (prerequisite && prerequisite.type === 'checkbox') { + const wrapper = document.createElement('div'); + wrapper.className = 'form-check form-switch mb-0'; + + const proxy = document.createElement('input'); + proxy.type = 'checkbox'; + proxy.className = 'form-check-input'; + proxy.id = `${dependency.card.id}-requires-proxy`; + proxy.checked = prerequisite.checked; + proxy.setAttribute('data-dependency-proxy-for', prerequisite.id); + proxy.setAttribute('data-ignore-settings-change', 'true'); + + proxy.addEventListener('change', () => { + prerequisite.checked = proxy.checked; + prerequisite.dispatchEvent(new Event('change', { bubbles: true })); + }); + + const proxyLabel = document.createElement('label'); + proxyLabel.className = 'form-check-label ms-2'; + proxyLabel.setAttribute('for', proxy.id); + proxyLabel.textContent = `Enable ${dependency.label}`; + + wrapper.append(proxy, proxyLabel); + actions.appendChild(wrapper); + } + + if (dependency.targetCardId) { + const link = document.createElement('a'); + link.href = `#${dependency.targetCardId}`; + link.setAttribute('data-admin-link', dependency.targetCardId); + link.className = 'small'; + link.textContent = `Go to ${dependency.label}`; + actions.appendChild(link); + } + + notice.appendChild(actions); + return notice; +} + +/** + * Enable or disable the controls a dependency guards. + * @param {object} dependency Dependency descriptor. + * @param {boolean} satisfied Whether the prerequisite is met. + */ +function setControlsDisabled(dependency, satisfied) { + if (dependency.mode === 'warn') { + return; + } + + const selector = dependency.scope || 'input, select, textarea, button'; + dependency.card.querySelectorAll(selector).forEach((control) => { + if (control.closest(`.${NOTICE_CLASS}`)) { + return; + } + + if (!satisfied) { + // Remember controls that were already disabled for other + // reasons so re-enabling does not override them. + if (!control.disabled) { + control.setAttribute(DISABLED_FLAG, 'true'); + control.disabled = true; + } + } else if (control.getAttribute(DISABLED_FLAG) === 'true') { + control.removeAttribute(DISABLED_FLAG); + control.disabled = false; + } + }); +} + +/** + * Apply the current state of one dependency. + * @param {object} dependency Dependency descriptor. + */ +function applyDependency(dependency) { + const prerequisite = document.getElementById(dependency.prerequisiteId); + const satisfied = isSatisfied(prerequisite); + + const existing = dependency.card.querySelector(`:scope > .${NOTICE_CLASS}`); + if (existing) { + existing.remove(); + } + + setControlsDisabled(dependency, satisfied); + + if (satisfied) { + return; + } + + const notice = buildNotice(dependency, prerequisite); + const heading = dependency.card.querySelector('h4, h5, h6'); + if (heading && heading.parentElement === dependency.card) { + heading.insertAdjacentElement('afterend', notice); + } else { + dependency.card.prepend(notice); + } +} + +/** + * Wire up every declared dependency and keep it live. + */ +export function initAdminSettingsDependencies() { + const dependencies = collectDependencies(); + if (!dependencies.length) { + return; + } + + dependencies.forEach((dependency) => { + applyDependency(dependency); + + const prerequisite = document.getElementById(dependency.prerequisiteId); + if (!prerequisite) { + console.warn( + `admin dependencies: "${dependency.card.id}" requires missing control ` + + `"${dependency.prerequisiteId}"`, + ); + return; + } + + prerequisite.addEventListener('change', () => applyDependency(dependency)); + prerequisite.addEventListener('input', () => applyDependency(dependency)); + }); +} + +document.addEventListener('DOMContentLoaded', initAdminSettingsDependencies); diff --git a/application/single_app/templates/admin/_panes/file-sync.html b/application/single_app/templates/admin/_panes/file-sync.html index fb31daf88..2d56920d2 100644 --- a/application/single_app/templates/admin/_panes/file-sync.html +++ b/application/single_app/templates/admin/_panes/file-sync.html @@ -3,7 +3,12 @@ Configure file synchronization for personal, group, and public workspaces.

-
+
File Sync @@ -20,9 +25,9 @@
File Sync can scan large remote shares and create many document versions. Start with narrow filters, conservative schedules, and low per-run limits before enabling broad workspace access.
- {% if not settings.file_sync_redis_ready %} + {% if settings.enable_redis_cache and not settings.file_sync_redis_ready %} {% elif settings.requested_enable_file_sync and not settings.file_sync_effective_enabled %} -
+
Permissions
diff --git a/application/single_app/templates/admin_settings.html b/application/single_app/templates/admin_settings.html index 5d0c4ad44..4071a778a 100644 --- a/application/single_app/templates/admin_settings.html +++ b/application/single_app/templates/admin_settings.html @@ -1856,6 +1856,7 @@
-
diff --git a/application/single_app/templates/admin_settings.html b/application/single_app/templates/admin_settings.html index 4071a778a..5c887efee 100644 --- a/application/single_app/templates/admin_settings.html +++ b/application/single_app/templates/admin_settings.html @@ -936,127 +936,43 @@

12. Enhanced Citations and Image Generation

{% set nav_layout = user_settings.get('settings', {}).get('navLayout') %} {% if not (nav_layout == 'sidebar' or (not nav_layout and app_settings.enable_left_nav_default)) %} - {% endif %} + {# Governance status is rendered outside the panes so a message stays + visible whichever governance tab is active. #} +
{% include "admin/_panes/security.html" %} @@ -984,7 +987,9 @@

12. Enhanced Citations and Image Generation

- {% include "admin/_panes/governance.html" %} + {% include "admin/_panes/feature-governance.html" %} + {% include "admin/_panes/governance-policies.html" %} + {% include "admin/_panes/mcp-governance.html" %} {% include "admin/_panes/logging.html" %} diff --git a/functional_tests/test_governance_route_and_wiring_coverage.py b/functional_tests/test_governance_route_and_wiring_coverage.py index a10b08ac6..73ecb1ffb 100644 --- a/functional_tests/test_governance_route_and_wiring_coverage.py +++ b/functional_tests/test_governance_route_and_wiring_coverage.py @@ -132,7 +132,7 @@ def test_governance_enforcement_hooks_across_changed_routes(): assert key in settings_content, f"Missing governance default setting key: {key}" for marker in [ - "id=\"governance\"", + "id=\"feature-governance\"", "governance-feature-policies-table", "governance-item-policies-table", "governance-item-policy-card-controls", From 98b7cd7cb45da9448683cc3dd6a1b38658d7dda1 Mon Sep 17 00:00:00 2001 From: Paul Lizer Date: Wed, 19 Aug 2026 15:18:14 -0400 Subject: [PATCH 16/25] Split Scale into Redis & Caching, Cosmos, and move Front Door to Security Scale mixed three concerns: cache configuration, Cosmos capacity, and Front Door. It is now Redis & Caching and Cosmos, with Front Door moved out entirely. Front Door configures authentication and redirect flows, not throughput, so it sits under Security as a Network tab. That move needed no markup surgery: panes are independent files and groups are entries in the navigation map, so moving a whole tab between groups is a map change alone. Only moving a card between tabs requires touching markup. This pane exercised the interleaved-modal case. Five modals sit between cards rather than collected at the end, and each has to travel with the tab owning its trigger. The split tool assigns the gap after a card to that card, which placed the Redis explorer with Redis, the three maintenance confirmations with Cosmos Maintenance, and the two throughput dialogs with Cosmos Throughput. Verified by listing the modals in each new pane. Extends the legacy redirect map so #scale still resolves. Field names and card ids are unchanged at 462 and 110, and the regression set holds at 32 pre-existing failures. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- application/single_app/admin_settings_nav.py | 24 +- .../static/js/admin/admin_sidebar_nav.js | 1 + .../admin/_panes/{scale.html => cosmos.html} | 487 +----------------- .../templates/admin/_panes/network.html | 41 ++ .../templates/admin/_panes/redis-caching.html | 448 ++++++++++++++++ .../single_app/templates/admin_settings.html | 4 +- 6 files changed, 514 insertions(+), 491 deletions(-) rename application/single_app/templates/admin/_panes/{scale.html => cosmos.html} (70%) create mode 100644 application/single_app/templates/admin/_panes/network.html create mode 100644 application/single_app/templates/admin/_panes/redis-caching.html diff --git a/application/single_app/admin_settings_nav.py b/application/single_app/admin_settings_nav.py index e09f9bfe9..f256b6ebe 100644 --- a/application/single_app/admin_settings_nav.py +++ b/application/single_app/admin_settings_nav.py @@ -193,6 +193,16 @@ {"id": "conversation-archiving-section", "label": "Conversation Archiving", "icon": "bi-archive"}, ], }, + { + # Front Door configures authentication and redirect flows, so it + # belongs with Security rather than with throughput settings. + "id": "network", + "label": "Network", + "icon": "bi-door-open", + "sections": [ + {"id": "front-door-section", "label": "Azure Front Door", "icon": "bi-door-open"}, + ], + }, ], }, { @@ -257,18 +267,24 @@ "icon": "bi-speedometer2", "tabs": [ { - "id": "scale", - "label": "Scale", - "icon": "bi-diagram-3", + "id": "redis-caching", + "label": "Redis & Caching", + "icon": "bi-database", "sections": [ {"id": "redis-cache-section", "label": "Redis Cache", "icon": "bi-database"}, {"id": "redis-monitoring-section", "label": "Redis Metrics", "icon": "bi-activity"}, {"id": "conversation-cache-section", "label": "Conversation Cache", "icon": "bi-chat-square-text"}, + ], + }, + { + "id": "cosmos", + "label": "Cosmos", + "icon": "bi-diagram-3", + "sections": [ {"id": "document-access-index-section", "label": "DAI Metrics", "icon": "bi-diagram-3"}, {"id": "cosmos-maintenance-section", "label": "Cosmos Maintenance", "icon": "bi-tools"}, {"id": "cosmos-throughput-section", "label": "Cosmos DB Throughput", "icon": "bi-speedometer2"}, {"id": "cosmos-throughput-metrics-table-section", "label": "Cosmos Metrics", "icon": "bi-table"}, - {"id": "front-door-section", "label": "Azure Front Door", "icon": "bi-door-open"}, ], }, ], diff --git a/application/single_app/static/js/admin/admin_sidebar_nav.js b/application/single_app/static/js/admin/admin_sidebar_nav.js index 304403f0c..edad020d8 100644 --- a/application/single_app/static/js/admin/admin_sidebar_nav.js +++ b/application/single_app/static/js/admin/admin_sidebar_nav.js @@ -287,6 +287,7 @@ function revealAdminGroupForTab(tabId) { // where their content now lives. Old bookmarks and links keep working. const LEGACY_TAB_REDIRECTS = { 'governance': 'feature-governance', + 'scale': 'redis-caching', }; function resolveAdminTabId(tabId) { diff --git a/application/single_app/templates/admin/_panes/scale.html b/application/single_app/templates/admin/_panes/cosmos.html similarity index 70% rename from application/single_app/templates/admin/_panes/scale.html rename to application/single_app/templates/admin/_panes/cosmos.html index 25e2c5fd7..9d1fa89ca 100644 --- a/application/single_app/templates/admin/_panes/scale.html +++ b/application/single_app/templates/admin/_panes/cosmos.html @@ -1,450 +1,4 @@ -
- {% set enable_dai_debug = settings.enable_dai_debug | default(false) %} -

- Configure Redis cache to improve enterprise scale and performance by caching session data. Enabling Redis allows you to horizontally scale your application across multiple instances without losing session data. -

-
-
- Redis Cache -
-

- Enable Redis Cache to store session data in a distributed cache. This is recommended for production and multi-instance deployments. -

-
- - - -
-
-
- -

(example: simple-chat.redis.cache.windows.net)

- -
-
- - -
-
- -
- - -
-
- Enter the full Key Vault secret name. - Enable Key Vault for Agent and Action Secrets - must be enabled and configured. -
-
- -
-
-
-
-
-
- Redis Monitoring -
-

- Monitor Redis availability, memory pressure, hit rate, evictions, and runtime cache usage before Redis-backed document list caching is enabled. -

-
-
- - -
-
-
-
-
-
-
Configuration
- Not loaded -
-
-
-
-
Health
- Not loaded -
-
-
-
-
App Cache Runtime
- Not loaded -
-
-
-
-
Session Runtime
- Not loaded -
-
-
-
-
-
-
Ping Latency
-
Not loaded
-
-
-
-
-
Memory Usage
-
Not loaded
-
Not loaded
-
-
-
-
-
Connected Clients
-
Not loaded
-
-
-
-
-
Ops/sec
-
Not loaded
-
-
-
-
-
Keyspace Hit Rate
-
Not loaded
-
-
-
-
-
Tracked Keys
-
Not loaded
-
-
-
-
-
DAI Version Markers
-
Not loaded
-
Not loaded
-
-
-
-
-
DAI Cache Payloads
-
Not loaded
-
Not loaded
-
-
-
-
-
Expired / Evicted Keys
-
- Not loaded - / - Not loaded -
-
-
-
-
-
Fragmentation Ratio
-
Not loaded
-
-
-
-
-
Error Replies
-
Not loaded
-
-
-
-
-
Rejected Connections
-
Not loaded
-
-
-
-
-
Redis Version
-
Not loaded
-
-
-
-
-
Monitoring Source
-
Not loaded
-
-
-
-
-
Last Checked
-
Not loaded
-
-
-
-
-
Last Error
-
Not loaded
-
-
-
-
-
- - - -
-
-
-
- Conversation Cache -
-

- Cache conversation list, feed, and advanced-search results per user. Redis is optional; cache misses and disabled cache paths continue using source Cosmos queries. -

-
- -
-
-
-
- - -
-
When enabled, list/feed/search payloads are cached by user and version. Disabling this bypasses cache reads and writes without requiring Redis.
-
-
- - -
Default 120 seconds. User-scoped version invalidation refreshes changed conversations; set to 0 to skip writing new entries.
-
-
-
-
-
-
Runtime Status
- Not loaded -
-
-
-
-
15m Cache Hit Rate
-
Not loaded
-
-
-
-
-
15m Cache Hits / Misses
-
Not loaded
-
-
-
-
-
15m Cache Bypasses / Errors
-
Not loaded
-
-
-
-
-
15m Writes / Invalidations
-
Not loaded
-
-
-
-
-
15m Operation Mix
-
Not loaded
-
-
-
-
-
Last Cache Event
-
Not loaded
-
-
-
-
-
Last Invalidation
-
Not loaded
-
-
-
-
- - Conversation cache metrics are lightweight in-process counters for the current app worker. Application Insights logs remain the durable fleet-wide source for cache warnings and fallback behavior. -
-
-
-
- +
@@ -1586,43 +1140,4 @@
-
-
-
- Front Door -
- -
-

Configure Front Door URL for authentication and redirect flows.

- -
- - - -
- -
- - - - The base URL of your Front Door or load balancer. The system will automatically generate: -
    -
  • Home redirect: https://your-frontdoor.azurefd.net
  • -
  • OAuth2 redirect: https://your-frontdoor.azurefd.net/getAToken
  • -
-
-
-
diff --git a/application/single_app/templates/admin/_panes/network.html b/application/single_app/templates/admin/_panes/network.html new file mode 100644 index 000000000..83ba4fb03 --- /dev/null +++ b/application/single_app/templates/admin/_panes/network.html @@ -0,0 +1,41 @@ +
+
+
+
+ Front Door +
+ +
+

Configure Front Door URL for authentication and redirect flows.

+ +
+ + + +
+ +
+ + + + The base URL of your Front Door or load balancer. The system will automatically generate: +
    +
  • Home redirect: https://your-frontdoor.azurefd.net
  • +
  • OAuth2 redirect: https://your-frontdoor.azurefd.net/getAToken
  • +
+
+
+
+
diff --git a/application/single_app/templates/admin/_panes/redis-caching.html b/application/single_app/templates/admin/_panes/redis-caching.html new file mode 100644 index 000000000..16d846c71 --- /dev/null +++ b/application/single_app/templates/admin/_panes/redis-caching.html @@ -0,0 +1,448 @@ +
+ {% set enable_dai_debug = settings.enable_dai_debug | default(false) %} +

+ Configure Redis cache to improve enterprise scale and performance by caching session data. Enabling Redis allows you to horizontally scale your application across multiple instances without losing session data. +

+
+
+ Redis Cache +
+

+ Enable Redis Cache to store session data in a distributed cache. This is recommended for production and multi-instance deployments. +

+
+ + + +
+
+
+ +

(example: simple-chat.redis.cache.windows.net)

+ +
+
+ + +
+
+ +
+ + +
+
+ Enter the full Key Vault secret name. + Enable Key Vault for Agent and Action Secrets + must be enabled and configured. +
+
+ +
+
+
+
+
+
+ Redis Monitoring +
+

+ Monitor Redis availability, memory pressure, hit rate, evictions, and runtime cache usage before Redis-backed document list caching is enabled. +

+
+
+ + +
+
+
+
+
+
+
Configuration
+ Not loaded +
+
+
+
+
Health
+ Not loaded +
+
+
+
+
App Cache Runtime
+ Not loaded +
+
+
+
+
Session Runtime
+ Not loaded +
+
+
+
+
+
+
Ping Latency
+
Not loaded
+
+
+
+
+
Memory Usage
+
Not loaded
+
Not loaded
+
+
+
+
+
Connected Clients
+
Not loaded
+
+
+
+
+
Ops/sec
+
Not loaded
+
+
+
+
+
Keyspace Hit Rate
+
Not loaded
+
+
+
+
+
Tracked Keys
+
Not loaded
+
+
+
+
+
DAI Version Markers
+
Not loaded
+
Not loaded
+
+
+
+
+
DAI Cache Payloads
+
Not loaded
+
Not loaded
+
+
+
+
+
Expired / Evicted Keys
+
+ Not loaded + / + Not loaded +
+
+
+
+
+
Fragmentation Ratio
+
Not loaded
+
+
+
+
+
Error Replies
+
Not loaded
+
+
+
+
+
Rejected Connections
+
Not loaded
+
+
+
+
+
Redis Version
+
Not loaded
+
+
+
+
+
Monitoring Source
+
Not loaded
+
+
+
+
+
Last Checked
+
Not loaded
+
+
+
+
+
Last Error
+
Not loaded
+
+
+
+
+
+ + + +
+
+
+
+ Conversation Cache +
+

+ Cache conversation list, feed, and advanced-search results per user. Redis is optional; cache misses and disabled cache paths continue using source Cosmos queries. +

+
+ +
+
+
+
+ + +
+
When enabled, list/feed/search payloads are cached by user and version. Disabling this bypasses cache reads and writes without requiring Redis.
+
+
+ + +
Default 120 seconds. User-scoped version invalidation refreshes changed conversations; set to 0 to skip writing new entries.
+
+
+
+
+
+
Runtime Status
+ Not loaded +
+
+
+
+
15m Cache Hit Rate
+
Not loaded
+
+
+
+
+
15m Cache Hits / Misses
+
Not loaded
+
+
+
+
+
15m Cache Bypasses / Errors
+
Not loaded
+
+
+
+
+
15m Writes / Invalidations
+
Not loaded
+
+
+
+
+
15m Operation Mix
+
Not loaded
+
+
+
+
+
Last Cache Event
+
Not loaded
+
+
+
+
+
Last Invalidation
+
Not loaded
+
+
+
+
+ + Conversation cache metrics are lightweight in-process counters for the current app worker. Application Insights logs remain the durable fleet-wide source for cache warnings and fallback behavior. +
+
+
+
+ +
diff --git a/application/single_app/templates/admin_settings.html b/application/single_app/templates/admin_settings.html index 39ebbc3fe..5e26216b9 100644 --- a/application/single_app/templates/admin_settings.html +++ b/application/single_app/templates/admin_settings.html @@ -1006,7 +1006,9 @@

12. Enhanced Citations and Image Generation

{% include "admin/_panes/data-management.html" %} - {% include "admin/_panes/scale.html" %} + {% include "admin/_panes/redis-caching.html" %} + {% include "admin/_panes/cosmos.html" %} + {% include "admin/_panes/network.html" %} {% include "admin/_panes/workspace-identities.html" %} From 08f5a99caa660d31316872236edf78ffabbc4515 Mon Sep 17 00:00:00 2001 From: Paul Lizer Date: Wed, 19 Aug 2026 15:19:56 -0400 Subject: [PATCH 17/25] Bump version and document the stage D card-move pattern Records the split pattern that Governance and Scale established, the property that moving a whole tab between groups needs only a navigation map change, and the gap that has to close before the remaining groups can move: the split tool handles one source pane at a time, but the rest pull cards from several. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- application/single_app/config.py | 2 +- .../ADMIN_SETTINGS_IA_REWORK_STATUS.md | 42 +++++++++++++++---- docs/explanation/release_notes.md | 19 +++++++++ 3 files changed, 54 insertions(+), 9 deletions(-) diff --git a/application/single_app/config.py b/application/single_app/config.py index 60b856bf6..7dab85ab9 100644 --- a/application/single_app/config.py +++ b/application/single_app/config.py @@ -96,7 +96,7 @@ EXECUTOR_TYPE = 'thread' EXECUTOR_MAX_WORKERS = 30 SESSION_TYPE = 'filesystem' -VERSION = "0.260.010" +VERSION = "0.260.011" IS_DEVELOPMENT = is_development_env_enabled() SESSION_COOKIE_SAMESITE = os.getenv('SESSION_COOKIE_SAMESITE', 'Lax') diff --git a/docs/explanation/features/ADMIN_SETTINGS_IA_REWORK_STATUS.md b/docs/explanation/features/ADMIN_SETTINGS_IA_REWORK_STATUS.md index 7069ef7d2..985751668 100644 --- a/docs/explanation/features/ADMIN_SETTINGS_IA_REWORK_STATUS.md +++ b/docs/explanation/features/ADMIN_SETTINGS_IA_REWORK_STATUS.md @@ -22,7 +22,7 @@ Development ─── feature/admin-settings-ia ──┬── #1297 stage 1 └──► final PR ──► Development ``` -Current version: **0.260.010**. Fingerprint: **462 field names / 110 card ids**. +Current version: **0.260.011**. Fingerprint: **462 field names / 110 card ids**. ### Shipped @@ -33,6 +33,7 @@ Current version: **0.260.010**. Fingerprint: **462 field names / 110 card ids**. | B | `data-requires` dependency announcements with inline mirror and link; File Sync and Permissions wired | | — | Form field contract enforced in CI | | C | Navigation moved into `admin_settings_nav.py` as groups → tabs → sections; both navigations render from it; group level with persisted collapse state and group pills; three-level search | +| D (part 1) | Governance split into three tabs; Scale split into two; Front Door moved to Security; legacy tab redirects added | --- @@ -79,15 +80,40 @@ template uncomposed while referencing a partial-backed card or field. --- -## Next: Stage D — the risky one +## Next: Stage D — the risky one, in progress -Re-home cards into the 14 target groups, **one group per commit**, running the -field contract test on each. The nav map now makes this a two-part edit: move -the card markup between partials, and move its entry between tabs in -`admin_settings_nav.py`. The parity test fails if the two disagree. +Re-home cards into the target groups, **one group per commit**, running the +field contract test on each. -The interim grouping in the map has 12 groups, because Workflow and Data -Lifecycle have no tabs until their cards move out of Workspaces and Safety. +### Proven pattern + +1. Split the source pane with the split tool. It works line by line and + **refuses to write unless every source line lands in exactly one output**. +2. Update `admin_settings_nav.py`: the tab entries and their sections. +3. Update the `{% include %}` list in `admin_settings.html`. +4. Add the old tab id to `LEGACY_TAB_REDIRECTS` in `admin_sidebar_nav.js`. +5. Verify: field contract, nav map test, parity test, Jinja compile, full + regression set. + +**Moving a whole tab between groups needs no markup change** — panes are +independent files and groups are entries in the map, so it is a one-line map +edit. Only moving a *card* between tabs requires markup surgery. Front Door +moved from Scale to Security that way. + +### Done + +| Group | Result | +|---|---| +| Governance | → Feature Governance, Policies, MCP Governance | +| Scale | → Redis & Caching, Cosmos; Front Door moved to Security as Network | + +### Remaining, and the gap to close first + +The remaining groups pull cards from **several** source panes, for example Data +Lifecycle wants Retention and Classification out of Workspaces and Archiving +out of Safety. The split tool only splits one pane at a time, so a +**move-card-between-panes** operation is needed next, with the same +every-line-accounted-for guarantee. Three complications found while splitting: diff --git a/docs/explanation/release_notes.md b/docs/explanation/release_notes.md index 3811e8f15..9da97239a 100644 --- a/docs/explanation/release_notes.md +++ b/docs/explanation/release_notes.md @@ -2,6 +2,25 @@ For feature-focused and fix-focused drill-downs by version, see [Features by Version](/explanation/features/) and [Fixes by Version](/explanation/fixes/). +### **(v0.260.011)** + +#### User Interface Enhancements + +* **Governance And Scale Split Into Focused Tabs** + * Governance held five cards covering three different jobs. It is now **Feature Governance** (which features are governed), **Policies** (the policies themselves), and **MCP Governance**. + * Scale mixed cache configuration with Cosmos capacity, and is now **Redis & Caching** and **Cosmos**. + * **Azure Front Door** moved out of Scale into Security, under a new **Network** tab. It configures authentication and redirect flows rather than throughput, so it never belonged with capacity settings. + * Existing links and bookmarks to `#governance` and `#scale` still work and land on the first tab of each group. + * No settings changed. Every option keeps its name and its saved value. + * (Ref: navigation map, `feature-governance`, `governance-policies`, `mcp-governance`, `redis-caching`, `cosmos`, `network`) + +#### Bug Fixes + +* **Governance Status Messages No Longer Get Stuck On One Tab** + * The inline governance status message lived inside the Governance pane, so a message raised while working in one area could end up rendered on a tab you were not looking at. + * It now sits outside the tabs and is visible wherever you are in Governance. + * (Ref: `governance-status`, `admin_governance.js`) + ### **(v0.260.010)** #### New Features From e6cd25779daed5d72cd3a3416bfb6ded56f313d6 Mon Sep 17 00:00:00 2001 From: Paul Lizer Date: Wed, 19 Aug 2026 15:55:54 -0400 Subject: [PATCH 18/25] Stage D part 2: Data Lifecycle and Chat groups Move cards across panes so they sit with the settings they actually work with, without touching a single field name. Data Lifecycle (new group) Retention retention-policy-section from workspaces Classification document-classification-section from workspaces Archiving conversation-archiving-section from safety These three decide how long content lives and how it is labelled. Archiving was under Safety, which describes what it guards against rather than what it does. Chat (new group) Chat Experience processing-thoughts-section from ai-models chat-file-uploads-section from workspaces (carries nested conversation-contents-drawer-section) workspace-scope-lock-section from workspaces Feedback & Alerts user-feedback-section from safety desktop-notifications-section from safety Moves use move_card.py, which refuses to write unless every source line lands in exactly one output. Nested cards travel with their parent and the gap after a card travels with it, so interleaved modals stay with the tab owning their trigger. Navigation is now 13 groups / 27 tabs / 88 sections. Verified field contract 462 names / 110 card ids, unchanged regression set 32 failures, identical to baseline jinja compile 29/29 admin templates xss sinks 5 changed files pass Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- application/single_app/admin_settings_nav.py | 59 ++- application/single_app/config.py | 2 +- .../templates/admin/_panes/ai-models.html | 21 - .../templates/admin/_panes/archiving.html | 21 + .../admin/_panes/chat-experience.html | 102 +++++ .../admin/_panes/classification.html | 47 +++ .../admin/_panes/feedback-alerts.html | 44 ++ .../templates/admin/_panes/retention.html | 270 ++++++++++++ .../templates/admin/_panes/safety.html | 61 --- .../templates/admin/_panes/workspaces.html | 392 ------------------ .../single_app/templates/admin_settings.html | 9 + .../ADMIN_SETTINGS_IA_REWORK_STATUS.md | 22 +- docs/explanation/release_notes.md | 20 + 13 files changed, 580 insertions(+), 490 deletions(-) create mode 100644 application/single_app/templates/admin/_panes/archiving.html create mode 100644 application/single_app/templates/admin/_panes/chat-experience.html create mode 100644 application/single_app/templates/admin/_panes/classification.html create mode 100644 application/single_app/templates/admin/_panes/feedback-alerts.html create mode 100644 application/single_app/templates/admin/_panes/retention.html diff --git a/application/single_app/admin_settings_nav.py b/application/single_app/admin_settings_nav.py index f256b6ebe..00dd71d46 100644 --- a/application/single_app/admin_settings_nav.py +++ b/application/single_app/admin_settings_nav.py @@ -51,6 +51,26 @@ "label": "Chat", "icon": "bi-chat-square-text", "tabs": [ + { + "id": "chat-experience", + "label": "Chat Experience", + "icon": "bi-chat-square-dots", + "sections": [ + {"id": "processing-thoughts-section", "label": "Processing Thoughts", "icon": "bi-stars"}, + {"id": "chat-file-uploads-section", "label": "Chat File Uploads", "icon": "bi-paperclip"}, + {"id": "conversation-contents-drawer-section", "label": "Conversation Contents Drawer", "icon": "bi-list-nested"}, + {"id": "workspace-scope-lock-section", "label": "Workspace Scope Lock", "icon": "bi-lock"}, + ], + }, + { + "id": "feedback-alerts", + "label": "Feedback & Alerts", + "icon": "bi-chat-square-heart", + "sections": [ + {"id": "user-feedback-section", "label": "User Feedback", "icon": "bi-chat-square-heart"}, + {"id": "desktop-notifications-section", "label": "Desktop Conversation Notifications", "icon": "bi-bell"}, + ], + }, { "id": "citation", "label": "Citations", @@ -73,7 +93,6 @@ "icon": "bi-cpu", "sections": [ {"id": "multi-endpoint-configuration", "label": "Model Endpoints", "icon": "bi-hdd-network"}, - {"id": "processing-thoughts-section", "label": "Processing Thoughts", "icon": "bi-stars"}, {"id": "shared-conversation-file-approvals-section", "label": "Shared Conversation File Approvals", "icon": "bi-file-earmark-lock"}, {"id": "embeddings-config", "label": "Embeddings", "icon": "bi-vector-pen"}, {"id": "image-config", "label": "Image Generation", "icon": "bi-image"}, @@ -117,12 +136,8 @@ {"id": "group-workspaces-section", "label": "Group Workspaces", "icon": "bi-people"}, {"id": "public-workspaces-section", "label": "Public Workspaces", "icon": "bi-globe"}, {"id": "file-sharing-section", "label": "File Sharing", "icon": "bi-share"}, - {"id": "chat-file-uploads-section", "label": "Chat File Uploads", "icon": "bi-paperclip"}, {"id": "metadata-extraction-section", "label": "Metadata Extraction", "icon": "bi-file-earmark-code"}, {"id": "multimodal-vision-section", "label": "Multi-Modal Vision Analysis", "icon": "bi-eye"}, - {"id": "document-classification-section", "label": "Document Classification", "icon": "bi-tags"}, - {"id": "retention-policy-section", "label": "Retention Policy", "icon": "bi-hourglass-split"}, - {"id": "workspace-scope-lock-section", "label": "Workspace Scope Lock", "icon": "bi-lock"}, {"id": "user-agreement-section", "label": "User Agreement", "icon": "bi-file-earmark-check"}, ], }, @@ -187,10 +202,7 @@ "icon": "bi-shield-check", "sections": [ {"id": "content-safety-section", "label": "Content Safety", "icon": "bi-shield-exclamation"}, - {"id": "user-feedback-section", "label": "User Feedback", "icon": "bi-chat-square-heart"}, - {"id": "desktop-notifications-section", "label": "Desktop Conversation Notifications", "icon": "bi-bell"}, {"id": "permissions-section", "label": "Permissions", "icon": "bi-person-check"}, - {"id": "conversation-archiving-section", "label": "Conversation Archiving", "icon": "bi-archive"}, ], }, { @@ -238,6 +250,37 @@ }, ], }, + { + "id": "data-lifecycle", + "label": "Data Lifecycle", + "icon": "bi-hourglass-split", + "tabs": [ + { + "id": "retention", + "label": "Retention", + "icon": "bi-hourglass-split", + "sections": [ + {"id": "retention-policy-section", "label": "Retention Policy", "icon": "bi-hourglass-split"}, + ], + }, + { + "id": "classification", + "label": "Classification", + "icon": "bi-tags", + "sections": [ + {"id": "document-classification-section", "label": "Document Classification", "icon": "bi-tags"}, + ], + }, + { + "id": "archiving", + "label": "Archiving", + "icon": "bi-archive", + "sections": [ + {"id": "conversation-archiving-section", "label": "Conversation Archiving", "icon": "bi-archive"}, + ], + }, + ], + }, { "id": "backup-recovery", "label": "Backup & Recovery", diff --git a/application/single_app/config.py b/application/single_app/config.py index 7dab85ab9..0244ed3e0 100644 --- a/application/single_app/config.py +++ b/application/single_app/config.py @@ -96,7 +96,7 @@ EXECUTOR_TYPE = 'thread' EXECUTOR_MAX_WORKERS = 30 SESSION_TYPE = 'filesystem' -VERSION = "0.260.011" +VERSION = "0.260.012" IS_DEVELOPMENT = is_development_env_enabled() SESSION_COOKIE_SAMESITE = os.getenv('SESSION_COOKIE_SAMESITE', 'Lax') diff --git a/application/single_app/templates/admin/_panes/ai-models.html b/application/single_app/templates/admin/_panes/ai-models.html index 3df45b251..f12b06236 100644 --- a/application/single_app/templates/admin/_panes/ai-models.html +++ b/application/single_app/templates/admin/_panes/ai-models.html @@ -236,27 +236,6 @@
Global Endpoints
-
-
- Processing Thoughts -
-

When enabled, real-time processing steps are shown to users during chat responses and persisted for later review.

-
- - - -
-
- -
Shared Conversation File Approvals diff --git a/application/single_app/templates/admin/_panes/archiving.html b/application/single_app/templates/admin/_panes/archiving.html new file mode 100644 index 000000000..0d56b6223 --- /dev/null +++ b/application/single_app/templates/admin/_panes/archiving.html @@ -0,0 +1,21 @@ +
+
+
+ Conversation Archiving +
+

When enabled, conversation deletions will be archived instead of permanently deleted.

+
+ + + +
+
+
\ No newline at end of file diff --git a/application/single_app/templates/admin/_panes/chat-experience.html b/application/single_app/templates/admin/_panes/chat-experience.html new file mode 100644 index 000000000..223e1beb0 --- /dev/null +++ b/application/single_app/templates/admin/_panes/chat-experience.html @@ -0,0 +1,102 @@ +
+
+
+ Processing Thoughts +
+

When enabled, real-time processing steps are shown to users during chat responses and persisted for later review.

+
+ + + +
+
+ + +
+
+ Chat File Uploads +
+

+ Control whether users can upload files directly into chat conversations. +

+
+ + + +
+ +
+
+ Conversation Contents Drawer +
+

+ Let users navigate long conversations from a list of their prompts. +

+
+ + + +
+

+ When enabled, users can turn the drawer off for their own account from Profile settings. +

+
+
+ + + +
+

+ Required app role value: ChatFileUploadUser. Assign this role to users or groups in the Enterprise App before enabling the requirement. Existing chat attachments remain visible; this only controls new uploads. +

+
+
+
+ Workspace Scope Lock +
+

+ Control whether users can unlock workspace scope in chat conversations. When scope is locked, conversations are restricted to the workspaces that produced search results, preventing accidental cross-contamination with other data sources. +

+
+ + + +
+
+ + +
\ No newline at end of file diff --git a/application/single_app/templates/admin/_panes/classification.html b/application/single_app/templates/admin/_panes/classification.html new file mode 100644 index 000000000..597736e46 --- /dev/null +++ b/application/single_app/templates/admin/_panes/classification.html @@ -0,0 +1,47 @@ +
+
+
+ Document Classification +
+

+ Enable this feature to allow users to classify documents uploaded to their workspaces using predefined categories. +

+
+ + + +
+ + +
+
Classification Categories
+

Define the labels and corresponding colors for document classification.

+ + + + + + + + + + + +
LabelColorActions
+ + + +
+
+ + +
\ No newline at end of file diff --git a/application/single_app/templates/admin/_panes/feedback-alerts.html b/application/single_app/templates/admin/_panes/feedback-alerts.html new file mode 100644 index 000000000..4ee6ae562 --- /dev/null +++ b/application/single_app/templates/admin/_panes/feedback-alerts.html @@ -0,0 +1,44 @@ +
+
+
+ User Feedback +
+

Enable user feedback (thumbs up/down) for AI responses.

+
+ + + +
+
+
+
+ Desktop Conversation Notifications +
+

+ Allow users to receive an operating system notification when an AI response finishes while SimpleChat is open in a hidden or unfocused browser tab. +

+
+ + +
+

+ Users can turn notifications off from Profile. Browser permission is required, and notifications stop when the SimpleChat tab is closed. +

+
+
\ No newline at end of file diff --git a/application/single_app/templates/admin/_panes/retention.html b/application/single_app/templates/admin/_panes/retention.html new file mode 100644 index 000000000..c3aa2258a --- /dev/null +++ b/application/single_app/templates/admin/_panes/retention.html @@ -0,0 +1,270 @@ +
+
+
+ Retention Policy +
+

Automatically delete aged conversations and documents based on configurable retention periods. Users, group owners, and public workspace admins can set their own retention policies.

+ + + + +
+
+
+ + + +
+
+
+
+ + + +
+
+
+
+ + + +
+
+
+ + +
+
Default Retention Policies
+

Set organization-wide default retention periods for each workspace type. Users can override these defaults with their own preferences. Setting a default here means new users/workspaces will start with this retention period.

+ + +
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + Force Push: Override all user/group/workspace custom retention policies with the organization defaults above. Users will then use the organization default until they set their own preference. +
+ +
+
+
+ + +
+ + + Retention policy will run once daily at this hour (UTC timezone). +
+ + +
+
+ +
+ {% if settings.retention_policy_last_run %} + {{ settings.retention_policy_last_run }} + {% else %} + Never run + {% endif %} +
+
+
+ +
+ {% if settings.retention_policy_next_run %} + {{ settings.retention_policy_next_run }} + {% else %} + Not scheduled + {% endif %} +
+
+
+ + +
+ + + Trigger retention policy execution immediately for selected workspace types, bypassing the scheduled time. + +
+ + +
+ + +
\ No newline at end of file diff --git a/application/single_app/templates/admin/_panes/safety.html b/application/single_app/templates/admin/_panes/safety.html index 5fda7791e..c4973acdb 100644 --- a/application/single_app/templates/admin/_panes/safety.html +++ b/application/single_app/templates/admin/_panes/safety.html @@ -171,49 +171,7 @@
-
-
- User Feedback -
-

Enable user feedback (thumbs up/down) for AI responses.

-
- - - -
-
-
-
- Desktop Conversation Notifications -
-

- Allow users to receive an operating system notification when an AI response finishes while SimpleChat is open in a hidden or unfocused browser tab. -

-
- - -
-

- Users can turn notifications off from Profile. Browser permission is required, and notifications stop when the SimpleChat tab is closed. -

-
-
-
- Conversation Archiving -
-

When enabled, conversation deletions will be archived instead of permanently deleted.

-
- - - -
-
diff --git a/application/single_app/templates/admin/_panes/workspaces.html b/application/single_app/templates/admin/_panes/workspaces.html index 3bf8d4aa9..9200f71d9 100644 --- a/application/single_app/templates/admin/_panes/workspaces.html +++ b/application/single_app/templates/admin/_panes/workspaces.html @@ -506,64 +506,6 @@
-
-
- Chat File Uploads -
-

- Control whether users can upload files directly into chat conversations. -

-
- - - -
- -
-
- Conversation Contents Drawer -
-

- Let users navigate long conversations from a list of their prompts. -

-
- - - -
-

- When enabled, users can turn the drawer off for their own account from Profile settings. -

-
-
- - - -
-

- Required app role value: ChatFileUploadUser. Assign this role to users or groups in the Enterprise App before enabling the requirement. Existing chat attachments remain visible; this only controls new uploads. -

-
@@ -698,340 +640,6 @@
-
-
- Document Classification -
-

- Enable this feature to allow users to classify documents uploaded to their workspaces using predefined categories. -

-
- - - -
- - -
-
Classification Categories
-

Define the labels and corresponding colors for document classification.

- - - - - - - - - - - -
LabelColorActions
- - - -
-
- - -
-
- Retention Policy -
-

Automatically delete aged conversations and documents based on configurable retention periods. Users, group owners, and public workspace admins can set their own retention policies.

- - - - -
-
-
- - - -
-
-
-
- - - -
-
-
-
- - - -
-
-
- - -
-
Default Retention Policies
-

Set organization-wide default retention periods for each workspace type. Users can override these defaults with their own preferences. Setting a default here means new users/workspaces will start with this retention period.

- - -
-
- -
-
- - -
-
- - -
-
- - -
-
- -
-
- - -
-
- - -
-
- - -
-
- -
-
- - -
-
- - -
-
- - -
-
-
- - Force Push: Override all user/group/workspace custom retention policies with the organization defaults above. Users will then use the organization default until they set their own preference. -
- -
-
-
- - -
- - - Retention policy will run once daily at this hour (UTC timezone). -
- - -
-
- -
- {% if settings.retention_policy_last_run %} - {{ settings.retention_policy_last_run }} - {% else %} - Never run - {% endif %} -
-
-
- -
- {% if settings.retention_policy_next_run %} - {{ settings.retention_policy_next_run }} - {% else %} - Not scheduled - {% endif %} -
-
-
- - -
- - - Trigger retention policy execution immediately for selected workspace types, bypassing the scheduled time. - -
- - -
- - -
-
- Workspace Scope Lock -
-

- Control whether users can unlock workspace scope in chat conversations. When scope is locked, conversations are restricted to the workspaces that produced search results, preventing accidental cross-contamination with other data sources. -

-
- - - -
-
- -
User Agreement diff --git a/application/single_app/templates/admin_settings.html b/application/single_app/templates/admin_settings.html index 5e26216b9..65e958b1d 100644 --- a/application/single_app/templates/admin_settings.html +++ b/application/single_app/templates/admin_settings.html @@ -1018,9 +1018,18 @@

12. Enhanced Citations and Image Generation

{% include "admin/_panes/workspaces.html" %} + {# Data Lifecycle: what happens to user data over time #} + {% include "admin/_panes/retention.html" %} + {% include "admin/_panes/classification.html" %} + {% include "admin/_panes/archiving.html" %} + {% include "admin/_panes/citation.html" %} + {# Chat: the conversation surface itself #} + {% include "admin/_panes/chat-experience.html" %} + {% include "admin/_panes/feedback-alerts.html" %} + {% include "admin/_panes/safety.html" %} diff --git a/docs/explanation/features/ADMIN_SETTINGS_IA_REWORK_STATUS.md b/docs/explanation/features/ADMIN_SETTINGS_IA_REWORK_STATUS.md index 985751668..0e9a7ef64 100644 --- a/docs/explanation/features/ADMIN_SETTINGS_IA_REWORK_STATUS.md +++ b/docs/explanation/features/ADMIN_SETTINGS_IA_REWORK_STATUS.md @@ -22,7 +22,8 @@ Development ─── feature/admin-settings-ia ──┬── #1297 stage 1 └──► final PR ──► Development ``` -Current version: **0.260.011**. Fingerprint: **462 field names / 110 card ids**. +Current version: **0.260.012**. Fingerprint: **462 field names / 110 card ids**. +Navigation: **13 groups / 27 tabs / 88 sections**. ### Shipped @@ -106,14 +107,21 @@ moved from Scale to Security that way. |---|---| | Governance | → Feature Governance, Policies, MCP Governance | | Scale | → Redis & Caching, Cosmos; Front Door moved to Security as Network | +| Data Lifecycle | **New group.** Retention + Classification pulled from Workspaces, Archiving pulled from Safety | +| Chat | **New group.** Chat Experience (thoughts from AI Models, file uploads + contents drawer and scope lock from Workspaces), Feedback & Alerts (feedback + desktop notifications from Safety) | -### Remaining, and the gap to close first +### The cross-pane move tool -The remaining groups pull cards from **several** source panes, for example Data -Lifecycle wants Retention and Classification out of Workspaces and Archiving -out of Safety. The split tool only splits one pane at a time, so a -**move-card-between-panes** operation is needed next, with the same -every-line-accounted-for guarantee. +`move_card.py` moves a top-level card from one pane to another with the same +every-line-accounted-for guarantee as the split tool. Two behaviours matter: + +- **Nested cards travel with their parent automatically**, because the card + scanner skips over card bodies. `conversation-contents-drawer-section` moved + inside `chat-file-uploads-section` without being named. +- **The gap after a card travels with it**, which is what correctly routes an + interleaved modal to the tab that owns its trigger. + +### Remaining Three complications found while splitting: diff --git a/docs/explanation/release_notes.md b/docs/explanation/release_notes.md index 9da97239a..3d12d8127 100644 --- a/docs/explanation/release_notes.md +++ b/docs/explanation/release_notes.md @@ -2,6 +2,26 @@ For feature-focused and fix-focused drill-downs by version, see [Features by Version](/explanation/features/) and [Fixes by Version](/explanation/fixes/). +### **(v0.260.012)** + +#### User Interface Enhancements + +* **New Data Lifecycle Group For Retention, Classification And Archiving** + * Retention policy, document classification and conversation archiving all decide how long content lives and how it is labelled, but they were split across Workspaces and Safety. They now sit together in a **Data Lifecycle** group with a tab each: **Retention**, **Classification** and **Archiving**. + * Conversation archiving in particular was buried under Safety, which described what it protects against rather than what it does. + * (Ref: navigation map, `retention-policy-section`, `document-classification-section`, `conversation-archiving-section`) + +* **Chat Group Gathers The Settings That Shape A Conversation** + * Settings that change what a conversation looks and behaves like were spread across AI Models, Workspaces and Safety. The **Chat** group now holds them in two tabs. + * **Chat Experience** collects model thought display, chat file uploads (with the conversation contents drawer) and workspace scope lock. + * **Feedback & Alerts** collects user feedback and desktop notifications, which are both about how the app talks back to the user rather than about safety enforcement. + * (Ref: `chat-experience`, `feedback-alerts`, `processing-thoughts-section`, `chat-file-uploads-section`, `workspace-scope-lock-section`, `user-feedback-section`, `desktop-notifications-section`) + +* **Settings Keep Their Values Through The Move** + * Cards were relocated between tabs without renaming a single field, so every saved value is preserved and the form submits exactly the payload it did before. + * Sidebar search still finds a setting by group, tab or card name, so you can reach anything without knowing where it now lives. + * (Ref: admin settings field contract, `admin_settings_nav.py`) + ### **(v0.260.011)** #### User Interface Enhancements From b4335658f9e3dbe62d0594a7957520f6425124b5 Mon Sep 17 00:00:00 2001 From: Paul Lizer Date: Wed, 19 Aug 2026 16:11:52 -0400 Subject: [PATCH 19/25] Stage D part 3: dismantle General and Security General had grown into a catch-all of eleven unrelated cards. Security held one card while a separate Safety tab mixed content filtering with role permissions. Both are broken up here. Appearance Branding branding, home page text, appearance Notices & Agreements classification banner, chat AI notice, terms of use, user agreement (from workspaces) Pages & Links static pages, external links (from general) Security Access & Roles permissions-section who gets in, and as what Secrets keyvault-section was the "security" tab Content Safety content-safety-section what may be said once in Session system-settings-section interim home, see below Network front-door-section Operations Logging becomes Logging & Health, gaining health check and API documentation from General. Help Gains Support Menu from General. system-settings-section is a mixed card: four of its eight fields are idle timeout, so Session is the plurality home. The remaining fields are split out to their real homes in a later change. Two hardcoded-id traps closed openKeyVaultSettings switched tabs by the literal id "security-tab", so the Data Management link to Key Vault broke the moment that tab was renamed. The function was dead weight: the link now carries data-admin-link and the tab is resolved from the DOM. The landing pane was hardcoded twice, as "show active" in one pane and showAdminTab('general') in the sidebar. Splitting that pane left Admin Settings with no active pane at all. Every pane now renders {% if admin_landing_tab == '' %} show active{% endif %}, fed by get_landing_tab_id(), and the sidebar reads the first rendered tab. Four test files hardcoded 'general' or 'security'. They now assert the durable properties instead: Latest Features is last, exactly one pane resolves active, every tab renders exactly one pane. Navigation is now 13 groups / 31 tabs / 88 sections. Verified field contract 462 names / 110 card ids, unchanged regression set 32 failures, identical to baseline jinja compile 33/33 admin templates xss sinks 41 changed files pass render check exactly one pane resolves active, and it is the first tab of the first group Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- application/single_app/admin_settings_nav.py | 85 ++- application/single_app/app.py | 3 +- application/single_app/config.py | 2 +- .../static/js/admin/admin_data_management.js | 17 +- .../static/js/admin/admin_sidebar_nav.js | 12 +- .../templates/admin/_panes/access-roles.html | 49 ++ .../templates/admin/_panes/agents.html | 2 +- .../templates/admin/_panes/ai-models.html | 2 +- .../templates/admin/_panes/archiving.html | 2 +- .../templates/admin/_panes/branding.html | 165 +++++ .../admin/_panes/chat-experience.html | 2 +- .../templates/admin/_panes/citation.html | 2 +- .../admin/_panes/classification.html | 2 +- .../{safety.html => content-safety.html} | 45 +- .../admin/_panes/control-center-config.html | 2 +- .../templates/admin/_panes/cosmos.html | 2 +- .../templates/admin/_panes/custom-pages.html | 54 +- .../admin/_panes/data-management.html | 4 +- .../admin/_panes/feature-governance.html | 2 +- .../admin/_panes/feedback-alerts.html | 2 +- .../templates/admin/_panes/file-sync.html | 2 +- .../templates/admin/_panes/general.html | 682 ------------------ .../admin/_panes/governance-policies.html | 2 +- .../admin/_panes/latest-features.html | 8 +- .../templates/admin/_panes/logging.html | 99 ++- .../admin/_panes/mcp-governance.html | 2 +- .../templates/admin/_panes/network.html | 2 +- .../templates/admin/_panes/notices.html | 288 ++++++++ .../templates/admin/_panes/redis-caching.html | 2 +- .../templates/admin/_panes/retention.html | 2 +- .../admin/_panes/search-extract.html | 2 +- .../_panes/{security.html => secrets.html} | 2 +- .../templates/admin/_panes/send-feedback.html | 2 +- .../templates/admin/_panes/session.html | 87 +++ .../templates/admin/_panes/support-menu.html | 105 +++ .../admin/_panes/workspace-identities.html | 2 +- .../templates/admin/_panes/workspaces.html | 105 +-- .../single_app/templates/admin_settings.html | 10 +- .../ADMIN_SETTINGS_IA_REWORK_STATUS.md | 27 +- docs/explanation/release_notes.md | 32 + .../test_admin_latest_features_tab.py | 35 +- .../test_admin_settings_nav_map.py | 17 +- .../test_admin_source_review_depth_save.py | 6 +- .../test_data_management_security_patterns.py | 1 - 44 files changed, 1063 insertions(+), 915 deletions(-) create mode 100644 application/single_app/templates/admin/_panes/access-roles.html create mode 100644 application/single_app/templates/admin/_panes/branding.html rename application/single_app/templates/admin/_panes/{safety.html => content-safety.html} (75%) delete mode 100644 application/single_app/templates/admin/_panes/general.html create mode 100644 application/single_app/templates/admin/_panes/notices.html rename application/single_app/templates/admin/_panes/{security.html => secrets.html} (99%) create mode 100644 application/single_app/templates/admin/_panes/session.html create mode 100644 application/single_app/templates/admin/_panes/support-menu.html diff --git a/application/single_app/admin_settings_nav.py b/application/single_app/admin_settings_nav.py index 00dd71d46..e10f54c75 100644 --- a/application/single_app/admin_settings_nav.py +++ b/application/single_app/admin_settings_nav.py @@ -19,29 +19,35 @@ "icon": "bi-palette", "tabs": [ { - "id": "general", - "label": "General", - "icon": "bi-gear", + "id": "branding", + "label": "Branding", + "icon": "bi-palette", "sections": [ {"id": "branding-section", "label": "Branding", "icon": "bi-palette"}, {"id": "home-page-text-section", "label": "Home Page Text", "icon": "bi-house"}, {"id": "appearance-section", "label": "Appearance", "icon": "bi-brush"}, - {"id": "health-check-section", "label": "Health Check", "icon": "bi-heart-pulse"}, - {"id": "swagger-section", "label": "API Documentation", "icon": "bi-file-earmark-code"}, + ], + }, + { + # Everything the app states to the user before or during use: + # banners, notices and the agreements they must accept. + "id": "notices", + "label": "Notices & Agreements", + "icon": "bi-megaphone", + "sections": [ {"id": "classification-banner-section", "label": "Classification Banner", "icon": "bi-shield-exclamation"}, {"id": "ai-notice-section", "label": "Chat AI Notice", "icon": "bi-robot"}, {"id": "terms-of-use-section", "label": "Terms of Use", "icon": "bi-door-open"}, - {"id": "support-menu-section", "label": "Support", "icon": "bi-life-preserver"}, - {"id": "external-links-section", "label": "External Links", "icon": "bi-box-arrow-up-right"}, - {"id": "system-settings-section", "label": "System Settings", "icon": "bi-gear-fill"}, + {"id": "user-agreement-section", "label": "User Agreement", "icon": "bi-file-earmark-check"}, ], }, { "id": "custom-pages", - "label": "Custom Pages", + "label": "Pages & Links", "icon": "bi-window-plus", "sections": [ {"id": "custom-pages-section", "label": "Static Pages", "icon": "bi-file-earmark-richtext"}, + {"id": "external-links-section", "label": "External Links", "icon": "bi-box-arrow-up-right"}, ], }, ], @@ -138,7 +144,6 @@ {"id": "file-sharing-section", "label": "File Sharing", "icon": "bi-share"}, {"id": "metadata-extraction-section", "label": "Metadata Extraction", "icon": "bi-file-earmark-code"}, {"id": "multimodal-vision-section", "label": "Multi-Modal Vision Analysis", "icon": "bi-eye"}, - {"id": "user-agreement-section", "label": "User Agreement", "icon": "bi-file-earmark-check"}, ], }, { @@ -189,20 +194,40 @@ "icon": "bi-shield-lock", "tabs": [ { - "id": "security", - "label": "Security", - "icon": "bi-shield-lock", + # Who can get in and what role they need. Distinct from Content + # Safety, which is about what may be said once you are in. + "id": "access-roles", + "label": "Access & Roles", + "icon": "bi-person-check", + "sections": [ + {"id": "permissions-section", "label": "Permissions", "icon": "bi-person-check"}, + ], + }, + { + "id": "secrets", + "label": "Secrets", + "icon": "bi-safe", "sections": [ {"id": "keyvault-section", "label": "Key Vault", "icon": "bi-safe"}, ], }, { - "id": "safety", - "label": "Safety", - "icon": "bi-shield-check", + "id": "content-safety", + "label": "Content Safety", + "icon": "bi-shield-exclamation", "sections": [ {"id": "content-safety-section", "label": "Content Safety", "icon": "bi-shield-exclamation"}, - {"id": "permissions-section", "label": "Permissions", "icon": "bi-person-check"}, + ], + }, + { + # Interim home for the mixed System Settings card. Four of its + # eight fields are idle-timeout, which is the plurality; the + # rest are split out to their real homes in a later change. + "id": "session", + "label": "Session", + "icon": "bi-hourglass-split", + "sections": [ + {"id": "system-settings-section", "label": "System Settings", "icon": "bi-sliders"}, ], }, { @@ -348,12 +373,14 @@ }, { "id": "logging", - "label": "Logging", + "label": "Logging & Health", "icon": "bi-journal-text", "sections": [ {"id": "application-insights-section", "label": "Application Insights", "icon": "bi-graph-up"}, {"id": "debug-logging-section", "label": "Debug Logging", "icon": "bi-bug"}, {"id": "file-processing-logs-section", "label": "File Process Logging", "icon": "bi-file-earmark-text"}, + {"id": "health-check-section", "label": "Health Check", "icon": "bi-heart-pulse"}, + {"id": "swagger-section", "label": "API Documentation", "icon": "bi-file-earmark-code"}, ], }, ], @@ -363,6 +390,16 @@ "label": "Help", "icon": "bi-life-preserver", "tabs": [ + { + # Where an admin looks first when a user needs help, ahead of + # raising it with the project. + "id": "support-menu", + "label": "Support Menu", + "icon": "bi-life-preserver", + "sections": [ + {"id": "support-menu-section", "label": "Support", "icon": "bi-life-preserver"}, + ], + }, { "id": "send-feedback", "label": "Send Feedback", @@ -408,6 +445,18 @@ def get_group_for_tab(tab_id): return None +def get_landing_tab_id(): + """Return the tab an admin lands on when no tab is requested. + + This is the first tab of the first group rather than a fixed id, so the + landing pane follows the navigation map as cards are regrouped. Latest + Features is pinned last precisely so it can never win this. + """ + for _, tab in iter_tabs(): + return tab["id"] + return None + + def get_section_ids(): """Return every section target declared across the navigation.""" return [ diff --git a/application/single_app/app.py b/application/single_app/app.py index 35cc2d336..d8f837e33 100644 --- a/application/single_app/app.py +++ b/application/single_app/app.py @@ -31,7 +31,7 @@ from functions_content import * from functions_documents import * from functions_latest_features_nav import should_hide_latest_features_nav -from admin_settings_nav import ADMIN_NAV +from admin_settings_nav import ADMIN_NAV, get_landing_tab_id from functions_search import * from functions_settings import * from functions_mcp_server_config import is_mcp_ui_enabled @@ -600,6 +600,7 @@ def inject_settings(): user_settings=user_settings, custom_pages_nav=custom_pages_nav, admin_nav=ADMIN_NAV, + admin_landing_tab=get_landing_tab_id(), latest_features_current_version=VERSION, latest_features_nav_hidden=latest_features_nav_hidden, latest_features_nav_hidden_by_development=IS_DEVELOPMENT, diff --git a/application/single_app/config.py b/application/single_app/config.py index 0244ed3e0..e1fb9e3b7 100644 --- a/application/single_app/config.py +++ b/application/single_app/config.py @@ -96,7 +96,7 @@ EXECUTOR_TYPE = 'thread' EXECUTOR_MAX_WORKERS = 30 SESSION_TYPE = 'filesystem' -VERSION = "0.260.012" +VERSION = "0.260.013" IS_DEVELOPMENT = is_development_env_enabled() SESSION_COOKIE_SAMESITE = os.getenv('SESSION_COOKIE_SAMESITE', 'Lax') diff --git a/application/single_app/static/js/admin/admin_data_management.js b/application/single_app/static/js/admin/admin_data_management.js index 481b2069f..e8c2761a9 100644 --- a/application/single_app/static/js/admin/admin_data_management.js +++ b/application/single_app/static/js/admin/admin_data_management.js @@ -388,7 +388,8 @@ function bindEvents() { pendingDataManagementBackupDelete = null; }); elements.dataManagementConfirmBackupDeleteBtn?.addEventListener("click", deleteDataManagementBackup); - elements.dataManagementKeyVaultLink?.addEventListener("click", openKeyVaultSettings); + // The Key Vault link carries data-admin-link, so admin_card_links.js + // resolves the owning tab from the DOM and scrolls to the card. elements.dataManagementCosmosEditorOpenDangerBtn?.addEventListener("click", showCosmosEditorDangerModal); elements.datamanagementcosmoseditordangeraccept?.addEventListener("change", updateCosmosEditorDangerAcceptState); elements.dataManagementCosmosEditorAcceptDangerBtn?.addEventListener("click", acceptCosmosEditorDanger); @@ -909,20 +910,6 @@ function setKeyStorageAlert(variant, iconClass, title, message, linkText) { setText(elements.dataManagementKeyVaultLink, linkText); } -function openKeyVaultSettings(event) { - event?.preventDefault(); - const securityTabButton = document.getElementById("security-tab"); - if (securityTabButton && window.bootstrap?.Tab) { - window.bootstrap.Tab.getOrCreateInstance(securityTabButton).show(); - } else if (securityTabButton) { - securityTabButton.click(); - } - window.location.hash = "security"; - window.setTimeout(() => { - document.getElementById("keyvault-section")?.scrollIntoView({ block: "start", behavior: "smooth" }); - }, 100); -} - function collectSettings() { const backupStorageAuthenticationType = getValue(elements.datamanagementstorageauth) || backupStorageAuthManagedIdentity; return { diff --git a/application/single_app/static/js/admin/admin_sidebar_nav.js b/application/single_app/static/js/admin/admin_sidebar_nav.js index edad020d8..706c87e5f 100644 --- a/application/single_app/static/js/admin/admin_sidebar_nav.js +++ b/application/single_app/static/js/admin/admin_sidebar_nav.js @@ -145,12 +145,15 @@ function initAdminSidebarNav() { // Set the initial active tab - but only if no tab is already active. // Latest Features is deliberately excluded so it never opens by default. + // The landing tab is whichever tab the nav map renders first rather than a + // hardcoded id, so it stays correct as the information architecture moves. const activeTab = document.querySelector('.admin-nav-tab.active, .admin-nav-section.active'); if (!activeTab) { - const firstTab = document.querySelector('.admin-nav-tab[data-tab="general"]'); - if (firstTab) { + const firstTab = document.querySelector('.admin-nav-tab[data-tab]'); + const firstTabId = firstTab ? firstTab.getAttribute('data-tab') : null; + if (firstTabId) { firstTab.classList.add('active'); - showAdminTab('general'); + showAdminTab(firstTabId); } } else { console.log('initAdminSidebarNav - Found existing active tab, preserving current state:', activeTab.getAttribute('data-tab')); @@ -288,6 +291,9 @@ function revealAdminGroupForTab(tabId) { const LEGACY_TAB_REDIRECTS = { 'governance': 'feature-governance', 'scale': 'redis-caching', + 'general': 'branding', + 'safety': 'access-roles', + 'security': 'secrets', }; function resolveAdminTabId(tabId) { diff --git a/application/single_app/templates/admin/_panes/access-roles.html b/application/single_app/templates/admin/_panes/access-roles.html new file mode 100644 index 000000000..3e93d9ff7 --- /dev/null +++ b/application/single_app/templates/admin/_panes/access-roles.html @@ -0,0 +1,49 @@ +
+
+
+ Permissions +
+

Control which users can access specific administrative views related to safety and feedback.

+ + +
+ + + +
+

+ Required app role value: SafetyViolationAdmin. Assign this role to users or groups in the Enterprise App before enabling the requirement. If disabled, any user with the general Admin app role can access the Safety Violations admin page. +

+ + +
+ + + +
+

+ Required app role value: FeedbackAdmin. Assign this role to users or groups in the Enterprise App before enabling the requirement. If disabled, any user with the general Admin app role can access the User Feedback admin page. Requires Enable User Feedback to be active. +

+
+ + + + +
diff --git a/application/single_app/templates/admin/_panes/agents.html b/application/single_app/templates/admin/_panes/agents.html index 5994976d2..622dde43d 100644 --- a/application/single_app/templates/admin/_panes/agents.html +++ b/application/single_app/templates/admin/_panes/agents.html @@ -1,4 +1,4 @@ -
+

Configure AI agents and actions for enhanced functionality. Agents provide AI-driven task automation while Actions extend functionality with custom tools and integrations. diff --git a/application/single_app/templates/admin/_panes/ai-models.html b/application/single_app/templates/admin/_panes/ai-models.html index f12b06236..75437fded 100644 --- a/application/single_app/templates/admin/_panes/ai-models.html +++ b/application/single_app/templates/admin/_panes/ai-models.html @@ -1,4 +1,4 @@ -

+

Configure all AI model settings including GPT for text generation, embeddings for semantic search, and image generation capabilities. diff --git a/application/single_app/templates/admin/_panes/archiving.html b/application/single_app/templates/admin/_panes/archiving.html index 0d56b6223..d2943f47d 100644 --- a/application/single_app/templates/admin/_panes/archiving.html +++ b/application/single_app/templates/admin/_panes/archiving.html @@ -1,4 +1,4 @@ -

+
Conversation Archiving diff --git a/application/single_app/templates/admin/_panes/branding.html b/application/single_app/templates/admin/_panes/branding.html new file mode 100644 index 000000000..c648c07fa --- /dev/null +++ b/application/single_app/templates/admin/_panes/branding.html @@ -0,0 +1,165 @@ +
+ +

+ Configure general application settings, including the application's title, logo, and landing page text. +

+
+
+ Branding +
+

+ Configure your application's title, logo, and branding elements. +

+
+ + +
+
Logo Settings
+
+ + + +
+
+ + + +
+
+
+ + {{ settings.landing_page_logo_scale_percent | default(100) }}% +
+ +
+ 50% + 500% +
+ + Adjust the logo size on the home page only. This does not change the logo size in the top or sidebar navigation. + +
+
+ + + This logo will be displayed in light mode and stored at up to 500px tall so the main page can render it sharply without keeping oversized assets in settings. +
+
+ + + This logo will be displayed in dark mode. If not provided, the light mode logo will be used in both themes. Dark logos are also stored at up to 500px tall. +
+
+ + + Recommended: 16x16 or 32x32 pixel png or jpg or ico. +
+
+ +
+
+ Home Page Text +
+

Configure the text content displayed on your application's home page using Markdown formatting.

+ +
+ + + Choose how the landing page markdown is aligned on the home page. +
+ +
+ + + +
+ + + + +
+ +
+
+ Appearance +
+

Configure the app appearance and theme settings.

+ +
+ +
+ + + +
+

Users can still toggle dark mode individually from the navigation bar.

+
+ +
+ +
+ + + +
+

Users can still toggle the left navigation sidebar individually from the header.

+
+
+ + + + + + +
diff --git a/application/single_app/templates/admin/_panes/chat-experience.html b/application/single_app/templates/admin/_panes/chat-experience.html index 223e1beb0..37d8033a6 100644 --- a/application/single_app/templates/admin/_panes/chat-experience.html +++ b/application/single_app/templates/admin/_panes/chat-experience.html @@ -1,4 +1,4 @@ -
+
Processing Thoughts diff --git a/application/single_app/templates/admin/_panes/citation.html b/application/single_app/templates/admin/_panes/citation.html index 94f9788e1..4d42286e9 100644 --- a/application/single_app/templates/admin/_panes/citation.html +++ b/application/single_app/templates/admin/_panes/citation.html @@ -1,4 +1,4 @@ -
+

Configure standard and enhanced citations features for your and group workspaces. diff --git a/application/single_app/templates/admin/_panes/classification.html b/application/single_app/templates/admin/_panes/classification.html index 597736e46..425eeedcf 100644 --- a/application/single_app/templates/admin/_panes/classification.html +++ b/application/single_app/templates/admin/_panes/classification.html @@ -1,4 +1,4 @@ -

+
Document Classification diff --git a/application/single_app/templates/admin/_panes/safety.html b/application/single_app/templates/admin/_panes/content-safety.html similarity index 75% rename from application/single_app/templates/admin/_panes/safety.html rename to application/single_app/templates/admin/_panes/content-safety.html index c4973acdb..88eae95bb 100644 --- a/application/single_app/templates/admin/_panes/safety.html +++ b/application/single_app/templates/admin/_panes/content-safety.html @@ -1,4 +1,4 @@ -
+

Configure content safety, archiving, and user feedback settings. If Content Safety is enabled, user @@ -174,49 +174,6 @@

-
-
- Permissions -
-

Control which users can access specific administrative views related to safety and feedback.

- - -
- - - -
-

- Required app role value: SafetyViolationAdmin. Assign this role to users or groups in the Enterprise App before enabling the requirement. If disabled, any user with the general Admin app role can access the Safety Violations admin page. -

- - -
- - - -
-

- Required app role value: FeedbackAdmin. Assign this role to users or groups in the Enterprise App before enabling the requirement. If disabled, any user with the general Admin app role can access the User Feedback admin page. Requires Enable User Feedback to be active. -

-
diff --git a/application/single_app/templates/admin/_panes/control-center-config.html b/application/single_app/templates/admin/_panes/control-center-config.html index dd2143276..6567e5537 100644 --- a/application/single_app/templates/admin/_panes/control-center-config.html +++ b/application/single_app/templates/admin/_panes/control-center-config.html @@ -1,4 +1,4 @@ -
+

Configure Control Center access and permissions for administrators.

diff --git a/application/single_app/templates/admin/_panes/cosmos.html b/application/single_app/templates/admin/_panes/cosmos.html index 9d1fa89ca..33cf6b0f3 100644 --- a/application/single_app/templates/admin/_panes/cosmos.html +++ b/application/single_app/templates/admin/_panes/cosmos.html @@ -1,4 +1,4 @@ -
+
diff --git a/application/single_app/templates/admin/_panes/custom-pages.html b/application/single_app/templates/admin/_panes/custom-pages.html index a3d0f32ef..00fc68987 100644 --- a/application/single_app/templates/admin/_panes/custom-pages.html +++ b/application/single_app/templates/admin/_panes/custom-pages.html @@ -1,4 +1,4 @@ -
+

Enable trusted deployment-time custom pages and manage metadata for simple static pages served from the application custom_pages folders.

@@ -78,4 +78,56 @@
Static Page Metadata
+
diff --git a/application/single_app/templates/admin/_panes/data-management.html b/application/single_app/templates/admin/_panes/data-management.html index aba966397..d99c04990 100644 --- a/application/single_app/templates/admin/_panes/data-management.html +++ b/application/single_app/templates/admin/_panes/data-management.html @@ -1,4 +1,4 @@ -
+

Backup, Migrate & Restore

@@ -241,7 +241,7 @@
Encryption
Key Vault is strongly recommended
Generated backup encryption keys are stored in the Data Management settings document when Key Vault is not enabled.
- Open Key Vault settings + Open Key Vault settings
diff --git a/application/single_app/templates/admin/_panes/feature-governance.html b/application/single_app/templates/admin/_panes/feature-governance.html index d0f153d74..0b7dcfed1 100644 --- a/application/single_app/templates/admin/_panes/feature-governance.html +++ b/application/single_app/templates/admin/_panes/feature-governance.html @@ -1,4 +1,4 @@ -
+

Configure governance enforcement for endpoint, agent, and action creation or usage. Governance is disabled by default and only enforced when toggled on. diff --git a/application/single_app/templates/admin/_panes/feedback-alerts.html b/application/single_app/templates/admin/_panes/feedback-alerts.html index 4ee6ae562..5488e7242 100644 --- a/application/single_app/templates/admin/_panes/feedback-alerts.html +++ b/application/single_app/templates/admin/_panes/feedback-alerts.html @@ -1,4 +1,4 @@ -

+
User Feedback diff --git a/application/single_app/templates/admin/_panes/file-sync.html b/application/single_app/templates/admin/_panes/file-sync.html index 2d56920d2..eb440758a 100644 --- a/application/single_app/templates/admin/_panes/file-sync.html +++ b/application/single_app/templates/admin/_panes/file-sync.html @@ -1,4 +1,4 @@ -
+

Configure file synchronization for personal, group, and public workspaces.

diff --git a/application/single_app/templates/admin/_panes/general.html b/application/single_app/templates/admin/_panes/general.html deleted file mode 100644 index bd3d5be40..000000000 --- a/application/single_app/templates/admin/_panes/general.html +++ /dev/null @@ -1,682 +0,0 @@ -
- -

- Configure general application settings, including the application's title, logo, and landing page text. -

-
-
- Branding -
-

- Configure your application's title, logo, and branding elements. -

-
- - -
-
Logo Settings
-
- - - -
-
- - - -
-
-
- - {{ settings.landing_page_logo_scale_percent | default(100) }}% -
- -
- 50% - 500% -
- - Adjust the logo size on the home page only. This does not change the logo size in the top or sidebar navigation. - -
-
- - - This logo will be displayed in light mode and stored at up to 500px tall so the main page can render it sharply without keeping oversized assets in settings. -
-
- - - This logo will be displayed in dark mode. If not provided, the light mode logo will be used in both themes. Dark logos are also stored at up to 500px tall. -
-
- - - Recommended: 16x16 or 32x32 pixel png or jpg or ico. -
-
- -
-
- Home Page Text -
-

Configure the text content displayed on your application's home page using Markdown formatting.

- -
- - - Choose how the landing page markdown is aligned on the home page. -
- -
- - - -
- - - - -
- -
-
- Appearance -
-

Configure the app appearance and theme settings.

- -
- -
- - - -
-

Users can still toggle dark mode individually from the navigation bar.

-
- -
- -
- - - -
-

Users can still toggle the left navigation sidebar individually from the header.

-
-
- -
-
-
- Health Check -
- -
-

Configure health check endpoint for external monitoring systems.

- -
-
-
-
-
-
Authenticated Endpoint
-

Use when your monitoring system can reach a protected route and you want the endpoint governed by the normal app access boundary.

-
- Protected -
-
- - - -
-

Best for internal monitors or diagnostics tooling that already authenticates to the application.

-
-
-
-
-
-
-
Unauthenticated Endpoint
-

Use when a platform health probe cannot sign in and only needs a lightweight availability response.

-
- No Auth -
-
- - - -
- -
-
-
-
- -
-
-
- API Documentation -
-
- - - Open Swagger UI - -
-
-

Configure automatic OpenAPI/Swagger documentation for API endpoints.

- -
- - - -
-

- Provides interactive API documentation, endpoint testing, and schema validation. - Useful for developers, API integration, and system troubleshooting. -

-
- -
-
- Classification Banner -
-
- - - -
-
- - -
-
- - -
-
- - -
-
- - {{ settings.classification_banner_text or 'Banner Preview' }} - -
-
- -
-
- Chat AI Notice -
-

- Display an administrator-provided reminder directly below the chat input, such as guidance that responses are AI-generated and should be reviewed. -

-
- - -
-
-
- - -
Plain text only. Line breaks are preserved.
-
-
- - -
Changing the notice text or display behavior creates a new message version and shows it again.
-
-
-
- -
-
- Terms of Use -
-

- Require users to accept the Terms of Use before using SimpleChat. Standard Microsoft sign-in users see it before authentication; Teams SSO and other passive sign-in flows are gated immediately after the app session is created. -

- -
- - - -
- -
-
- - -
-
- - - Changing the title, message, or frequency creates a new terms version that users must accept again. -
-
- -
- - - Plain text is shown to users with line breaks preserved. -
- -
- - - Use a local path such as / or an admin-approved HTTP(S) URL. Signed-in users are locally logged out before this redirect. -
- -
-
- - -
-
- - -
-
- - -
- -
-
- Support -
-
- - - -
-

When enabled, users can access Support destinations like Send Feedback and Latest Features directly from navigation.

- -
-
- - - This name will appear in user navigation as the Support menu title. -
- -
- - - -
- -
- - - User Send Feedback drafts will be addressed to this internal email address. -
- -
- - - -
- -
-
User-Facing Latest Features
-

Choose which recent features are shared on the user Latest Features page. Deployment and Redis start unchecked because they are mainly admin-facing rollout and infrastructure topics.

-
- - - - When enabled, user-facing Latest Features cards can show public guide buttons in addition to the direct in-app shortcuts. -
- {% for release_group in support_latest_feature_release_groups %} - {% set checklist_collapse_id = release_group.collapse_id ~ 'Checklist' %} -
-
-
-
-
{{ release_group.label }}{% if release_group.release_version %}v{{ release_group.release_version }}{% endif %}
-

{{ release_group.description }}

-
- {% if not release_group.default_expanded %} - - {% endif %} -
-
-
- {% for feature in release_group.features %} -
-
- - -
-
- {% endfor %} -
-
-
-
- {% endfor %} -
-
-
- - - -
-
- System Settings -
-

- System-level settings that control application behavior, including file size limits, conversation history, - and default prompts. -

-
- - -
-
- - -
-
- - - -
-
-
- - - Users are logged out locally after this many minutes of inactivity. Minimum value: 10 minutes. -
-
- - - Show the warning modal after this many minutes of inactivity. Set this equal to the logout timeout to disable the warning dialog window. -
-
- - - Custom text shown at the top of the idle warning dialog. -
-
-
- - -
-
- - Shown to signed-in users who lack the required roles. Use Enter for line breaks. - -
-
-
diff --git a/application/single_app/templates/admin/_panes/governance-policies.html b/application/single_app/templates/admin/_panes/governance-policies.html index 8b9d2130f..da2b94fd1 100644 --- a/application/single_app/templates/admin/_panes/governance-policies.html +++ b/application/single_app/templates/admin/_panes/governance-policies.html @@ -1,4 +1,4 @@ -
+
Feature Policies
diff --git a/application/single_app/templates/admin/_panes/latest-features.html b/application/single_app/templates/admin/_panes/latest-features.html index 0ef37a941..a5d8c5f5d 100644 --- a/application/single_app/templates/admin/_panes/latest-features.html +++ b/application/single_app/templates/admin/_panes/latest-features.html @@ -1,4 +1,4 @@ -
+

Review the main capabilities introduced in v{{ config['VERSION'] }} and use this page as an admin-facing overview for what to highlight with your teams.

@@ -832,7 +832,7 @@

Admins can enable this once and let users see more of the model pipeline, including sending, generating, and responded states with clearer timing information.

  • Admins can choose whether this announcement is visible on the user-facing Latest Features page from General > User-Facing Latest Features.
  • +

    Configure health check endpoint for external monitoring systems.

    + +
    +
    +
    +
    +
    +
    Authenticated Endpoint
    +

    Use when your monitoring system can reach a protected route and you want the endpoint governed by the normal app access boundary.

    +
    + Protected +
    +
    + + + +
    +

    Best for internal monitors or diagnostics tooling that already authenticates to the application.

    +
    +
    +
    +
    +
    +
    +
    Unauthenticated Endpoint
    +

    Use when a platform health probe cannot sign in and only needs a lightweight availability response.

    +
    + No Auth +
    +
    + + + +
    + +
    +
    +
    +
    +
    +
    +
    + API Documentation +
    +
    + + + Open Swagger UI + +
    +
    +

    Configure automatic OpenAPI/Swagger documentation for API endpoints.

    + +
    + + + +
    +

    + Provides interactive API documentation, endpoint testing, and schema validation. + Useful for developers, API integration, and system troubleshooting. +

    +
    diff --git a/application/single_app/templates/admin/_panes/mcp-governance.html b/application/single_app/templates/admin/_panes/mcp-governance.html index 31080591e..8a57389de 100644 --- a/application/single_app/templates/admin/_panes/mcp-governance.html +++ b/application/single_app/templates/admin/_panes/mcp-governance.html @@ -1,4 +1,4 @@ -
    +
    MCP Action Destination Governance diff --git a/application/single_app/templates/admin/_panes/network.html b/application/single_app/templates/admin/_panes/network.html index 83ba4fb03..0f9216bad 100644 --- a/application/single_app/templates/admin/_panes/network.html +++ b/application/single_app/templates/admin/_panes/network.html @@ -1,4 +1,4 @@ -
    +
    diff --git a/application/single_app/templates/admin/_panes/notices.html b/application/single_app/templates/admin/_panes/notices.html new file mode 100644 index 000000000..c05ac2b68 --- /dev/null +++ b/application/single_app/templates/admin/_panes/notices.html @@ -0,0 +1,288 @@ +
    +
    +
    + Classification Banner +
    +
    + + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + {{ settings.classification_banner_text or 'Banner Preview' }} + +
    +
    + +
    +
    + Chat AI Notice +
    +

    + Display an administrator-provided reminder directly below the chat input, such as guidance that responses are AI-generated and should be reviewed. +

    +
    + + +
    +
    +
    + + +
    Plain text only. Line breaks are preserved.
    +
    +
    + + +
    Changing the notice text or display behavior creates a new message version and shows it again.
    +
    +
    +
    + +
    +
    + Terms of Use +
    +

    + Require users to accept the Terms of Use before using SimpleChat. Standard Microsoft sign-in users see it before authentication; Teams SSO and other passive sign-in flows are gated immediately after the app session is created. +

    + +
    + + + +
    + +
    +
    + + +
    +
    + + + Changing the title, message, or frequency creates a new terms version that users must accept again. +
    +
    + +
    + + + Plain text is shown to users with line breaks preserved. +
    + +
    + + + Use a local path such as / or an admin-approved HTTP(S) URL. Signed-in users are locally logged out before this redirect. +
    + +
    +
    + + +
    +
    + + +
    +
    + + +
    + + + +
    +
    + User Agreement +
    +

    + Configure a user agreement that users must accept before uploading files. + Supports Markdown formatting. +

    + + +
    + + + +
    + + +
    +
    + + +
    + +

    Select where the user agreement should be shown (at least one required):

    +
    +
    +
    + + +
    +
    + + +
    +
    +
    +
    + + +
    +
    + + +
    +
    +
    + +
    + + +
    + + +
    + 0 / 200 words + +
    +
    + + +
    + + + +
    + + +
    + +
    +
    +
    +
    diff --git a/application/single_app/templates/admin/_panes/redis-caching.html b/application/single_app/templates/admin/_panes/redis-caching.html index 16d846c71..5bbaa8c4d 100644 --- a/application/single_app/templates/admin/_panes/redis-caching.html +++ b/application/single_app/templates/admin/_panes/redis-caching.html @@ -1,4 +1,4 @@ -
    +
    {% set enable_dai_debug = settings.enable_dai_debug | default(false) %}

    Configure Redis cache to improve enterprise scale and performance by caching session data. Enabling Redis allows you to horizontally scale your application across multiple instances without losing session data. diff --git a/application/single_app/templates/admin/_panes/retention.html b/application/single_app/templates/admin/_panes/retention.html index c3aa2258a..a2bc06781 100644 --- a/application/single_app/templates/admin/_panes/retention.html +++ b/application/single_app/templates/admin/_panes/retention.html @@ -1,4 +1,4 @@ -

    +
    Retention Policy diff --git a/application/single_app/templates/admin/_panes/search-extract.html b/application/single_app/templates/admin/_panes/search-extract.html index 1db055f2b..04de8cc88 100644 --- a/application/single_app/templates/admin/_panes/search-extract.html +++ b/application/single_app/templates/admin/_panes/search-extract.html @@ -1,4 +1,4 @@ -
    +

    Configure Azure AI Search, Document Intelligence, and multimedia support settings. diff --git a/application/single_app/templates/admin/_panes/security.html b/application/single_app/templates/admin/_panes/secrets.html similarity index 99% rename from application/single_app/templates/admin/_panes/security.html rename to application/single_app/templates/admin/_panes/secrets.html index 40d43e1a9..dbf36ddc0 100644 --- a/application/single_app/templates/admin/_panes/security.html +++ b/application/single_app/templates/admin/_panes/secrets.html @@ -1,4 +1,4 @@ -

    +

    Manage security settings for key vault and other security configurations.

    diff --git a/application/single_app/templates/admin/_panes/send-feedback.html b/application/single_app/templates/admin/_panes/send-feedback.html index 1d9cdb365..5acd28833 100644 --- a/application/single_app/templates/admin/_panes/send-feedback.html +++ b/application/single_app/templates/admin/_panes/send-feedback.html @@ -1,4 +1,4 @@ -
    +

    Prepare a prefilled email draft for bug reports or feature requests. This utility is separate from saved settings and records each submission intent in the activity log before opening your local email client.

    diff --git a/application/single_app/templates/admin/_panes/session.html b/application/single_app/templates/admin/_panes/session.html new file mode 100644 index 000000000..cb8fe9abe --- /dev/null +++ b/application/single_app/templates/admin/_panes/session.html @@ -0,0 +1,87 @@ +
    +
    +
    + System Settings +
    +

    + System-level settings that control application behavior, including file size limits, conversation history, + and default prompts. +

    +
    + + +
    +
    + + +
    +
    + + + +
    +
    +
    + + + Users are logged out locally after this many minutes of inactivity. Minimum value: 10 minutes. +
    +
    + + + Show the warning modal after this many minutes of inactivity. Set this equal to the logout timeout to disable the warning dialog window. +
    +
    + + + Custom text shown at the top of the idle warning dialog. +
    +
    +
    + + +
    +
    + + Shown to signed-in users who lack the required roles. Use Enter for line breaks. + +
    +
    +
    \ No newline at end of file diff --git a/application/single_app/templates/admin/_panes/support-menu.html b/application/single_app/templates/admin/_panes/support-menu.html new file mode 100644 index 000000000..57c11877b --- /dev/null +++ b/application/single_app/templates/admin/_panes/support-menu.html @@ -0,0 +1,105 @@ +
    +
    +
    + Support +
    +
    + + + +
    +

    When enabled, users can access Support destinations like Send Feedback and Latest Features directly from navigation.

    + +
    +
    + + + This name will appear in user navigation as the Support menu title. +
    + +
    + + + +
    + +
    + + + User Send Feedback drafts will be addressed to this internal email address. +
    + +
    + + + +
    + +
    +
    User-Facing Latest Features
    +

    Choose which recent features are shared on the user Latest Features page. Deployment and Redis start unchecked because they are mainly admin-facing rollout and infrastructure topics.

    +
    + + + + When enabled, user-facing Latest Features cards can show public guide buttons in addition to the direct in-app shortcuts. +
    + {% for release_group in support_latest_feature_release_groups %} + {% set checklist_collapse_id = release_group.collapse_id ~ 'Checklist' %} +
    +
    +
    +
    +
    {{ release_group.label }}{% if release_group.release_version %}v{{ release_group.release_version }}{% endif %}
    +

    {{ release_group.description }}

    +
    + {% if not release_group.default_expanded %} + + {% endif %} +
    +
    +
    + {% for feature in release_group.features %} +
    +
    + + +
    +
    + {% endfor %} +
    +
    +
    +
    + {% endfor %} +
    +
    +
    +
    \ No newline at end of file diff --git a/application/single_app/templates/admin/_panes/workspace-identities.html b/application/single_app/templates/admin/_panes/workspace-identities.html index da77b91b5..7c3569ebd 100644 --- a/application/single_app/templates/admin/_panes/workspace-identities.html +++ b/application/single_app/templates/admin/_panes/workspace-identities.html @@ -1,4 +1,4 @@ -
    +

    Manage the global authentication profiles that File Sync sources and Actions reuse, so credentials stay scoped and are resolved server-side.

    diff --git a/application/single_app/templates/admin/_panes/workspaces.html b/application/single_app/templates/admin/_panes/workspaces.html index 9200f71d9..690a902af 100644 --- a/application/single_app/templates/admin/_panes/workspaces.html +++ b/application/single_app/templates/admin/_panes/workspaces.html @@ -1,4 +1,4 @@ -
    +

    Configure workspace settings like personal/group access, multimedia support, metadata, and document classification.

    @@ -640,107 +640,4 @@
    -
    -
    - User Agreement -
    -

    - Configure a user agreement that users must accept before uploading files. - Supports Markdown formatting. -

    - - -
    - - - -
    - - -
    -
    - - -
    - -

    Select where the user agreement should be shown (at least one required):

    -
    -
    -
    - - -
    -
    - - -
    -
    -
    -
    - - -
    -
    - - -
    -
    -
    - -
    - - -
    - - -
    - 0 / 200 words - -
    -
    - - -
    - - - -
    - - -
    - -
    -
    -
    diff --git a/application/single_app/templates/admin_settings.html b/application/single_app/templates/admin_settings.html index 65e958b1d..118431f86 100644 --- a/application/single_app/templates/admin_settings.html +++ b/application/single_app/templates/admin_settings.html @@ -980,7 +980,10 @@

    12. Enhanced Citations and Image Generation

    visible whichever governance tab is active. #}
    - {% include "admin/_panes/security.html" %} + {% include "admin/_panes/secrets.html" %} + {% include "admin/_panes/access-roles.html" %} + {% include "admin/_panes/content-safety.html" %} + {% include "admin/_panes/session.html" %} {% include "admin/_panes/agents.html" %} @@ -994,7 +997,8 @@

    12. Enhanced Citations and Image Generation

    {% include "admin/_panes/logging.html" %} - {% include "admin/_panes/general.html" %} + {% include "admin/_panes/branding.html" %} + {% include "admin/_panes/notices.html" %} {% include "admin/_panes/custom-pages.html" %} @@ -1031,11 +1035,11 @@

    12. Enhanced Citations and Image Generation

    {% include "admin/_panes/feedback-alerts.html" %} - {% include "admin/_panes/safety.html" %} {% include "admin/_panes/search-extract.html" %} + {% include "admin/_panes/support-menu.html" %} {% include "admin/_panes/send-feedback.html" %} {# Latest Features stays last so it never opens by default #} diff --git a/docs/explanation/features/ADMIN_SETTINGS_IA_REWORK_STATUS.md b/docs/explanation/features/ADMIN_SETTINGS_IA_REWORK_STATUS.md index 0e9a7ef64..3632338b9 100644 --- a/docs/explanation/features/ADMIN_SETTINGS_IA_REWORK_STATUS.md +++ b/docs/explanation/features/ADMIN_SETTINGS_IA_REWORK_STATUS.md @@ -22,8 +22,8 @@ Development ─── feature/admin-settings-ia ──┬── #1297 stage 1 └──► final PR ──► Development ``` -Current version: **0.260.012**. Fingerprint: **462 field names / 110 card ids**. -Navigation: **13 groups / 27 tabs / 88 sections**. +Current version: **0.260.013**. Fingerprint: **462 field names / 110 card ids**. +Navigation: **13 groups / 31 tabs / 88 sections**. ### Shipped @@ -109,6 +109,29 @@ moved from Scale to Security that way. | Scale | → Redis & Caching, Cosmos; Front Door moved to Security as Network | | Data Lifecycle | **New group.** Retention + Classification pulled from Workspaces, Archiving pulled from Safety | | Chat | **New group.** Chat Experience (thoughts from AI Models, file uploads + contents drawer and scope lock from Workspaces), Feedback & Alerts (feedback + desktop notifications from Safety) | +| Appearance | **General dismantled.** → Branding, Notices & Agreements (+ user agreement from Workspaces), Pages & Links (+ external links) | +| Security | **Safety dismantled.** → Access & Roles, Secrets, Content Safety, Session, Network | +| Operations | Logging is now Logging & Health, gaining health check and API documentation from General | +| Help | Gains Support Menu from General | + +### Two hardcoded-id traps found and closed + +Both were latent breakage that only surfaced because a tab id changed: + +- **`openKeyVaultSettings`** in `admin_data_management.js` switched tabs by the + literal id `security-tab`. It was dead weight anyway — the link now carries + `data-admin-link` and `admin_card_links.js` resolves the tab from the DOM. +- **The landing pane** was `show active` hardcoded in one pane's markup and + `showAdminTab('general')` hardcoded in the sidebar script. Splitting that + pane left Admin Settings with **no active pane at all**. Every pane now + renders `{% if admin_landing_tab == '' %} show active{% endif %}`, fed by + `get_landing_tab_id()`, and the sidebar reads the first rendered tab. + +**Assert durable properties, not current ids.** Four test files hardcoded +`general` or `security` and had to be rewritten to assert what actually matters +— Latest Features is last, exactly one pane is active, every tab renders one +pane. Any new test that names a tab id will need the same treatment next time. + ### The cross-pane move tool diff --git a/docs/explanation/release_notes.md b/docs/explanation/release_notes.md index 3d12d8127..8380fa7a2 100644 --- a/docs/explanation/release_notes.md +++ b/docs/explanation/release_notes.md @@ -2,6 +2,38 @@ For feature-focused and fix-focused drill-downs by version, see [Features by Version](/explanation/features/) and [Fixes by Version](/explanation/fixes/). +### **(v0.260.013)** + +#### User Interface Enhancements + +* **General Tab Broken Up Into Focused Tabs** + * General had grown into a catch-all of eleven unrelated cards: branding sat next to health checks, API documentation, terms of use and system settings. + * Appearance now has **Branding** (branding, home page text, appearance), **Notices & Agreements** (classification banner, chat AI notice, terms of use and the user agreement pulled across from Workspaces) and **Pages & Links** (static pages plus external links). + * Health Check and API Documentation moved to Operations, which is now **Logging & Health** — they report on how the app is running rather than how it looks. + * Support moved to Help as its own **Support Menu** tab, next to Send Feedback. + * (Ref: `branding`, `notices`, `custom-pages`, `logging`, `support-menu`) + +* **Security Split Into Five Purposeful Tabs** + * Security held a single Key Vault card while an unrelated Safety tab mixed content filtering with role permissions, which are different jobs. + * Security is now **Access & Roles** (who gets in and with what role), **Secrets** (Key Vault), **Content Safety** (what may be said once you are in), **Session** (idle timeout and related system settings) and **Network** (Azure Front Door). + * (Ref: `access-roles`, `secrets`, `content-safety`, `session`, `network`) + +#### Bug Fixes + +* **"Open Key Vault Settings" Link No Longer Depends On A Hardcoded Tab** + * The link from Data Management to Key Vault switched tabs by a hardcoded id, so it silently stopped working whenever that tab was renamed. + * It now uses the standard card link, which finds the owning tab from the page itself and stays correct however the settings are grouped. + * (Ref: `data-management-key-vault-link`, `admin_card_links.js`, `admin_data_management.js`) + +* **Admin Settings Always Opens On A Real Tab** + * The tab shown on arrival was pinned to a specific id in both the markup and the sidebar script. Regrouping settings could leave Admin Settings opening with no tab selected at all. + * The landing tab is now taken from the navigation map, so it follows the settings and can never be Latest Features. + * (Ref: `admin_landing_tab`, `get_landing_tab_id`, `admin_sidebar_nav.js`) + +* **Stale Tab Names In Latest Features** + * Several Latest Features entries pointed readers at tabs by their old names after the settings moved. + * (Ref: `latest-features` pane) + ### **(v0.260.012)** #### User Interface Enhancements diff --git a/functional_tests/test_admin_latest_features_tab.py b/functional_tests/test_admin_latest_features_tab.py index 322bfea1a..31d96dfc4 100644 --- a/functional_tests/test_admin_latest_features_tab.py +++ b/functional_tests/test_admin_latest_features_tab.py @@ -341,7 +341,7 @@ def test_latest_features_sidebar_navigation(): # Tab order is defined once in the nav map, which the sidebar renders from, # so ordering is asserted there rather than against the rendered markup. tab_ids = get_tab_ids() - assert tab_ids.index('latest-features') > tab_ids.index('general'), 'Latest Features should appear after General' + assert tab_ids[-1] == 'latest-features', 'Latest Features should be the last destination of all' assert tab_ids.index('latest-features') > tab_ids.index('send-feedback'), 'Latest Features should be the last destination, after Send Feedback' print('Latest Features sidebar navigation is present') @@ -372,10 +372,18 @@ def test_latest_features_top_nav_priority(): ) # Latest Features opened on every visit to Admin Settings, which is why it - # is pinned last and General is the landing tab instead. - assert 'class="tab-pane fade" id="latest-features" role="tabpanel" aria-labelledby="latest-features-tab"' in template_content, 'Latest Features pane should not be the default active tab' - assert 'class="tab-pane fade show active" id="general" role="tabpanel" aria-labelledby="general-tab"' in template_content, 'General pane should be the default active tab' - assert tab_ids[0] == 'general', 'General should be the first tab in the navigation map' + # is pinned last. The landing pane is not hardcoded: every pane derives its + # active state from admin_landing_tab, which is the first tab of the first + # group, so regrouping cards can never leave Admin Settings with no pane + # showing or with Latest Features showing. + assert '
    1}) assert not duplicates, f'Tabs listed more than once in the nav map: {duplicates}' - assert template_content.count('id="security" role="tabpanel"') == 1, 'Security tab pane should appear exactly once' - assert template_content.count('tab-pane fade show active') == 1, 'Only one tab pane should be marked show active' + # Every tab in the map must render exactly one pane. Checking all of them + # is stronger than naming one tab, and cannot go stale as tabs are renamed. + for tab_id in tab_ids: + pane_count = template_content.count(f'id="{tab_id}" role="tabpanel"') + assert pane_count == 1, f"Tab pane '{tab_id}' should appear exactly once, found {pane_count}" + # No pane may hardcode the active state; exactly one resolves it at render + # time from the nav map. + assert template_content.count('tab-pane fade show active') == 0, ( + 'No pane should hardcode "show active"; it is derived from admin_landing_tab' + ) + landing_conditionals = template_content.count("{% if admin_landing_tab == '") + assert landing_conditionals == len(tab_ids), ( + f'Every one of the {len(tab_ids)} panes should derive its active state ' + f'from admin_landing_tab, found {landing_conditionals}' + ) assert 'Managesecuritysettingsforkeyvaultandothersecurityconfigurations.

    ' in normalized_template, 'Security intro paragraph should be properly closed' # Every tab in the map must have a pane to activate. diff --git a/functional_tests/test_admin_settings_nav_map.py b/functional_tests/test_admin_settings_nav_map.py index ce015c6f6..0754dddf9 100644 --- a/functional_tests/test_admin_settings_nav_map.py +++ b/functional_tests/test_admin_settings_nav_map.py @@ -143,14 +143,23 @@ def test_latest_features_stays_last(): assert tab_ids[-1] == "latest-features", ( f"Latest Features must be the last tab, got '{tab_ids[-1]}'" ) - assert tab_ids[0] == "general", ( - f"General must be the first tab, got '{tab_ids[0]}'" - ) assert ADMIN_NAV[-1]["id"] == "help", ( "Latest Features should sit in the last group" ) - print("Latest Features is last; General leads.") + # The landing tab is whatever the map lists first, so it moves freely as the + # information architecture changes. What must never drift is that the admin + # does not land on Latest Features, which is the whole point of the move. + assert tab_ids[0] != "latest-features", ( + "Latest Features must never be the landing tab" + ) + assert ADMIN_NAV[0]["tabs"], "The first group must expose at least one tab" + assert tab_ids[0] == ADMIN_NAV[0]["tabs"][0]["id"], ( + "The landing tab must be the first tab of the first group, so the " + "sidebar and the tab strip agree on where an admin lands" + ) + + print(f"Latest Features is last; '{tab_ids[0]}' leads.") if __name__ == "__main__": diff --git a/functional_tests/test_admin_source_review_depth_save.py b/functional_tests/test_admin_source_review_depth_save.py index fa7a602cd..528bf0c0e 100644 --- a/functional_tests/test_admin_source_review_depth_save.py +++ b/functional_tests/test_admin_source_review_depth_save.py @@ -41,9 +41,11 @@ def test_source_review_depth_has_single_form_field(): f"Expected exactly one source_review_max_depth form field, found {depth_field_name_count}." ) + # Bound the Latest Features visibility block by its own container rather + # than by a neighbouring card, so the check survives cards moving between + # tabs during information architecture work. latest_features_start = template_content.index('id="support_latest_features_settings"') - external_links_start = template_content.index('id="external-links-section"') - latest_features_section = template_content[latest_features_start:external_links_start] + latest_features_section = template_content[latest_features_start:latest_features_start + 8000] assert 'name="source_review_max_depth"' not in latest_features_section, ( "Latest Features visibility controls must not post source_review_max_depth." diff --git a/functional_tests/test_data_management_security_patterns.py b/functional_tests/test_data_management_security_patterns.py index 422e3dfbf..284c123be 100644 --- a/functional_tests/test_data_management_security_patterns.py +++ b/functional_tests/test_data_management_security_patterns.py @@ -345,7 +345,6 @@ def test_admin_javascript_uses_safe_dom_patterns(): for required_snippet in [ 'document.createElement("tr")', 'document.createElement("td")', - 'openKeyVaultSettings', 'buildMigrationPlan()', 'queueMigration(false)', 'loadMigrationCatalog(targetType, "reset")', From 6c682bf6f3c8fe576c849b0202148f570fd1d5c4 Mon Sep 17 00:00:00 2001 From: Paul Lizer Date: Wed, 19 Aug 2026 16:20:34 -0400 Subject: [PATCH 20/25] Stage D part 4: Knowledge, Workspaces and Workflow Search & Extract held eight cards spanning four unrelated jobs, from Bing consent to voice transcription. Workspaces mixed workspace types with file rules, workflow and extraction settings. Knowledge Web & Research web search, URL access, deep research Search Index Azure AI Search Document Extraction document intelligence, chunk sizes, metadata extraction and multi-modal vision (from workspaces) Audio & Video video intelligence, voice conversations File Sync unchanged Voice and video sit under Knowledge rather than Chat because they are extraction pipelines, not conversation features. Workspaces Workspace Types personal, group, public Files & Sharing downloads, sharing, shared conversation file approvals (from ai-models) Global Identities unchanged Workflow is now its own group. It drives approvals and assignment across every workspace type and was too large to sit as one card. Navigation is now 14 groups / 36 tabs / 88 sections. Bug found and fixed by doing this The Group Workflow Assignment modal sat between two cards, so it followed file-download-settings-section into Files & Sharing while its trigger button went to Workflow. An inactive tab pane is hidden, so the dialog would never have opened. test_admin_settings_modal_placement.py now checks every modal lives in a pane that can open it, and that modal ids are unique. Verified against a deliberately stranded modal: the test fails with exactly that diagnosis. Also corrected two section comments that had drifted onto the wrong cards in ai-models, labelling embeddings as processing thoughts. AI Models and Agents are deliberately not in this change: gpt-configuration is not a top-level card, it lives inside legacyModelSettingsModal, so splitting that pane means moving a modal rather than a card and deserves its own review. Verified field contract 462 names / 110 card ids, unchanged regression set 32 failures, identical to baseline jinja compile 38/38 admin templates xss sinks pass modal placement 48 modals, all reachable, all ids unique Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- application/single_app/admin_settings_nav.py | 69 +- application/single_app/config.py | 2 +- .../static/js/admin/admin_sidebar_nav.js | 2 + .../templates/admin/_panes/ai-models.html | 21 - .../templates/admin/_panes/audio-video.html | 472 +++++ .../templates/admin/_panes/extraction.html | 724 +++++++ .../templates/admin/_panes/files-sharing.html | 223 +++ .../admin/_panes/search-extract.html | 1698 ----------------- .../templates/admin/_panes/search-index.html | 119 ++ .../templates/admin/_panes/web-research.html | 519 +++++ .../templates/admin/_panes/workflow.html | 157 ++ .../admin/_panes/workspace-types.html | 159 ++ .../templates/admin/_panes/workspaces.html | 643 ------- .../single_app/templates/admin_settings.html | 9 +- .../ADMIN_SETTINGS_IA_REWORK_STATUS.md | 32 +- docs/explanation/release_notes.md | 30 + .../test_admin_settings_modal_placement.py | 140 ++ 17 files changed, 2642 insertions(+), 2377 deletions(-) create mode 100644 application/single_app/templates/admin/_panes/audio-video.html create mode 100644 application/single_app/templates/admin/_panes/extraction.html create mode 100644 application/single_app/templates/admin/_panes/files-sharing.html delete mode 100644 application/single_app/templates/admin/_panes/search-extract.html create mode 100644 application/single_app/templates/admin/_panes/search-index.html create mode 100644 application/single_app/templates/admin/_panes/web-research.html create mode 100644 application/single_app/templates/admin/_panes/workflow.html create mode 100644 application/single_app/templates/admin/_panes/workspace-types.html delete mode 100644 application/single_app/templates/admin/_panes/workspaces.html create mode 100644 functional_tests/test_admin_settings_modal_placement.py diff --git a/application/single_app/admin_settings_nav.py b/application/single_app/admin_settings_nav.py index e10f54c75..b5c8b5e6f 100644 --- a/application/single_app/admin_settings_nav.py +++ b/application/single_app/admin_settings_nav.py @@ -99,7 +99,6 @@ "icon": "bi-cpu", "sections": [ {"id": "multi-endpoint-configuration", "label": "Model Endpoints", "icon": "bi-hdd-network"}, - {"id": "shared-conversation-file-approvals-section", "label": "Shared Conversation File Approvals", "icon": "bi-file-earmark-lock"}, {"id": "embeddings-config", "label": "Embeddings", "icon": "bi-vector-pen"}, {"id": "image-config", "label": "Image Generation", "icon": "bi-image"}, {"id": "gpt-config", "label": "Chat Model", "icon": "bi-chat-square-text"}, @@ -132,18 +131,25 @@ "icon": "bi-folder", "tabs": [ { - "id": "workspaces", - "label": "Workspaces", + "id": "workspace-types", + "label": "Workspace Types", "icon": "bi-folder", "sections": [ {"id": "personal-workspaces-section", "label": "Personal Workspaces", "icon": "bi-person"}, - {"id": "workflow-settings-section", "label": "Workflow", "icon": "bi-diagram-3"}, - {"id": "file-download-settings-section", "label": "File Downloads", "icon": "bi-download"}, {"id": "group-workspaces-section", "label": "Group Workspaces", "icon": "bi-people"}, {"id": "public-workspaces-section", "label": "Public Workspaces", "icon": "bi-globe"}, + ], + }, + { + # Who may take a file out of a workspace, and who must approve + # it first. + "id": "files-sharing", + "label": "Files & Sharing", + "icon": "bi-share", + "sections": [ + {"id": "file-download-settings-section", "label": "File Downloads", "icon": "bi-download"}, {"id": "file-sharing-section", "label": "File Sharing", "icon": "bi-share"}, - {"id": "metadata-extraction-section", "label": "Metadata Extraction", "icon": "bi-file-earmark-code"}, - {"id": "multimodal-vision-section", "label": "Multi-Modal Vision Analysis", "icon": "bi-eye"}, + {"id": "shared-conversation-file-approvals-section", "label": "Shared Conversation File Approvals", "icon": "bi-check2-square"}, ], }, { @@ -154,22 +160,65 @@ }, ], }, + { + # Workflow drives approvals and assignment across workspaces and is + # large enough to stand on its own rather than sit inside Workspaces. + "id": "workflow", + "label": "Workflow", + "icon": "bi-diagram-3", + "tabs": [ + { + "id": "workflow", + "label": "Workflow", + "icon": "bi-diagram-3", + "sections": [ + {"id": "workflow-settings-section", "label": "Workflow", "icon": "bi-diagram-3"}, + ], + }, + ], + }, { "id": "knowledge", "label": "Knowledge", "icon": "bi-search", "tabs": [ { - "id": "search-extract", - "label": "Search & Extract", - "icon": "bi-search", + # Reaching outside the tenant for material. + "id": "web-research", + "label": "Web & Research", + "icon": "bi-globe", "sections": [ {"id": "web-search-section", "label": "Web Search", "icon": "bi-globe"}, {"id": "url-access-section", "label": "URL Access", "icon": "bi-link-45deg"}, {"id": "source-review-section", "label": "Deep Research", "icon": "bi-binoculars"}, + ], + }, + { + "id": "search-index", + "label": "Search Index", + "icon": "bi-search", + "sections": [ {"id": "azure-ai-search-section", "label": "Azure AI Search", "icon": "bi-search"}, + ], + }, + { + # Turning a document into something searchable. + "id": "extraction", + "label": "Document Extraction", + "icon": "bi-file-earmark-text", + "sections": [ {"id": "document-intelligence-section", "label": "Document Intelligence", "icon": "bi-file-earmark-text"}, {"id": "chunk-size-section", "label": "Chunk Sizes", "icon": "bi-collection"}, + {"id": "metadata-extraction-section", "label": "Metadata Extraction", "icon": "bi-file-earmark-code"}, + {"id": "multimodal-vision-section", "label": "Multi-Modal Vision Analysis", "icon": "bi-eye"}, + ], + }, + { + # Voice and video are extraction pipelines, not chat features. + "id": "audio-video", + "label": "Audio & Video", + "icon": "bi-play-circle", + "sections": [ {"id": "video-intelligence-section", "label": "AI Video Intelligence", "icon": "bi-play-circle"}, {"id": "ai-voice-chat-section", "label": "AI Voice Conversations", "icon": "bi-mic"}, ], diff --git a/application/single_app/config.py b/application/single_app/config.py index e1fb9e3b7..992744396 100644 --- a/application/single_app/config.py +++ b/application/single_app/config.py @@ -96,7 +96,7 @@ EXECUTOR_TYPE = 'thread' EXECUTOR_MAX_WORKERS = 30 SESSION_TYPE = 'filesystem' -VERSION = "0.260.013" +VERSION = "0.260.014" IS_DEVELOPMENT = is_development_env_enabled() SESSION_COOKIE_SAMESITE = os.getenv('SESSION_COOKIE_SAMESITE', 'Lax') diff --git a/application/single_app/static/js/admin/admin_sidebar_nav.js b/application/single_app/static/js/admin/admin_sidebar_nav.js index 706c87e5f..7e9538f73 100644 --- a/application/single_app/static/js/admin/admin_sidebar_nav.js +++ b/application/single_app/static/js/admin/admin_sidebar_nav.js @@ -294,6 +294,8 @@ const LEGACY_TAB_REDIRECTS = { 'general': 'branding', 'safety': 'access-roles', 'security': 'secrets', + 'workspaces': 'workspace-types', + 'search-extract': 'web-research', }; function resolveAdminTabId(tabId) { diff --git a/application/single_app/templates/admin/_panes/ai-models.html b/application/single_app/templates/admin/_panes/ai-models.html index 75437fded..98e6590d4 100644 --- a/application/single_app/templates/admin/_panes/ai-models.html +++ b/application/single_app/templates/admin/_panes/ai-models.html @@ -235,27 +235,6 @@
    Global Endpoints
    {% endif %}
    - -
    -
    - Shared Conversation File Approvals -
    -

    Files generated by participants in a shared conversation are saved into the conversation owner's storage. When enabled, those files are held until an approver releases them.

    -
    - - - -
    -
    -
    diff --git a/application/single_app/templates/admin/_panes/audio-video.html b/application/single_app/templates/admin/_panes/audio-video.html new file mode 100644 index 000000000..fba46cabe --- /dev/null +++ b/application/single_app/templates/admin/_panes/audio-video.html @@ -0,0 +1,472 @@ +
    +
    +
    +
    +
    + +
    +
    +

    + AI Video Intelligence +

    +

    Extract insights and make video content searchable and interactive

    +
    +
    + +
    + +
    + + Make Video Content Fully Searchable: Upload videos to your workspace and automatically extract transcripts, identify speakers, detect topics, and generate searchable timestamps. Your AI can then answer questions about video content, cite specific moments, and provide deep insights. +
    + + +
    +
    + + +
    +

    + Upload video files (MP4, AVI, MOV, etc.) to workspaces where Azure Video Indexer automatically processes them. + Extract spoken content, identify speakers, detect faces, recognize brands, and generate searchable metadata—perfect for meetings, presentations, training videos, and recorded content. +

    +
    + + Full-Text Search + + + Speaker ID + + + Timestamped Citations + + + Multi-Language + +
    +
    + + {% set public_video_indexer_endpoint = "https://api.videoindexer.ai" %} + {% set government_video_indexer_endpoint = "https://api.videoindexer.ai.azure.us" %} + {% set configured_video_indexer_endpoint = settings.video_indexer_endpoint or default_video_indexer_endpoint %} + {% if configured_video_indexer_endpoint == government_video_indexer_endpoint %} + {% set video_indexer_cloud = "usgovernment" %} + {% elif configured_video_indexer_endpoint == public_video_indexer_endpoint %} + {% set video_indexer_cloud = "public" %} + {% else %} + {% set video_indexer_cloud = "custom" %} + {% endif %} + {% if azure_environment == "usgovernment" %} + {% set azure_environment_label = "Azure Government" %} + {% elif azure_environment == "custom" %} + {% set azure_environment_label = "Custom Cloud" %} + {% else %} + {% set azure_environment_label = "Azure Public / Commercial" %} + {% endif %} + + +
    +
    + Azure Video Indexer Configuration +
    +

    Connect to your Azure Video Indexer resource for advanced video processing and content extraction.

    + +
    + Identity overview: +
      +
    • Video Indexer can use its own managed identity to reach the linked Storage account during account creation.
    • +
    • SimpleChat authenticates to Video Indexer with the App Service system-assigned managed identity.
    • +
    • The App Service managed identity must have Contributor on the Video Indexer resource.
    • +
    • See Azure Video Indexer documentation for setup details
    • +
    +
    + +
    + + +
    Choose the endpoint family that matches your deployed cloud. Use Custom only when you need a non-standard Video Indexer endpoint.
    +
    + +
    + Current App Service environment: {{ azure_environment_label }}. This selector saves the Video Indexer API endpoint. If you are switching between Azure Public, Azure Government, or a custom cloud, update AZURE_ENVIRONMENT in your App Service configuration as well. +
    + +
    + The selected Video Indexer cloud does not match this app's current AZURE_ENVIRONMENT. Update the App Service configuration if you are moving to a different cloud. +
    + + + +
    + + +
    Only required when using a custom cloud or non-standard Video Indexer endpoint.
    +
    + +
    + + +
    Saved endpoint value used for Video Indexer API calls.
    +
    + +
    + + +
    The Azure resource group containing your Video Indexer account
    +
    + +
    + + +
    Your Azure subscription ID
    +
    + +
    + + +
    The name of your Video Indexer account resource
    +
    + +
    + + +
    Azure region where your Video Indexer account is deployed (e.g., eastus, westus2, northeurope)
    +
    + +
    + + +
    Found in the Video Indexer account Overview page in Azure Portal
    +
    + +
    + + +
    Default for {{ azure_environment_label }}: {{ default_video_indexer_arm_api_version }}
    +
    + +
    + + +
    +
    +
    + + +
    +
    +
    +
    + +
    +
    +

    + AI Voice Conversations +

    +

    Transform your AI experience with natural voice interactions

    +
    +
    + +
    + + +
    +
    + + +
    +

    + Let users opt in to a bundled sound when an AI response finishes outside their active view. + These local cues do not require Azure Speech Service. +

    +
    + +
    + + One Speech resource, three features: Audio file uploads, Voice Input, and Voice Responses all use the same Azure Speech Service section below. Configure the Speech resource once, then turn on whichever speech features you need. +
    + + +
    + {% set audio_runtime = audio_runtime_capabilities or {} %} + {% set audio_supported_extensions = audio_runtime.supported_extensions or [] %} + {% set audio_direct_extensions = audio_runtime.direct_transcription_extensions or [] %} +
    + + +
    +

    + Upload audio files (MP3, WAV, M4A, etc.) to workspaces where they're automatically transcribed and indexed. + The AI can then search, analyze, and answer questions about audio content—perfect for meetings, interviews, lectures, and podcasts. +

    +
    +
    + {{ audio_runtime.message or 'Audio runtime support has not been checked yet.' }} +
    +
    + Supported audio upload extensions: {{ audio_supported_extensions|join(', ') }} +
    + {% if audio_direct_extensions %} +
    + Without FFmpeg, source-file fallback is best for: {{ audio_direct_extensions|join(', ') }}. Container builds can include FFmpeg for broader codec support. +
    + {% endif %} +
    +
    + + +
    +
    + + +
    +

    + Talk to your AI instead of typing. Record voice messages up to 90 seconds directly in the chat interface. + Azure Speech Service instantly transcribes your speech with high accuracy, supporting multiple languages and accents. +

    +
    + + Hands-Free + + + Accessible + + + Fast Input + +
    +
    + + +
    +
    + + +
    +

    + Hear AI responses read aloud in natural, human-like voices powered by Azure's advanced neural Text-to-Speech. + Each message includes a speaker button—click to listen while multitasking, commuting, or whenever reading isn't convenient. +

    +
    + + Natural Voices + + + Multitask-Friendly + + + Multi-Language + +
    +
    + + +
    +
    + Azure Speech Service Configuration +
    +

    Configure the shared Azure Speech resource used by audio transcription, voice input, and text-to-speech.

    + +
    + Shared configuration: These settings apply to all enabled speech features. If you use managed identity and also enable Voice Responses, fill in the Speech Resource ID field so the Speech SDK can authenticate synthesis requests. +
    + +
    + + +
    Use the resource-specific custom-domain endpoint when selecting Managed Identity.
    +
    + +
    + + +
    Required for speech recognition locale defaults and for text-to-speech when using Managed Identity.
    +
    + +
    +
    + Resource ID builder: If the full ARM resource ID is hard to find in the portal, enter the subscription ID, resource group, and Speech resource name below. Simple Chat can build the resource ID for you. +
    + +
    +
    + + +
    +
    + + +
    +
    + + +
    If you use a custom-domain Speech endpoint, this is usually the first part of that hostname.
    +
    +
    + +
    +
    + +
    Provide Subscription ID, Resource Group, and Speech Resource Name to auto-build the ARM resource ID.
    + + + +
    Required for Voice Responses when using Managed Identity. You can paste the full value manually, or let the fields above build it for you.
    +
    + +
    + + +
    + +
    + + +
    +
    + +
    + + +
    +
    Required only when Authentication Type is set to Key.
    +
    + +
    + Managed identity roles: Start with Cognitive Services Speech User. If fast transcription or other write-style speech operations still return authorization errors, add Cognitive Services Speech Contributor. Managed identity also requires a custom-domain endpoint. +
    + +
    +

    + + + Enhanced Citations + + will dramatically improve the citation experience for video and audio files. + +

    +
    +
    diff --git a/application/single_app/templates/admin/_panes/extraction.html b/application/single_app/templates/admin/_panes/extraction.html new file mode 100644 index 000000000..9a056e8db --- /dev/null +++ b/application/single_app/templates/admin/_panes/extraction.html @@ -0,0 +1,724 @@ +
    +
    +
    Document Intelligence
    +

    + Configure Azure Document Intelligence settings. Document Intelligence always powers + Standard extraction and is required for workspaces and chat file uploads. +

    +
    + + + +
    + +
    + {% if content_understanding_supported %} +
    + Enhanced extraction uses Azure AI Content Understanding, which returns tables, + page structure, checkbox states, and AI-generated descriptions of figures, charts, and images. + Configure the connection below. +
    + {% else %} +
    + Azure AI Content Understanding is not available in the + {{ azure_environment }} cloud, so Enhanced extraction uses + Document Intelligence Layout here. There is nothing more to configure — + Enhanced still captures tables, page structure, forms, and checkbox states. +
    + {% endif %} + +
    + + +
    + Enhanced captures more document detail for PDFs and images, including tables, page structure, and checked or unchecked marks. It adds latency and has a 6X increase for every 1000 pages when selected. +
    +
    + + +
    + Auto samples this many first PDF pages with Document Intelligence Layout. If it detects tables, selection marks, or figures, the full PDF uses Enhanced; otherwise it finishes with Standard. Images use Enhanced in Auto mode. +
    +
    +
    +
    Standard: Document Intelligence Read. Fastest and lowest-cost path for plain text PDFs and images.
    +
    Enhanced: best for tables, section structure, page layout, forms, checkbox states, and figure descriptions. Expect more latency and higher cost.
    +
    Auto: samples the first pages with Document Intelligence Layout, then uses Enhanced only when the sample shows tables, selection marks, or figures.
    +
    +
    + +
    + + + +
    +
    + Captures equations in PDFs and images as LaTeX rather than approximate OCR text. This is a + billed Document Intelligence add-on that adds per-page cost to every Enhanced + extraction, so it is off by default. It applies to the Layout model only, so it has no effect + while extraction is set to Standard. +
    + + {% if content_understanding_supported %} +
    +
    +
    Azure AI Content Understanding
    + +
    +

    + Required for Enhanced extraction. Deployed from a Microsoft Foundry resource. + If this is left blank, Enhanced falls back to Document Intelligence Layout. +

    + +
    + + +
    + Your Microsoft Foundry resource endpoint, without a trailing path. +
    +
    + +
    + + +
    + Managed identity requires the Cognitive Services User role on the Foundry resource. +
    +
    + +
    + +
    + + +
    +
    + +
    +
    + + +
    Default: {{ content_understanding_api_version_default }}
    +
    +
    + + +
    Default: {{ content_understanding_document_analyzer_default }}
    +
    +
    + + +
    Default: {{ content_understanding_image_analyzer_default }}
    +
    +
    + + +
    +
    + {% endif %} +
    + +
    +
    Images Inside Office Files
    +

    + Neither extraction engine describes figures inside Word and PowerPoint files. When this is on, + embedded images are pulled out of the file, analyzed with whichever engine backs the selected + extraction mode, and indexed as their own citable chunks. This works with Standard extraction + too, using Document Intelligence. +

    +
    + + +
    +
    +
    + + +
    Images narrower or shorter than this are skipped as icons or spacers.
    +
    +
    + + +
    Caps per-document cost. Duplicate images are analyzed once.
    +
    +
    +
    + + + + +
    + + + +
    + +
    + +
    +
    + + +
    +
    + + +
    +
    + +
    + + +
    +
    +
    + + +
    +
    + + +
    +
    + +
    + + +
    +
    +
    + + + +
    +
    +
    + +
    +
    +
    Chunk Sizes
    + Cap: {{ chunk_size_cap }} +
    +

    Custom chunk sizes apply to new uploads only. Existing documents keep their current chunks.

    +
    + Heads up: Overrides are capped at {{ chunk_size_cap }} (2x embedding context window, fallback 16,384). +
    +
    + + +
    + +
    + + + {% set chunk_settings = chunk_size_settings or {} %} + {% set chunk_defaults = chunk_size_defaults or {} %} +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    Minimum enforced at 50% of target on merge.
    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    + +
    + + +
    Applies to new audio transcripts.
    +
    +
    + + +
    Pages per chunk after extraction.
    +
    +
    + + +
    Slides per chunk after extraction.
    +
    +
    +
    +
    + + +
    +
    + Metadata Extraction +
    +

    + Enable this to automatically parse and store file metadata for advanced indexing and search. +

    +
    + + + +
    + +
    + + + +
    + Uses Global Endpoints when multi-endpoint model management is enabled; otherwise uses the legacy GPT/APIM deployment settings. +
    +
    +
    + + +
    +
    + Multi-Modal Vision Analysis +
    +

    + Enable AI-powered vision analysis for images uploaded to chat or workspace. When enabled alongside Document Intelligence OCR, images will receive both text extraction (OCR) and semantic understanding (vision AI). +

    + +
    + How it works: +
      +
    • Document Intelligence: Extracts text from images (OCR)
    • +
    • Vision Model: Provides semantic analysis, object detection, and contextual understanding
    • +
    • Both analyses are combined and available in citations when Enhanced Citations is enabled
    • +
    +
    + +
    + + + +
    + +
    + + +
    Select a GPT model with vision capabilities (for example, gpt-4o or supported GPT 5 and later models). Only vision-capable models are shown.
    + + +
    +
    +
    +
    diff --git a/application/single_app/templates/admin/_panes/files-sharing.html b/application/single_app/templates/admin/_panes/files-sharing.html new file mode 100644 index 000000000..a66a04b76 --- /dev/null +++ b/application/single_app/templates/admin/_panes/files-sharing.html @@ -0,0 +1,223 @@ +
    +
    +
    + File Downloads +
    +

    + Control when users can download original workspace document files from personal, group, and public workspaces. +

    +
    + + + +
    +
    +
    + + + +
    +
    + + + +
    +
    + + No groups assigned. +
    + +
    +
    + + + +
    +
    + + + +
    +
    + + No public workspaces assigned. +
    + +
    + + + + + + +
    +
    + File Sharing +
    +

    + Turn this on to enable file sharing capabilities between users and workspaces. +

    +
    + + + +
    +
    + + + + + +
    +
    + Shared Conversation File Approvals +
    +

    Files generated by participants in a shared conversation are saved into the conversation owner's storage. When enabled, those files are held until an approver releases them.

    +
    + + + +
    +
    + +
    diff --git a/application/single_app/templates/admin/_panes/search-extract.html b/application/single_app/templates/admin/_panes/search-extract.html deleted file mode 100644 index 04de8cc88..000000000 --- a/application/single_app/templates/admin/_panes/search-extract.html +++ /dev/null @@ -1,1698 +0,0 @@ -
    - -

    - Configure Azure AI Search, Document Intelligence, and multimedia support settings. -

    - -
    -
    -
    Web Search (Azure AI Foundry Agent)
    - -
    -

    Enable web search by routing queries through an Azure AI Foundry agent configured by admins.

    -
    - - -
    - - - -
    - -
    -
    -
    URL Access
    - Shared Policy -
    -

    Control direct URL content fetching for pasted chat links, workflow prompts, and Deep Research source review.

    -
    - - -
    -
    - - -
    Required app role value: UrlAccessUser. Assign this role to users or groups in the Enterprise App before enabling the requirement. When enabled, only assigned users can use URL Access in chat or enable it for workflows.
    -
    - -
    -
    - URL Access uses the same server-side URL protections as Deep Research: non-HTTP(S) URLs, credentialed URLs, literal IP targets, localhost, metadata hosts, unsafe redirects, unsupported content types, and oversized pages are blocked before fetch. -
    -
    -
    - - -
    Hard limit: 100 direct URLs per chat message.
    -
    -
    - - -
    Hard limit: 500 direct URLs per workflow prompt.
    -
    -
    -
    - -
    -
    -
    - - -
    - - -
    -
    -
    -
    Leave blank to allow any public domain that passes safety checks. Deep Research uses this same allow list.
    -
    -
    -
    - - -
    - - -
    -
    -
    -
    Blocked domains apply to URL Access and Deep Research source-page review.
    -
    -
    - -
    -
    -
    -
    URL Policy Test
    -
    Check a URL against the current allowed and blocked domain rules before saving.
    -
    - -
    -
    -
    - -
    -
    -
    Deep Research
    -
    - Optional - -
    -
    -

    Plan bounded web searches, inspect source pages, and keep an internal research ledger for evidence coverage. Direct pasted URLs use the shared URL Access controls above.

    -
    - - -
    - -
    -
    - Server-side protections always block non-HTTP(S) URLs, literal IP URL targets, localhost, metadata hosts, credentialed URLs, excessive redirects, unsupported content types, oversized pages, and requests beyond the configured page budget. DNS hostnames that resolve to private/internal addresses require the internal-host opt-in below. -
    - -
    - - -
    Required app role value: DeepResearchUser. Assign this role to users or groups in the Enterprise App before enabling the requirement. When enabled, only assigned users can use Deep Research.
    -
    - -
    - - -
    Allows DNS hostnames that resolve to private/internal addresses. Literal IP URL targets, localhost, metadata hosts, link-local addresses, and reserved addresses remain blocked.
    -
    - -
    -
    - - - -
    Deep Research runs only when the user selects it for the message.
    -
    -
    - - -
    Hard limit: 10 pages.
    -
    -
    - - -
    Limits initial search-result and direct URL pages so budget remains for child pages.
    -
    -
    - - -
    Direct URLs beyond this cap are recorded as omitted in the ledger.
    -
    -
    - - -
    Includes the original current-message query.
    -
    -
    - - -
    Hard limit: 30 seconds.
    -
    -
    - - -
    Every redirect target is revalidated.
    -
    -
    - - -
    Hard limit: 5 MB.
    -
    -
    - - -
    Depth 2 follows selected links from seed and child pages.
    -
    -
    - -
    -
    - {% set js_runtime = source_review_runtime_capabilities or {} %} -
    - - -
    -
    Deep Research follows only scored, policy-approved links within the page and depth budgets.
    -
    -
    -
    - - -
    -
    The selected chat model can propose bounded current-message-only query variants before page review.
    -
    -
    -
    - - -
    -
    Creates a Markdown chat artifact with search queries, reviewed sources, skipped URLs, and coverage.
    -
    -
    -
    - - -
    -
    When source pages expose candidate links, the selected chat model can rank those candidates before the server fetches them.
    -
    -
    -
    - - -
    -
    Requires verified Playwright browser runtime support on the app host.
    -
    - {{ js_runtime.message or 'Runtime support has not been checked yet.' }} -
    - {% if not js_runtime.js_rendering_available %} -
    Install/enable the Playwright Chromium runtime, then reload Admin Settings to enable this option.
    - {% endif %} - {% if js_runtime.sandbox_disabled %} -
    Chromium sandbox is disabled by environment configuration.
    - {% endif %} -
    -
    - - -
    When JavaScript rendering is enabled, Deep Research can click visible Load More controls until this cap is reached.
    -
    -
    -
    - - -
    -
    -
    -
    - - -
    -
    -
    -
    -
    - - - - - - - - -
    -
    Azure AI Search
    -

    - Configure Azure AI Search settings. -

    -
    - - - -
    - -
    - -
    -
    - - -
    -
    - - -
    -
    - -
    - - -
    -
    -
    - - -
    -
    - - -
    -
    - -
    - - -
    -
    -
    - - - -
    -
    -
    - - -
    -
    Document Intelligence
    -

    - Configure Azure Document Intelligence settings. Document Intelligence always powers - Standard extraction and is required for workspaces and chat file uploads. -

    -
    - - - -
    - -
    - {% if content_understanding_supported %} -
    - Enhanced extraction uses Azure AI Content Understanding, which returns tables, - page structure, checkbox states, and AI-generated descriptions of figures, charts, and images. - Configure the connection below. -
    - {% else %} -
    - Azure AI Content Understanding is not available in the - {{ azure_environment }} cloud, so Enhanced extraction uses - Document Intelligence Layout here. There is nothing more to configure — - Enhanced still captures tables, page structure, forms, and checkbox states. -
    - {% endif %} - -
    - - -
    - Enhanced captures more document detail for PDFs and images, including tables, page structure, and checked or unchecked marks. It adds latency and has a 6X increase for every 1000 pages when selected. -
    -
    - - -
    - Auto samples this many first PDF pages with Document Intelligence Layout. If it detects tables, selection marks, or figures, the full PDF uses Enhanced; otherwise it finishes with Standard. Images use Enhanced in Auto mode. -
    -
    -
    -
    Standard: Document Intelligence Read. Fastest and lowest-cost path for plain text PDFs and images.
    -
    Enhanced: best for tables, section structure, page layout, forms, checkbox states, and figure descriptions. Expect more latency and higher cost.
    -
    Auto: samples the first pages with Document Intelligence Layout, then uses Enhanced only when the sample shows tables, selection marks, or figures.
    -
    -
    - -
    - - - -
    -
    - Captures equations in PDFs and images as LaTeX rather than approximate OCR text. This is a - billed Document Intelligence add-on that adds per-page cost to every Enhanced - extraction, so it is off by default. It applies to the Layout model only, so it has no effect - while extraction is set to Standard. -
    - - {% if content_understanding_supported %} -
    -
    -
    Azure AI Content Understanding
    - -
    -

    - Required for Enhanced extraction. Deployed from a Microsoft Foundry resource. - If this is left blank, Enhanced falls back to Document Intelligence Layout. -

    - -
    - - -
    - Your Microsoft Foundry resource endpoint, without a trailing path. -
    -
    - -
    - - -
    - Managed identity requires the Cognitive Services User role on the Foundry resource. -
    -
    - -
    - -
    - - -
    -
    - -
    -
    - - -
    Default: {{ content_understanding_api_version_default }}
    -
    -
    - - -
    Default: {{ content_understanding_document_analyzer_default }}
    -
    -
    - - -
    Default: {{ content_understanding_image_analyzer_default }}
    -
    -
    - - -
    -
    - {% endif %} -
    - -
    -
    Images Inside Office Files
    -

    - Neither extraction engine describes figures inside Word and PowerPoint files. When this is on, - embedded images are pulled out of the file, analyzed with whichever engine backs the selected - extraction mode, and indexed as their own citable chunks. This works with Standard extraction - too, using Document Intelligence. -

    -
    - - -
    -
    -
    - - -
    Images narrower or shorter than this are skipped as icons or spacers.
    -
    -
    - - -
    Caps per-document cost. Duplicate images are analyzed once.
    -
    -
    -
    - - - - -
    - - - -
    - -
    - -
    -
    - - -
    -
    - - -
    -
    - -
    - - -
    -
    -
    - - -
    -
    - - -
    -
    - -
    - - -
    -
    -
    - - - -
    -
    -
    - -
    -
    -
    Chunk Sizes
    - Cap: {{ chunk_size_cap }} -
    -

    Custom chunk sizes apply to new uploads only. Existing documents keep their current chunks.

    -
    - Heads up: Overrides are capped at {{ chunk_size_cap }} (2x embedding context window, fallback 16,384). -
    -
    - - -
    - -
    - - - {% set chunk_settings = chunk_size_settings or {} %} - {% set chunk_defaults = chunk_size_defaults or {} %} -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    Minimum enforced at 50% of target on merge.
    -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    - -
    - - -
    Applies to new audio transcripts.
    -
    -
    - - -
    Pages per chunk after extraction.
    -
    -
    - - -
    Slides per chunk after extraction.
    -
    -
    -
    -
    - - -
    -
    -
    -
    - -
    -
    -

    - AI Video Intelligence -

    -

    Extract insights and make video content searchable and interactive

    -
    -
    - -
    - -
    - - Make Video Content Fully Searchable: Upload videos to your workspace and automatically extract transcripts, identify speakers, detect topics, and generate searchable timestamps. Your AI can then answer questions about video content, cite specific moments, and provide deep insights. -
    - - -
    -
    - - -
    -

    - Upload video files (MP4, AVI, MOV, etc.) to workspaces where Azure Video Indexer automatically processes them. - Extract spoken content, identify speakers, detect faces, recognize brands, and generate searchable metadata—perfect for meetings, presentations, training videos, and recorded content. -

    -
    - - Full-Text Search - - - Speaker ID - - - Timestamped Citations - - - Multi-Language - -
    -
    - - {% set public_video_indexer_endpoint = "https://api.videoindexer.ai" %} - {% set government_video_indexer_endpoint = "https://api.videoindexer.ai.azure.us" %} - {% set configured_video_indexer_endpoint = settings.video_indexer_endpoint or default_video_indexer_endpoint %} - {% if configured_video_indexer_endpoint == government_video_indexer_endpoint %} - {% set video_indexer_cloud = "usgovernment" %} - {% elif configured_video_indexer_endpoint == public_video_indexer_endpoint %} - {% set video_indexer_cloud = "public" %} - {% else %} - {% set video_indexer_cloud = "custom" %} - {% endif %} - {% if azure_environment == "usgovernment" %} - {% set azure_environment_label = "Azure Government" %} - {% elif azure_environment == "custom" %} - {% set azure_environment_label = "Custom Cloud" %} - {% else %} - {% set azure_environment_label = "Azure Public / Commercial" %} - {% endif %} - - -
    -
    - Azure Video Indexer Configuration -
    -

    Connect to your Azure Video Indexer resource for advanced video processing and content extraction.

    - -
    - Identity overview: -
      -
    • Video Indexer can use its own managed identity to reach the linked Storage account during account creation.
    • -
    • SimpleChat authenticates to Video Indexer with the App Service system-assigned managed identity.
    • -
    • The App Service managed identity must have Contributor on the Video Indexer resource.
    • -
    • See Azure Video Indexer documentation for setup details
    • -
    -
    - -
    - - -
    Choose the endpoint family that matches your deployed cloud. Use Custom only when you need a non-standard Video Indexer endpoint.
    -
    - -
    - Current App Service environment: {{ azure_environment_label }}. This selector saves the Video Indexer API endpoint. If you are switching between Azure Public, Azure Government, or a custom cloud, update AZURE_ENVIRONMENT in your App Service configuration as well. -
    - -
    - The selected Video Indexer cloud does not match this app's current AZURE_ENVIRONMENT. Update the App Service configuration if you are moving to a different cloud. -
    - - - -
    - - -
    Only required when using a custom cloud or non-standard Video Indexer endpoint.
    -
    - -
    - - -
    Saved endpoint value used for Video Indexer API calls.
    -
    - -
    - - -
    The Azure resource group containing your Video Indexer account
    -
    - -
    - - -
    Your Azure subscription ID
    -
    - -
    - - -
    The name of your Video Indexer account resource
    -
    - -
    - - -
    Azure region where your Video Indexer account is deployed (e.g., eastus, westus2, northeurope)
    -
    - -
    - - -
    Found in the Video Indexer account Overview page in Azure Portal
    -
    - -
    - - -
    Default for {{ azure_environment_label }}: {{ default_video_indexer_arm_api_version }}
    -
    - -
    - - -
    -
    -
    - - -
    -
    -
    -
    - -
    -
    -

    - AI Voice Conversations -

    -

    Transform your AI experience with natural voice interactions

    -
    -
    - -
    - - -
    -
    - - -
    -

    - Let users opt in to a bundled sound when an AI response finishes outside their active view. - These local cues do not require Azure Speech Service. -

    -
    - -
    - - One Speech resource, three features: Audio file uploads, Voice Input, and Voice Responses all use the same Azure Speech Service section below. Configure the Speech resource once, then turn on whichever speech features you need. -
    - - -
    - {% set audio_runtime = audio_runtime_capabilities or {} %} - {% set audio_supported_extensions = audio_runtime.supported_extensions or [] %} - {% set audio_direct_extensions = audio_runtime.direct_transcription_extensions or [] %} -
    - - -
    -

    - Upload audio files (MP3, WAV, M4A, etc.) to workspaces where they're automatically transcribed and indexed. - The AI can then search, analyze, and answer questions about audio content—perfect for meetings, interviews, lectures, and podcasts. -

    -
    -
    - {{ audio_runtime.message or 'Audio runtime support has not been checked yet.' }} -
    -
    - Supported audio upload extensions: {{ audio_supported_extensions|join(', ') }} -
    - {% if audio_direct_extensions %} -
    - Without FFmpeg, source-file fallback is best for: {{ audio_direct_extensions|join(', ') }}. Container builds can include FFmpeg for broader codec support. -
    - {% endif %} -
    -
    - - -
    -
    - - -
    -

    - Talk to your AI instead of typing. Record voice messages up to 90 seconds directly in the chat interface. - Azure Speech Service instantly transcribes your speech with high accuracy, supporting multiple languages and accents. -

    -
    - - Hands-Free - - - Accessible - - - Fast Input - -
    -
    - - -
    -
    - - -
    -

    - Hear AI responses read aloud in natural, human-like voices powered by Azure's advanced neural Text-to-Speech. - Each message includes a speaker button—click to listen while multitasking, commuting, or whenever reading isn't convenient. -

    -
    - - Natural Voices - - - Multitask-Friendly - - - Multi-Language - -
    -
    - - -
    -
    - Azure Speech Service Configuration -
    -

    Configure the shared Azure Speech resource used by audio transcription, voice input, and text-to-speech.

    - -
    - Shared configuration: These settings apply to all enabled speech features. If you use managed identity and also enable Voice Responses, fill in the Speech Resource ID field so the Speech SDK can authenticate synthesis requests. -
    - -
    - - -
    Use the resource-specific custom-domain endpoint when selecting Managed Identity.
    -
    - -
    - - -
    Required for speech recognition locale defaults and for text-to-speech when using Managed Identity.
    -
    - -
    -
    - Resource ID builder: If the full ARM resource ID is hard to find in the portal, enter the subscription ID, resource group, and Speech resource name below. Simple Chat can build the resource ID for you. -
    - -
    -
    - - -
    -
    - - -
    -
    - - -
    If you use a custom-domain Speech endpoint, this is usually the first part of that hostname.
    -
    -
    - -
    -
    - -
    Provide Subscription ID, Resource Group, and Speech Resource Name to auto-build the ARM resource ID.
    - - - -
    Required for Voice Responses when using Managed Identity. You can paste the full value manually, or let the fields above build it for you.
    -
    - -
    - - -
    - -
    - - -
    -
    - -
    - - -
    -
    Required only when Authentication Type is set to Key.
    -
    - -
    - Managed identity roles: Start with Cognitive Services Speech User. If fast transcription or other write-style speech operations still return authorization errors, add Cognitive Services Speech Contributor. Managed identity also requires a custom-domain endpoint. -
    - -
    -

    - - - Enhanced Citations - - will dramatically improve the citation experience for video and audio files. - -

    -
    -
    diff --git a/application/single_app/templates/admin/_panes/search-index.html b/application/single_app/templates/admin/_panes/search-index.html new file mode 100644 index 000000000..285f96da9 --- /dev/null +++ b/application/single_app/templates/admin/_panes/search-index.html @@ -0,0 +1,119 @@ +
    +
    +
    Azure AI Search
    +

    + Configure Azure AI Search settings. +

    +
    + + + +
    + +
    + +
    +
    + + +
    +
    + + +
    +
    + +
    + + +
    +
    +
    + + +
    +
    + + +
    +
    + +
    + + +
    +
    +
    + + + +
    +
    +
    + + +
    diff --git a/application/single_app/templates/admin/_panes/web-research.html b/application/single_app/templates/admin/_panes/web-research.html new file mode 100644 index 000000000..7ad547075 --- /dev/null +++ b/application/single_app/templates/admin/_panes/web-research.html @@ -0,0 +1,519 @@ +
    + +

    + Configure Azure AI Search, Document Intelligence, and multimedia support settings. +

    + +
    +
    +
    Web Search (Azure AI Foundry Agent)
    + +
    +

    Enable web search by routing queries through an Azure AI Foundry agent configured by admins.

    +
    + + +
    + + + +
    + +
    +
    +
    URL Access
    + Shared Policy +
    +

    Control direct URL content fetching for pasted chat links, workflow prompts, and Deep Research source review.

    +
    + + +
    +
    + + +
    Required app role value: UrlAccessUser. Assign this role to users or groups in the Enterprise App before enabling the requirement. When enabled, only assigned users can use URL Access in chat or enable it for workflows.
    +
    + +
    +
    + URL Access uses the same server-side URL protections as Deep Research: non-HTTP(S) URLs, credentialed URLs, literal IP targets, localhost, metadata hosts, unsafe redirects, unsupported content types, and oversized pages are blocked before fetch. +
    +
    +
    + + +
    Hard limit: 100 direct URLs per chat message.
    +
    +
    + + +
    Hard limit: 500 direct URLs per workflow prompt.
    +
    +
    +
    + +
    +
    +
    + + +
    + + +
    +
    +
    +
    Leave blank to allow any public domain that passes safety checks. Deep Research uses this same allow list.
    +
    +
    +
    + + +
    + + +
    +
    +
    +
    Blocked domains apply to URL Access and Deep Research source-page review.
    +
    +
    + +
    +
    +
    +
    URL Policy Test
    +
    Check a URL against the current allowed and blocked domain rules before saving.
    +
    + +
    +
    +
    + +
    +
    +
    Deep Research
    +
    + Optional + +
    +
    +

    Plan bounded web searches, inspect source pages, and keep an internal research ledger for evidence coverage. Direct pasted URLs use the shared URL Access controls above.

    +
    + + +
    + +
    +
    + Server-side protections always block non-HTTP(S) URLs, literal IP URL targets, localhost, metadata hosts, credentialed URLs, excessive redirects, unsupported content types, oversized pages, and requests beyond the configured page budget. DNS hostnames that resolve to private/internal addresses require the internal-host opt-in below. +
    + +
    + + +
    Required app role value: DeepResearchUser. Assign this role to users or groups in the Enterprise App before enabling the requirement. When enabled, only assigned users can use Deep Research.
    +
    + +
    + + +
    Allows DNS hostnames that resolve to private/internal addresses. Literal IP URL targets, localhost, metadata hosts, link-local addresses, and reserved addresses remain blocked.
    +
    + +
    +
    + + + +
    Deep Research runs only when the user selects it for the message.
    +
    +
    + + +
    Hard limit: 10 pages.
    +
    +
    + + +
    Limits initial search-result and direct URL pages so budget remains for child pages.
    +
    +
    + + +
    Direct URLs beyond this cap are recorded as omitted in the ledger.
    +
    +
    + + +
    Includes the original current-message query.
    +
    +
    + + +
    Hard limit: 30 seconds.
    +
    +
    + + +
    Every redirect target is revalidated.
    +
    +
    + + +
    Hard limit: 5 MB.
    +
    +
    + + +
    Depth 2 follows selected links from seed and child pages.
    +
    +
    + +
    +
    + {% set js_runtime = source_review_runtime_capabilities or {} %} +
    + + +
    +
    Deep Research follows only scored, policy-approved links within the page and depth budgets.
    +
    +
    +
    + + +
    +
    The selected chat model can propose bounded current-message-only query variants before page review.
    +
    +
    +
    + + +
    +
    Creates a Markdown chat artifact with search queries, reviewed sources, skipped URLs, and coverage.
    +
    +
    +
    + + +
    +
    When source pages expose candidate links, the selected chat model can rank those candidates before the server fetches them.
    +
    +
    +
    + + +
    +
    Requires verified Playwright browser runtime support on the app host.
    +
    + {{ js_runtime.message or 'Runtime support has not been checked yet.' }} +
    + {% if not js_runtime.js_rendering_available %} +
    Install/enable the Playwright Chromium runtime, then reload Admin Settings to enable this option.
    + {% endif %} + {% if js_runtime.sandbox_disabled %} +
    Chromium sandbox is disabled by environment configuration.
    + {% endif %} +
    +
    + + +
    When JavaScript rendering is enabled, Deep Research can click visible Load More controls until this cap is reached.
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + + + + + + + + +
    diff --git a/application/single_app/templates/admin/_panes/workflow.html b/application/single_app/templates/admin/_panes/workflow.html new file mode 100644 index 000000000..242119643 --- /dev/null +++ b/application/single_app/templates/admin/_panes/workflow.html @@ -0,0 +1,157 @@ +
    +
    +
    + Workflow +
    +

    + Control personal and group workflow authoring and execution for users. +

    +
    + + + +
    +
    + + + +
    +

    + Required app role value: WorkflowUser. Assign this role to users or groups in the Enterprise App before enabling the requirement. +

    + +
    + + +
    + Maximum automatic tool or action calls an agent can make during one workflow run. Default is 60; increase for large document sets. +
    + +
    + +
    + + +
    + Maximum ordered instruction tasks users can add to one workflow. Default is 50; supported range is 1-100. +
    +
    + +
    + +
    + + + +
    +
    + + + +
    +
    + + No groups assigned. +
    + +

    + Group workflow authoring is available to group Owners and Admins by default. Enable the owner-only setting below to limit group agent, action, and workflow management to Owners. +

    +
    + + + + +
    diff --git a/application/single_app/templates/admin/_panes/workspace-types.html b/application/single_app/templates/admin/_panes/workspace-types.html new file mode 100644 index 000000000..83123fd7a --- /dev/null +++ b/application/single_app/templates/admin/_panes/workspace-types.html @@ -0,0 +1,159 @@ +
    +

    + Configure workspace settings like personal/group access, multimedia support, metadata, and document classification. +

    + + +
    +
    + Personal Workspaces +
    +

    + Turn this on to allow access and management of your personal workspace. +

    +
    + + + +
    +
    + +
    +
    + Group Workspaces +
    +

    + Turn this on to allow access and management of group workspaces, as well as group collaboration features. +

    +
    + + + +
    + + +
    +
    + + +
    + + + +
    +

    + When enabled, no users will be able to create new groups, regardless of app role assignment. This global setting overrides the Require CreateGroups App Role setting below. +

    + +
    + + + +
    +

    + Required app role value: CreateGroups. Assign this role to users or groups in the Enterprise App before enabling the requirement. If disabled, any authenticated user can create groups while group workspaces and group creation are enabled. +

    + +
    + + +
    + + + +
    +

    + If enabled, only the group Owner can create, edit, and delete group agents, group actions, and group workflows. Admins and other roles will only be able to view them. +

    +
    + +
    + +
    +
    + Public Workspaces +
    +

    + Turn this on to enable public workspaces that are visible to everyone in the organization. +

    +
    + + + +
    + +
    + + +
    + Optional. End users will see this label instead of Public Workspace. Admin settings and internal references continue to use Public Workspace. +
    +
    + +
    +
    +
    + + + +
    +

    + Required app role value: CreatePublicWorkspaces. Assign this role to users or groups in the Enterprise App before enabling the requirement. If disabled, any authenticated user can create public workspaces while public workspaces are enabled. +

    +
    + +
    + + +
    diff --git a/application/single_app/templates/admin/_panes/workspaces.html b/application/single_app/templates/admin/_panes/workspaces.html deleted file mode 100644 index 690a902af..000000000 --- a/application/single_app/templates/admin/_panes/workspaces.html +++ /dev/null @@ -1,643 +0,0 @@ -
    -

    - Configure workspace settings like personal/group access, multimedia support, metadata, and document classification. -

    - - -
    -
    - Personal Workspaces -
    -

    - Turn this on to allow access and management of your personal workspace. -

    -
    - - - -
    -
    - -
    -
    - Workflow -
    -

    - Control personal and group workflow authoring and execution for users. -

    -
    - - - -
    -
    - - - -
    -

    - Required app role value: WorkflowUser. Assign this role to users or groups in the Enterprise App before enabling the requirement. -

    - -
    - - -
    - Maximum automatic tool or action calls an agent can make during one workflow run. Default is 60; increase for large document sets. -
    - -
    - -
    - - -
    - Maximum ordered instruction tasks users can add to one workflow. Default is 50; supported range is 1-100. -
    -
    - -
    - -
    - - - -
    -
    - - - -
    -
    - - No groups assigned. -
    - -

    - Group workflow authoring is available to group Owners and Admins by default. Enable the owner-only setting below to limit group agent, action, and workflow management to Owners. -

    -
    - -
    -
    - File Downloads -
    -

    - Control when users can download original workspace document files from personal, group, and public workspaces. -

    -
    - - - -
    -
    -
    - - - -
    -
    - - - -
    -
    - - No groups assigned. -
    - -
    -
    - - - -
    -
    - - - -
    -
    - - No public workspaces assigned. -
    - -
    - - - - - - - - -
    -
    - Group Workspaces -
    -

    - Turn this on to allow access and management of group workspaces, as well as group collaboration features. -

    -
    - - - -
    - - -
    -
    - - -
    - - - -
    -

    - When enabled, no users will be able to create new groups, regardless of app role assignment. This global setting overrides the Require CreateGroups App Role setting below. -

    - -
    - - - -
    -

    - Required app role value: CreateGroups. Assign this role to users or groups in the Enterprise App before enabling the requirement. If disabled, any authenticated user can create groups while group workspaces and group creation are enabled. -

    - -
    - - -
    - - - -
    -

    - If enabled, only the group Owner can create, edit, and delete group agents, group actions, and group workflows. Admins and other roles will only be able to view them. -

    -
    - -
    - -
    -
    - Public Workspaces -
    -

    - Turn this on to enable public workspaces that are visible to everyone in the organization. -

    -
    - - - -
    - -
    - - -
    - Optional. End users will see this label instead of Public Workspace. Admin settings and internal references continue to use Public Workspace. -
    -
    - -
    -
    -
    - - - -
    -

    - Required app role value: CreatePublicWorkspaces. Assign this role to users or groups in the Enterprise App before enabling the requirement. If disabled, any authenticated user can create public workspaces while public workspaces are enabled. -

    -
    - -
    - -
    -
    - File Sharing -
    -

    - Turn this on to enable file sharing capabilities between users and workspaces. -

    -
    - - - -
    -
    - - -
    -
    - Metadata Extraction -
    -

    - Enable this to automatically parse and store file metadata for advanced indexing and search. -

    -
    - - - -
    - -
    - - - -
    - Uses Global Endpoints when multi-endpoint model management is enabled; otherwise uses the legacy GPT/APIM deployment settings. -
    -
    -
    - - -
    -
    - Multi-Modal Vision Analysis -
    -

    - Enable AI-powered vision analysis for images uploaded to chat or workspace. When enabled alongside Document Intelligence OCR, images will receive both text extraction (OCR) and semantic understanding (vision AI). -

    - -
    - How it works: -
      -
    • Document Intelligence: Extracts text from images (OCR)
    • -
    • Vision Model: Provides semantic analysis, object detection, and contextual understanding
    • -
    • Both analyses are combined and available in citations when Enhanced Citations is enabled
    • -
    -
    - -
    - - - -
    - -
    - - -
    Select a GPT model with vision capabilities (for example, gpt-4o or supported GPT 5 and later models). Only vision-capable models are shown.
    - - -
    -
    -
    - - - -
    diff --git a/application/single_app/templates/admin_settings.html b/application/single_app/templates/admin_settings.html index 118431f86..0d1d9e4b1 100644 --- a/application/single_app/templates/admin_settings.html +++ b/application/single_app/templates/admin_settings.html @@ -1020,7 +1020,9 @@

    12. Enhanced Citations and Image Generation

    {% include "admin/_panes/file-sync.html" %} - {% include "admin/_panes/workspaces.html" %} + {% include "admin/_panes/workspace-types.html" %} + {% include "admin/_panes/files-sharing.html" %} + {% include "admin/_panes/workflow.html" %} {# Data Lifecycle: what happens to user data over time #} {% include "admin/_panes/retention.html" %} @@ -1037,7 +1039,10 @@

    12. Enhanced Citations and Image Generation

    - {% include "admin/_panes/search-extract.html" %} + {% include "admin/_panes/web-research.html" %} + {% include "admin/_panes/search-index.html" %} + {% include "admin/_panes/extraction.html" %} + {% include "admin/_panes/audio-video.html" %} {% include "admin/_panes/support-menu.html" %} {% include "admin/_panes/send-feedback.html" %} diff --git a/docs/explanation/features/ADMIN_SETTINGS_IA_REWORK_STATUS.md b/docs/explanation/features/ADMIN_SETTINGS_IA_REWORK_STATUS.md index 3632338b9..8fd27d02d 100644 --- a/docs/explanation/features/ADMIN_SETTINGS_IA_REWORK_STATUS.md +++ b/docs/explanation/features/ADMIN_SETTINGS_IA_REWORK_STATUS.md @@ -22,8 +22,8 @@ Development ─── feature/admin-settings-ia ──┬── #1297 stage 1 └──► final PR ──► Development ``` -Current version: **0.260.013**. Fingerprint: **462 field names / 110 card ids**. -Navigation: **13 groups / 31 tabs / 88 sections**. +Current version: **0.260.014**. Fingerprint: **462 field names / 110 card ids**. +Navigation: **14 groups / 36 tabs / 88 sections**. ### Shipped @@ -113,6 +113,34 @@ moved from Scale to Security that way. | Security | **Safety dismantled.** → Access & Roles, Secrets, Content Safety, Session, Network | | Operations | Logging is now Logging & Health, gaining health check and API documentation from General | | Help | Gains Support Menu from General | +| Knowledge | **Search & Extract dismantled.** → Web & Research, Search Index, Document Extraction (+ metadata extraction and multi-modal vision from Workspaces), Audio & Video | +| Workspaces | → Workspace Types, Files & Sharing (+ shared conversation file approvals from AI Models), Global Identities | +| Workflow | **New group**, split out of Workspaces | + +### Card container ids are not the nav section ids + +Several cards are wrapped in a container whose id differs from the nav map's +section id, which is an inner anchor: + +| Nav section id | Card container id | +|---|---| +| `web-search-section` | `web-search-foundry-section` | +| `embeddings-config` | `embeddings-configuration` | +| `image-config` | `image-generation-configuration` | +| `gpt-config` | `gpt-configuration` | +| `agents-config` | `agents-configuration` | +| `actions-config` | `actions-configuration` | + +**The move and split tools take container ids**; the nav map keeps its own. +Run `list_pane_cards.py` for the real container id rather than assuming. + +### Trap: gpt-configuration is inside a modal + +`gpt-configuration` is **not** a top-level card. It lives inside +`legacyModelSettingsModal` in the AI Models pane. Splitting AI Models means +moving a modal, not a card, which is why AI Models and Agents are deliberately +left for their own change rather than bundled with Knowledge and Workspaces. + ### Two hardcoded-id traps found and closed diff --git a/docs/explanation/release_notes.md b/docs/explanation/release_notes.md index 8380fa7a2..5d5f6154b 100644 --- a/docs/explanation/release_notes.md +++ b/docs/explanation/release_notes.md @@ -2,6 +2,36 @@ For feature-focused and fix-focused drill-downs by version, see [Features by Version](/explanation/features/) and [Fixes by Version](/explanation/fixes/). +### **(v0.260.014)** + +#### User Interface Enhancements + +* **Knowledge Settings Split By What They Actually Do** + * Search & Extract held eight cards spanning four unrelated jobs, from Bing consent to voice transcription. + * Knowledge now has **Web & Research** (web search, URL access, deep research), **Search Index** (Azure AI Search), **Document Extraction** (document intelligence, chunk sizes, plus metadata extraction and multi-modal vision brought over from Workspaces) and **Audio & Video** (video intelligence, voice conversations), alongside the existing File Sync. + * Voice and video sit under Knowledge rather than Chat because they are extraction pipelines that turn recordings into searchable content. + * (Ref: `web-research`, `search-index`, `extraction`, `audio-video`) + +* **Workspaces Focused On Workspaces** + * Workspaces mixed workspace types with file rules, workflow and extraction settings. + * It is now **Workspace Types** (personal, group, public), **Files & Sharing** (downloads, sharing, and shared conversation file approvals brought over from AI Models) and the existing Global Identities. + * (Ref: `workspace-types`, `files-sharing`) + +* **Workflow Is Its Own Area** + * Workflow drives approvals and assignment across every workspace type and was too large to sit as one card inside Workspaces. It now has its own group. + * (Ref: `workflow`, `workflow-settings-section`) + +#### Bug Fixes + +* **Group Workflow Assignment Dialog Could Not Open** + * The Group Workflow Assignment dialog ended up in a different tab from the button that opens it. Because an inactive tab is hidden, the dialog would not have appeared at all. + * The dialog now sits with its button, and a new check verifies this for every dialog in Admin Settings so it cannot happen again. + * (Ref: `groupWorkflowAssignmentModal`, `test_admin_settings_modal_placement.py`) + +* **Misplaced Section Comments In AI Models** + * Two section comments had drifted onto the wrong cards while settings were being regrouped, labelling the embeddings card as processing thoughts. + * (Ref: `ai-models` pane) + ### **(v0.260.013)** #### User Interface Enhancements diff --git a/functional_tests/test_admin_settings_modal_placement.py b/functional_tests/test_admin_settings_modal_placement.py new file mode 100644 index 000000000..483f0ec02 --- /dev/null +++ b/functional_tests/test_admin_settings_modal_placement.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +""" +Functional test for Admin Settings modal placement across tab panes. +Version: 0.260.014 +Implemented in: 0.260.014 + +Admin Settings modals are interleaved between cards rather than collected at the +end of the template, so splitting a tab pane can silently strand a modal in a +pane that never opens it. A modal inside an inactive tab pane cannot be shown, +because the pane itself is hidden, so the failure is invisible until a user +clicks the button. + +This test ensures every modal lives in a pane that can actually open it. +""" + +import os +import re +import sys +import collections + +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +PANES_DIR = os.path.join( + os.path.dirname(os.path.dirname(os.path.abspath(__file__))), + "application", + "single_app", + "templates", + "admin", + "_panes", +) +SHELL = os.path.join( + os.path.dirname(os.path.dirname(os.path.abspath(__file__))), + "application", + "single_app", + "templates", + "admin_settings.html", +) + +MODAL_PATTERN = re.compile(r'class="modal fade[^"]*"\s+id="([^"]+)"') +TRIGGER_PATTERN = re.compile(r'data-bs-target="#([^"]+)"') + +# The shell sits outside every pane, so a modal there is reachable from anywhere +# and a trigger there is not owned by any one pane. +SHELL_KEY = "__shell__" + + +def _read(path): + with open(path, "r", encoding="utf-8") as handle: + return handle.read() + + +def _collect(): + """Map each modal id to its pane, and each modal id to the panes opening it.""" + modal_home = {} + trigger_homes = collections.defaultdict(set) + + sources = [(SHELL_KEY, SHELL)] + for name in sorted(os.listdir(PANES_DIR)): + if name.endswith(".html"): + sources.append((name[: -len(".html")], os.path.join(PANES_DIR, name))) + + for pane, path in sources: + markup = _read(path) + for match in MODAL_PATTERN.finditer(markup): + modal_home[match.group(1)] = pane + for match in TRIGGER_PATTERN.finditer(markup): + trigger_homes[match.group(1)].add(pane) + + return modal_home, trigger_homes + + +def test_modals_live_where_they_are_opened(): + """Every modal must sit in a pane that can open it, or in the shell.""" + print("Testing Admin Settings modal placement...") + + modal_home, trigger_homes = _collect() + assert modal_home, "No modals found; the collector is probably looking in the wrong place" + + stranded = [] + for modal_id, home in sorted(modal_home.items()): + if home == SHELL_KEY: + continue + openers = trigger_homes.get(modal_id) + if not openers: + # Opened from JavaScript rather than markup, which this test cannot + # attribute to a pane. Those are covered by the card link tests. + continue + foreign = {pane for pane in openers if pane not in (home, SHELL_KEY)} + if foreign and home not in openers: + stranded.append((modal_id, home, sorted(foreign))) + + assert not stranded, "Modals stranded in a pane that never opens them:\n" + "\n".join( + f" '{modal_id}' lives in '{home}' but is only opened from {openers}" + for modal_id, home, openers in stranded + ) + + print(f"All {len(modal_home)} modals sit in a pane that can open them.") + return True + + +def test_modal_ids_are_unique(): + """A duplicated modal id would make the wrong dialog open.""" + print("Testing Admin Settings modal id uniqueness...") + + counts = collections.Counter() + paths = [SHELL] + [ + os.path.join(PANES_DIR, name) + for name in sorted(os.listdir(PANES_DIR)) + if name.endswith(".html") + ] + for path in paths: + for match in MODAL_PATTERN.finditer(_read(path)): + counts[match.group(1)] += 1 + + duplicates = sorted(modal_id for modal_id, count in counts.items() if count > 1) + assert not duplicates, f"Modal ids declared more than once: {duplicates}" + + print(f"All {len(counts)} modal ids are unique.") + return True + + +if __name__ == "__main__": + tests = [ + test_modals_live_where_they_are_opened, + test_modal_ids_are_unique, + ] + results = [] + for test in tests: + print(f"\nRunning {test.__name__}...") + try: + results.append(bool(test())) + except Exception as error: # noqa: BLE001 - report and continue + print(f"Test failed: {error}") + import traceback + + traceback.print_exc() + results.append(False) + + print(f"\nResults: {sum(results)}/{len(results)} tests passed") + sys.exit(0 if all(results) else 1) From 6343cfa01a0e268f030e9ed59d9aba0d50c94945 Mon Sep 17 00:00:00 2001 From: Paul Lizer Date: Wed, 19 Aug 2026 16:42:23 -0400 Subject: [PATCH 21/25] Stage D part 5: AI Models and Agents & Actions The last two catch-all tabs. Both needed modal surgery rather than plain card moves, which is why they were held back. AI Models Model Endpoints multi-endpoint-configuration, and the legacy model settings modal that holds the Chat Model card and is opened from it Embeddings embeddings-configuration Image Generation image-generation-configuration Agents & Actions Agents agents-configuration, agent-template-approvals-section Actions document-action-capabilities-card, actions-configuration Inbound MCP inbound-mcp-configuration and its three dialogs Navigation is now 14 groups / 40 tabs / 88 sections. Three splitting problems, and how each was solved gpt-configuration is not a top-level card. It lives inside legacyModelSettingsModal. The modal was relocated to sit directly after multi-endpoint-configuration, the card holding its trigger, and both were then assigned to the same tab so the modal's opening lines, the nested card and the closing lines landed together and it reassembled intact. legacyModelDiscoveryIdentityGuideModal is opened from endpoints, embeddings and image generation, which now sit in three different tabs, so it could only ever have opened from one of them. It moved to the shell, outside every pane. Checked first that it carries no name= attribute, since moving a field outside the form would change the save payload. It carries none. The inbound MCP block is a {% if mcp_ui_enabled %} conditional spanning a card and three modals. It was lifted out whole into its own pane rather than split. Tab-level conditions The nav map now supports condition on a tab, not just a section, so a tab whose entire pane is behind a feature flag disappears instead of rendering empty. Both renderers honour it. Verified: 40 tabs with mcp_ui_enabled, 39 without. Also removed seven dangling section comments left at the tail of panes, each labelling a card that had moved to another tab. Two location tests asserted document order that only meant something while everything shared one tab. They now assert the real invariant: the embeddings and image cards are not inside the legacy modal, and the document action card leads the tab it actually lives in. Verified field contract 462 names / 110 card ids, unchanged regression set 32 failures, identical to baseline jinja compile 42/42 admin templates xss sinks pass modal placement all modals reachable, all ids unique Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- application/single_app/admin_settings_nav.py | 46 +- application/single_app/config.py | 2 +- .../static/js/admin/admin_sidebar_nav.js | 1 + .../single_app/templates/_sidebar_nav.html | 4 + .../templates/admin/_panes/actions.html | 205 +++++ .../templates/admin/_panes/agents.html | 738 ------------------ .../admin/_panes/chat-experience.html | 2 - .../admin/_panes/classification.html | 2 - .../templates/admin/_panes/cosmos.html | 2 - .../templates/admin/_panes/embeddings.html | 148 ++++ .../admin/_panes/image-generation.html | 165 ++++ .../templates/admin/_panes/inbound-mcp.html | 535 +++++++++++++ .../{ai-models.html => model-endpoints.html} | 394 +--------- .../templates/admin/_panes/retention.html | 2 - .../single_app/templates/admin_settings.html | 87 ++- .../ADMIN_SETTINGS_IA_REWORK_STATUS.md | 41 +- docs/explanation/release_notes.md | 25 + ...n_document_action_capabilities_location.py | 16 +- ..._ai_models_tab_embedding_image_location.py | 37 +- 19 files changed, 1285 insertions(+), 1167 deletions(-) create mode 100644 application/single_app/templates/admin/_panes/actions.html create mode 100644 application/single_app/templates/admin/_panes/embeddings.html create mode 100644 application/single_app/templates/admin/_panes/image-generation.html create mode 100644 application/single_app/templates/admin/_panes/inbound-mcp.html rename application/single_app/templates/admin/_panes/{ai-models.html => model-endpoints.html} (53%) diff --git a/application/single_app/admin_settings_nav.py b/application/single_app/admin_settings_nav.py index b5c8b5e6f..48026e3ea 100644 --- a/application/single_app/admin_settings_nav.py +++ b/application/single_app/admin_settings_nav.py @@ -94,14 +94,31 @@ "icon": "bi-cpu", "tabs": [ { - "id": "ai-models", - "label": "AI Models", - "icon": "bi-cpu", + # The Chat Model card is reached through the legacy model + # settings dialog opened from here, so it is listed with the + # endpoints rather than as a tab of its own. + "id": "model-endpoints", + "label": "Model Endpoints", + "icon": "bi-hdd-network", "sections": [ {"id": "multi-endpoint-configuration", "label": "Model Endpoints", "icon": "bi-hdd-network"}, + {"id": "gpt-config", "label": "Chat Model", "icon": "bi-chat-square-text"}, + ], + }, + { + "id": "embeddings", + "label": "Embeddings", + "icon": "bi-vector-pen", + "sections": [ {"id": "embeddings-config", "label": "Embeddings", "icon": "bi-vector-pen"}, + ], + }, + { + "id": "image-generation", + "label": "Image Generation", + "icon": "bi-image", + "sections": [ {"id": "image-config", "label": "Image Generation", "icon": "bi-image"}, - {"id": "gpt-config", "label": "Chat Model", "icon": "bi-chat-square-text"}, ], }, ], @@ -113,13 +130,30 @@ "tabs": [ { "id": "agents", - "label": "Agents and Actions", + "label": "Agents", "icon": "bi-robot", "sections": [ - {"id": "document-action-capabilities-card", "label": "Document Action Capabilities", "icon": "bi-files"}, {"id": "agents-config", "label": "Agents Configuration", "icon": "bi-robot"}, {"id": "agent-template-approvals-section", "label": "Agent Template Approvals", "icon": "bi-layers", "condition": "enable_agent_template_gallery"}, + ], + }, + { + "id": "actions", + "label": "Actions", + "icon": "bi-plugin", + "sections": [ + {"id": "document-action-capabilities-card", "label": "Document Action Capabilities", "icon": "bi-files"}, {"id": "actions-config", "label": "Actions Configuration", "icon": "bi-plugin"}, + ], + }, + { + # The whole tab is behind mcp_ui_enabled, so the tab carries the + # condition rather than the single section inside it. + "id": "inbound-mcp", + "label": "Inbound MCP", + "icon": "bi-diagram-3", + "condition": "mcp_ui_enabled", + "sections": [ {"id": "inbound-mcp-configuration", "label": "Inbound MCP", "icon": "bi-diagram-3", "condition": "mcp_ui_enabled"}, ], }, diff --git a/application/single_app/config.py b/application/single_app/config.py index 992744396..27e50b825 100644 --- a/application/single_app/config.py +++ b/application/single_app/config.py @@ -96,7 +96,7 @@ EXECUTOR_TYPE = 'thread' EXECUTOR_MAX_WORKERS = 30 SESSION_TYPE = 'filesystem' -VERSION = "0.260.014" +VERSION = "0.260.015" IS_DEVELOPMENT = is_development_env_enabled() SESSION_COOKIE_SAMESITE = os.getenv('SESSION_COOKIE_SAMESITE', 'Lax') diff --git a/application/single_app/static/js/admin/admin_sidebar_nav.js b/application/single_app/static/js/admin/admin_sidebar_nav.js index 7e9538f73..1bf0e5e53 100644 --- a/application/single_app/static/js/admin/admin_sidebar_nav.js +++ b/application/single_app/static/js/admin/admin_sidebar_nav.js @@ -296,6 +296,7 @@ const LEGACY_TAB_REDIRECTS = { 'security': 'secrets', 'workspaces': 'workspace-types', 'search-extract': 'web-research', + 'ai-models': 'model-endpoints', }; function resolveAdminTabId(tabId) { diff --git a/application/single_app/templates/_sidebar_nav.html b/application/single_app/templates/_sidebar_nav.html index 9c1cf3b79..8c2880678 100644 --- a/application/single_app/templates/_sidebar_nav.html +++ b/application/single_app/templates/_sidebar_nav.html @@ -517,6 +517,9 @@ {% endif %} {% else %} + {% if not admin_tab.condition + or (admin_tab.condition == 'enable_agent_template_gallery' and app_settings.enable_agent_template_gallery) + or (admin_tab.condition == 'mcp_ui_enabled' and mcp_ui_enabled) %} {% endif %} + {% endif %} {% endfor %} diff --git a/application/single_app/templates/admin/_panes/actions.html b/application/single_app/templates/admin/_panes/actions.html new file mode 100644 index 000000000..221cfac5b --- /dev/null +++ b/application/single_app/templates/admin/_panes/actions.html @@ -0,0 +1,205 @@ +
    +
    +
    +
    +
    Document Action Capabilities
    +

    Configure the document actions shown in the Action dropdown in Chat and Workflow. These controls live in the Agents and Actions tab, but remain separate from the global agent and custom action cards below.

    +
    + Chat: 2-300 | Workflow: 2-1000 +
    + +
    +
    +
    +
    + + + +
    +
    +
    + + {{ analyze_capability.chat_max_documents }} +
    +
    +
    + +
    +
    + +
    +
    +
    + 2 + 300 +
    +
    +
    +
    + + {{ analyze_capability.workflow_max_documents }} +
    +
    +
    + +
    +
    + +
    +
    +
    + 2 + 1000 +
    +
    +
    +
    +
    +
    +
    + + + +
    +
    +
    + + {{ comparison_capability.chat_max_documents }} +
    +
    +
    + +
    +
    + +
    +
    +
    + 2 + 300 +
    +
    +
    +
    + + {{ comparison_capability.workflow_max_documents }} +
    +
    +
    + +
    +
    + +
    +
    +
    + 2 + 1000 +
    +
    +
    +
    +
    +
    + + +
    +
    + Global Actions Configuration +
    +

    + Configure custom actions and tools to extend functionality with integrations and specialized capabilities. +

    + + {% if not settings.enable_semantic_kernel %} +
    + Actions are not available while agents are disabled. Enable agents above to configure actions. +
    + {% else %} +
    +
    +
    Global Actions
    + +
    +

    Disable a global action to keep the configuration without exposing it to runtime action loading until it is re-enabled.

    + {% if settings.per_user_semantic_kernel %} + +
    +
    Workspace Action Feature Toggles
    +
    + + + + +
    +
    + + + + +
    +
    + {% endif %} + + +
    +
    Core Action Toggles
    +
    + + + +
    +
    + + + +
    +
    + + + +
    +
    + + + +
    +
    + + + +
    +
    + + + +
    +
    + + + +
    +
    +
    + Tabular Processing Action + +
    + Automatically enabled when Enhanced Citations is enabled +
    +
    + + + + + + + + + +
    Display NameDescriptionActions
    +
    + {% endif %} +
    +
    diff --git a/application/single_app/templates/admin/_panes/agents.html b/application/single_app/templates/admin/_panes/agents.html index 622dde43d..889caa9cc 100644 --- a/application/single_app/templates/admin/_panes/agents.html +++ b/application/single_app/templates/admin/_panes/agents.html @@ -23,112 +23,6 @@ {% set analyze_capability = settings.document_action_capabilities.analyze %} {% set comparison_capability = settings.document_action_capabilities.comparison %} -
    -
    -
    -
    Document Action Capabilities
    -

    Configure the document actions shown in the Action dropdown in Chat and Workflow. These controls live in the Agents and Actions tab, but remain separate from the global agent and custom action cards below.

    -
    - Chat: 2-300 | Workflow: 2-1000 -
    - -
    -
    -
    -
    - - - -
    -
    -
    - - {{ analyze_capability.chat_max_documents }} -
    -
    -
    - -
    -
    - -
    -
    -
    - 2 - 300 -
    -
    -
    -
    - - {{ analyze_capability.workflow_max_documents }} -
    -
    -
    - -
    -
    - -
    -
    -
    - 2 - 1000 -
    -
    -
    -
    -
    -
    -
    - - - -
    -
    -
    - - {{ comparison_capability.chat_max_documents }} -
    -
    -
    - -
    -
    - -
    -
    -
    - 2 - 300 -
    -
    -
    -
    - - {{ comparison_capability.workflow_max_documents }} -
    -
    -
    - -
    -
    - -
    -
    -
    - 2 - 1000 -
    -
    -
    -
    -
    -
    - -
    Agents Configuration @@ -386,636 +280,4 @@
    {% endif %} - - -
    -
    - Global Actions Configuration -
    -

    - Configure custom actions and tools to extend functionality with integrations and specialized capabilities. -

    - - {% if not settings.enable_semantic_kernel %} -
    - Actions are not available while agents are disabled. Enable agents above to configure actions. -
    - {% else %} -
    -
    -
    Global Actions
    - -
    -

    Disable a global action to keep the configuration without exposing it to runtime action loading until it is re-enabled.

    - {% if settings.per_user_semantic_kernel %} - -
    -
    Workspace Action Feature Toggles
    -
    - - - - -
    -
    - - - - -
    -
    - {% endif %} - - -
    -
    Core Action Toggles
    -
    - - - -
    -
    - - - -
    -
    - - - -
    -
    - - - -
    -
    - - - -
    -
    - - - -
    -
    - - - -
    -
    -
    - Tabular Processing Action - -
    - Automatically enabled when Enhanced Citations is enabled -
    -
    - - - - - - - - - -
    Display NameDescriptionActions
    -
    - {% endif %} -
    - {% if mcp_ui_enabled %} -
    -
    -
    -
    - Inbound MCP Server -
    -

    - Configure the governed SimpleChat MCP endpoint used by external MCP clients. Personal tools use delegated user tokens by default; tool access remains deny-by-default until authentication, source, client, and governance policy all allow the request. -

    -
    -
    - Preview - -
    -
    - - - -
    -
    -
    -
    Runtime Gate
    -
    - - - -
    -
    - - -
    Default: DelegatedMcpServerAccess. VS Code and other user clients must present this delegated scope.
    -
    -
    - - -
    Default: InboundMCPUserAccess. Governance determines which users/groups can use tools after this Entra role and delegated scope pass.
    -
    -
    - - -
    Default: InboundMCPAppAccess. Reserved for future app-only MCP tools and still governed separately.
    -
    -
    -
    - -
    -
    -
    Endpoint Contract
    -
    -
    MCP endpoint
    -
    {{ inbound_mcp_resource_path }}
    -
    Protected resource metadata
    -
    {{ inbound_mcp_prm_path }}
    -
    Transport
    -
    Streamable HTTP JSON-RPC
    -
    Current tool surface
    -
    Personal delegated tools only.
    -
    Tool identity
    -
    Delegated user token required for personal data.
    -
    - -
    -
    - -
    -
    -
    -
    -
    Request Size & Throttling
    -
    Protect the inbound MCP endpoint from oversized payloads and noisy clients. Rate limits are enforced per caller and tool category across app instances.
    -
    -
    - - -
    -
    -
    -
    - - -
    Default: 65536. Range: 1 KB to 1 MB.
    -
    -
    - - -
    Default: 60. Applies to each throttle category.
    -
    -
    - - -
    Default: 120.
    -
    -
    - - -
    Default: 30.
    -
    -
    - - -
    Default: 10.
    -
    -
    - -
    -
    - -
    - - - -
    - -
    -
    -
    -
    -
    Allowed client app IDs
    -
    Required allowlist. Empty means no MCP clients can connect.
    -
    - -
    -
    - - - - - - - - - -
    Client app IDDescriptionActions
    -
    -
    -
    -
    - -
    -
    -
    - - -
    -
    - Off allows only the configured SimpleChat tenant. Turning this on auto-includes the SimpleChat tenant and lets admins add more tenants. -
    -
    -
    -
    Allowed tenant IDs
    - -
    -
    - - - - - - - - - -
    Tenant IDDescriptionActions
    -
    -
    -
    -
    -
    - -
    -
    -
    - - -
    -
    - Default on accepts any source signal at the runtime allowlist layer. Turn off to require explicit source values here. In both modes, admins must still create an inbound MCP source governance policy before tools are returned. -
    -
    - - -
    Default: X-SimpleChat-MCP-Source.
    -
    - - - -
    -
    -
    Allowed source IDs
    - -
    -
    - - - - - - - - - -
    Source valueDescriptionActions
    -
    -
    -
    -
    -
    -
    -
    - - - - {% endif %}
    diff --git a/application/single_app/templates/admin/_panes/chat-experience.html b/application/single_app/templates/admin/_panes/chat-experience.html index 37d8033a6..be9826832 100644 --- a/application/single_app/templates/admin/_panes/chat-experience.html +++ b/application/single_app/templates/admin/_panes/chat-experience.html @@ -97,6 +97,4 @@
    - -
    \ No newline at end of file diff --git a/application/single_app/templates/admin/_panes/classification.html b/application/single_app/templates/admin/_panes/classification.html index 425eeedcf..8a4dc6e11 100644 --- a/application/single_app/templates/admin/_panes/classification.html +++ b/application/single_app/templates/admin/_panes/classification.html @@ -42,6 +42,4 @@
    Classification Categories
    - -
    \ No newline at end of file diff --git a/application/single_app/templates/admin/_panes/cosmos.html b/application/single_app/templates/admin/_panes/cosmos.html index 33cf6b0f3..6163be982 100644 --- a/application/single_app/templates/admin/_panes/cosmos.html +++ b/application/single_app/templates/admin/_panes/cosmos.html @@ -1138,6 +1138,4 @@
    - -
    diff --git a/application/single_app/templates/admin/_panes/embeddings.html b/application/single_app/templates/admin/_panes/embeddings.html new file mode 100644 index 000000000..f05c7d66f --- /dev/null +++ b/application/single_app/templates/admin/_panes/embeddings.html @@ -0,0 +1,148 @@ + diff --git a/application/single_app/templates/admin/_panes/image-generation.html b/application/single_app/templates/admin/_panes/image-generation.html new file mode 100644 index 000000000..ee9293278 --- /dev/null +++ b/application/single_app/templates/admin/_panes/image-generation.html @@ -0,0 +1,165 @@ +
    +
    +
    + Image Generation Configuration +
    +

    + Configure image generation settings. Enable/disable, set endpoints, and choose a model. +

    + + +
    + + + +
    +
    + +
    + + + +
    + +
    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + +
    + + +
    +
    +
    +
    + +
    + + Save pending changes to settings before clicking Fetch Image Generation Models
    + + + + + + + + +
    + +
    +
    +
    + + +
    +
    +
    +
    +
    + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + +
    + + +
    +
    +
    +
    + + +
    +
    +
    diff --git a/application/single_app/templates/admin/_panes/inbound-mcp.html b/application/single_app/templates/admin/_panes/inbound-mcp.html new file mode 100644 index 000000000..3676ea641 --- /dev/null +++ b/application/single_app/templates/admin/_panes/inbound-mcp.html @@ -0,0 +1,535 @@ +
    + {% if mcp_ui_enabled %} +
    +
    +
    +
    + Inbound MCP Server +
    +

    + Configure the governed SimpleChat MCP endpoint used by external MCP clients. Personal tools use delegated user tokens by default; tool access remains deny-by-default until authentication, source, client, and governance policy all allow the request. +

    +
    +
    + Preview + +
    +
    + + + +
    +
    +
    +
    Runtime Gate
    +
    + + + +
    +
    + + +
    Default: DelegatedMcpServerAccess. VS Code and other user clients must present this delegated scope.
    +
    +
    + + +
    Default: InboundMCPUserAccess. Governance determines which users/groups can use tools after this Entra role and delegated scope pass.
    +
    +
    + + +
    Default: InboundMCPAppAccess. Reserved for future app-only MCP tools and still governed separately.
    +
    +
    +
    + +
    +
    +
    Endpoint Contract
    +
    +
    MCP endpoint
    +
    {{ inbound_mcp_resource_path }}
    +
    Protected resource metadata
    +
    {{ inbound_mcp_prm_path }}
    +
    Transport
    +
    Streamable HTTP JSON-RPC
    +
    Current tool surface
    +
    Personal delegated tools only.
    +
    Tool identity
    +
    Delegated user token required for personal data.
    +
    + +
    +
    + +
    +
    +
    +
    +
    Request Size & Throttling
    +
    Protect the inbound MCP endpoint from oversized payloads and noisy clients. Rate limits are enforced per caller and tool category across app instances.
    +
    +
    + + +
    +
    +
    +
    + + +
    Default: 65536. Range: 1 KB to 1 MB.
    +
    +
    + + +
    Default: 60. Applies to each throttle category.
    +
    +
    + + +
    Default: 120.
    +
    +
    + + +
    Default: 30.
    +
    +
    + + +
    Default: 10.
    +
    +
    + +
    +
    + +
    + + + +
    + +
    +
    +
    +
    +
    Allowed client app IDs
    +
    Required allowlist. Empty means no MCP clients can connect.
    +
    + +
    +
    + + + + + + + + + +
    Client app IDDescriptionActions
    +
    +
    +
    +
    + +
    +
    +
    + + +
    +
    + Off allows only the configured SimpleChat tenant. Turning this on auto-includes the SimpleChat tenant and lets admins add more tenants. +
    +
    +
    +
    Allowed tenant IDs
    + +
    +
    + + + + + + + + + +
    Tenant IDDescriptionActions
    +
    +
    +
    +
    +
    + +
    +
    +
    + + +
    +
    + Default on accepts any source signal at the runtime allowlist layer. Turn off to require explicit source values here. In both modes, admins must still create an inbound MCP source governance policy before tools are returned. +
    +
    + + +
    Default: X-SimpleChat-MCP-Source.
    +
    + + + +
    +
    +
    Allowed source IDs
    + +
    +
    + + + + + + + + + +
    Source valueDescriptionActions
    +
    +
    +
    +
    +
    +
    +
    + + + + {% endif %} +
    diff --git a/application/single_app/templates/admin/_panes/ai-models.html b/application/single_app/templates/admin/_panes/model-endpoints.html similarity index 53% rename from application/single_app/templates/admin/_panes/ai-models.html rename to application/single_app/templates/admin/_panes/model-endpoints.html index 98e6590d4..5c9683ad4 100644 --- a/application/single_app/templates/admin/_panes/ai-models.html +++ b/application/single_app/templates/admin/_panes/model-endpoints.html @@ -1,4 +1,4 @@ -
    +

    Configure all AI model settings including GPT for text generation, embeddings for semantic search, and image generation capabilities. @@ -235,396 +235,6 @@

    Global Endpoints
    {% endif %}
    - -
    -
    - Embeddings Configuration -
    -

    - Configure your embeddings settings. These are used for semantic search, knowledge-base lookups, etc. -

    - - -
    - - - -
    - -
    -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - -
    - - -
    -
    -
    -
    - -
    - Save pending changes to settings before clicking Fetch Embedding Models
    - - - - - - - - -
    - -
    -
    -
    - - -
    -
    -
    -
    -
    - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - -
    - - -
    -
    -
    - - -
    -
    - - -
    -
    - Image Generation Configuration -
    -

    - Configure image generation settings. Enable/disable, set endpoints, and choose a model. -

    - - -
    - - - -
    -
    - -
    - - - -
    - -
    -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - -
    - - -
    -
    -
    -
    - -
    - - Save pending changes to settings before clicking Fetch Image Generation Models
    - - - - - - - - -
    - -
    -
    -
    - - -
    -
    -
    -
    -
    - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - -
    - - -
    -
    -
    -
    - - -
    -
    - - - - {% include '_multiendpoint_modal.html' %} -
    + + {% include '_multiendpoint_modal.html' %}
    diff --git a/application/single_app/templates/admin/_panes/retention.html b/application/single_app/templates/admin/_panes/retention.html index a2bc06781..1e90230a4 100644 --- a/application/single_app/templates/admin/_panes/retention.html +++ b/application/single_app/templates/admin/_panes/retention.html @@ -265,6 +265,4 @@
    Default Retention PoliciesImportant: Deleted items respect your archiving settings. If conversation archiving is enabled, items will be archived before deletion. Activity logs are created for all deletions.
    - -
    \ No newline at end of file diff --git a/application/single_app/templates/admin_settings.html b/application/single_app/templates/admin_settings.html index 0d1d9e4b1..a34e450cf 100644 --- a/application/single_app/templates/admin_settings.html +++ b/application/single_app/templates/admin_settings.html @@ -953,6 +953,9 @@

    12. Enhanced Citations and Image Generation

    {% set group_is_first = loop.first %} {% for admin_tab in admin_group.tabs %} {% set is_default_tab = group_is_first and loop.first %} + {% if not admin_tab.condition + or (admin_tab.condition == 'enable_agent_template_gallery' and app_settings.enable_agent_template_gallery) + or (admin_tab.condition == 'mcp_ui_enabled' and mcp_ui_enabled) %}
    {% endif %} + {% endif %} {% endfor %} {% endfor %} @@ -987,6 +991,8 @@

    12. Enhanced Citations and Image Generation

    {% include "admin/_panes/agents.html" %} + {% include "admin/_panes/actions.html" %} + {% include "admin/_panes/inbound-mcp.html" %} @@ -1003,7 +1009,9 @@

    12. Enhanced Citations and Image Generation

    {% include "admin/_panes/custom-pages.html" %} - {% include "admin/_panes/ai-models.html" %} + {% include "admin/_panes/model-endpoints.html" %} + {% include "admin/_panes/embeddings.html" %} + {% include "admin/_panes/image-generation.html" %} {% include "admin/_panes/control-center-config.html" %} @@ -1088,6 +1096,83 @@
    diff --git a/application/single_app/templates/admin/_panes/session.html b/application/single_app/templates/admin/_panes/session.html index cb8fe9abe..d5bc9c36f 100644 --- a/application/single_app/templates/admin/_panes/session.html +++ b/application/single_app/templates/admin/_panes/session.html @@ -1,24 +1,11 @@
    -
    +
    - System Settings + Idle Session Timeout

    - System-level settings that control application behavior, including file size limits, conversation history, - and default prompts. + Warn inactive users and sign them out after a period of inactivity.

    -
    - - -
    -
    - - -
    Custom text shown at the top of the idle warning dialog.
    -
    - - -
    -
    - - Shown to signed-in users who lack the required roles. Use Enter for line breaks. - -
    -
    \ No newline at end of file +
    diff --git a/application/single_app/templates/admin_settings.html b/application/single_app/templates/admin_settings.html index 203d8a87b..f2daba2bc 100644 --- a/application/single_app/templates/admin_settings.html +++ b/application/single_app/templates/admin_settings.html @@ -2335,6 +2335,7 @@