diff --git a/application/v2_ui/src/lib/workspaceAgentAuthoring.ts b/application/v2_ui/src/lib/workspaceAgentAuthoring.ts
index dee772137..29088dc28 100644
--- a/application/v2_ui/src/lib/workspaceAgentAuthoring.ts
+++ b/application/v2_ui/src/lib/workspaceAgentAuthoring.ts
@@ -210,6 +210,8 @@ export interface AgentModelChoice {
key: string;
id: string;
endpointId: string;
+ /** The connection's display name, shown instead of its identifier. */
+ endpointName: string;
provider: string;
deployment: string;
modelName: string;
@@ -232,10 +234,11 @@ export function agentModelChoices(options: AgentEditorOptions): AgentModelChoice
key: JSON.stringify([endpoint.id, id]),
id,
endpointId: endpoint.id,
+ endpointName: agentText(endpoint.name),
provider: agentText(endpoint.provider) || 'aoai',
deployment,
modelName: agentText(model.modelName || model.name || id),
- label: `${agentText(model.displayName) || deployment} · ${endpoint.name || endpoint.id}`,
+ label: `${agentText(model.displayName) || deployment} · ${endpoint.name || 'Unnamed connection'}`,
apim: false,
});
}
@@ -244,7 +247,7 @@ export function agentModelChoices(options: AgentEditorOptions): AgentModelChoice
const settings = options.settings;
if (settings.enable_gpt_apim === true) {
return agentText(settings.azure_apim_gpt_deployment).split(',').map((item) => item.trim()).filter(Boolean).map((id) => ({
- key: JSON.stringify(['apim', id]), id, endpointId: '', provider: '',
+ key: JSON.stringify(['apim', id]), id, endpointId: '', endpointName: '', provider: '',
deployment: id, modelName: id, label: id, apim: true,
}));
}
@@ -254,7 +257,7 @@ export function agentModelChoices(options: AgentEditorOptions): AgentModelChoice
const id = agentText(model.id || model.deploymentName || model.deployment || model.modelName || model.name);
if (!id) continue;
choices.push({
- key: JSON.stringify(['legacy', id]), id, endpointId: '', provider: '',
+ key: JSON.stringify(['legacy', id]), id, endpointId: '', endpointName: '', provider: '',
deployment: agentText(model.deploymentName || model.deployment || id),
modelName: agentText(model.modelName || model.name || id),
label: agentText(model.display_name || model.displayName || model.deploymentName || model.deployment || id),
diff --git a/application/v2_ui/src/pages/workspace/AgentEditorPage.tsx b/application/v2_ui/src/pages/workspace/AgentEditorPage.tsx
index 77d26b593..0555d54ab 100644
--- a/application/v2_ui/src/pages/workspace/AgentEditorPage.tsx
+++ b/application/v2_ui/src/pages/workspace/AgentEditorPage.tsx
@@ -6,7 +6,7 @@ import { BookOpen, Cpu, IdCard, LayoutTemplate, Plug, ScrollText, SlidersHorizon
import { GlassButton } from '../../components/ui/primitives';
import { WorkspaceEditorFrame } from '../../components/workspace/WorkspaceEditorFrame';
import { SectionSkeleton } from '../../components/workspace/primitives';
-import { AgentIdentityFields } from '../../components/workspaceAgents/AgentIdentityFields';
+import { AgentIcon, AgentIdentityFields } from '../../components/workspaceAgents/AgentIdentityFields';
import { AgentModelFields } from '../../components/workspaceAgents/AgentModelFields';
import { AgentActionPicker } from '../../components/workspaceAgents/AgentActionPicker';
import { AgentKnowledgeFields } from '../../components/workspaceAgents/AgentKnowledgeFields';
@@ -201,7 +201,8 @@ function AgentEditorSession({ resourceId, scope, adapter }: { resourceId: string
return (
: undefined}
+ description={isNew ? 'Configure a reusable assistant. Changes are saved only when you choose Save agent.' : draft.description || undefined}
backTo={adapter.basePath} dirty={dirty || iconBusy} saving={saving} readOnly={readOnly} error={arraySecretError || saveError}
onSave={() => void save()} onDiscard={clear} saveLabel="Save agent" saveDisabled={Boolean(advancedError) || iconBusy}
initialSection={new URLSearchParams(location.search).get('templates') === '1' ? 'templates' : undefined}
diff --git a/docs/explanation/fixes/AGENT_EDITOR_INTERNAL_ID_EXPOSURE_FIX.md b/docs/explanation/fixes/AGENT_EDITOR_INTERNAL_ID_EXPOSURE_FIX.md
new file mode 100644
index 000000000..7abf10643
--- /dev/null
+++ b/docs/explanation/fixes/AGENT_EDITOR_INTERNAL_ID_EXPOSURE_FIX.md
@@ -0,0 +1,40 @@
+# Agent Editor Internal ID Exposure Fix
+
+Fixed in version: **0.261.277**
+
+## Issue
+
+The shared agent editor, which is used for personal, group and global agents, showed internal identifiers that mean nothing to users:
+
+- The header showed "Stable ID:
", and a generic icon appeared instead of the agent's own logo (for example the Microsoft 365 PNG shown on the agents list).
+- The Identity section repeated the stable ID.
+- Model & connection → Current selection showed the Endpoint ID, the Model ID and raw provider codes such as `aoai`.
+- The action picker showed `ID: ` on every action.
+- In Assigned knowledge, source workspaces showed ` · ` and documents showed ` · `. Unavailable references printed the raw key.
+
+## Root cause
+
+The editor was built as an authoring surface and rendered the stored references directly, because they were the only values guaranteed to exist.
+
+## Changes
+
+- `WorkspaceEditorFrame.tsx`: added an optional `iconNode` prop for the title tile.
+- `AgentEditorPage.tsx`: passes ``, the same component the agents list uses. The header subtitle is now the agent description instead of the stable ID.
+- `AgentIdentityFields.tsx`: removed the stable ID line.
+- `workspaceAgentAuthoring.ts`: `AgentModelChoice` now carries `endpointName`, and unnamed connections fall back to "Unnamed connection" instead of the endpoint ID.
+- `AgentModelFields.tsx`: Current selection now shows **Deployment**, **Model**, **Connection** and **Provider**:
+ - The provider shows its friendly label, such as "Azure OpenAI".
+ - Values that are GUIDs are never shown; a readable fallback appears instead.
+- `AgentActionPicker.tsx`: removed action IDs. Unresolved references show "Unavailable action [n]".
+- `AgentKnowledgeFields.tsx`:
+ - Workspaces show their type ("Personal workspace", "Group workspace" or "Public workspace").
+ - Documents show the workspace and file name, and keep their tags.
+ - Unavailable references use friendly text.
+ - IDs are no longer part of the search text.
+
+The stored data is unchanged. Only how it is presented changed.
+
+## Validation
+
+- `functional_tests/test_agent_editor_hides_internal_ids.py`
+- `npm run typecheck` in `application/v2_ui`
diff --git a/docs/explanation/release_notes.md b/docs/explanation/release_notes.md
index a5c0a6925..ae18e9a8c 100644
--- a/docs/explanation/release_notes.md
+++ b/docs/explanation/release_notes.md
@@ -2,6 +2,16 @@
For feature-focused and fix-focused drill-downs by version, see [Features by Version](https://github.com/microsoft/simplechat/tree/main/docs/explanation/features) and [Fixes by Version](https://github.com/microsoft/simplechat/tree/main/docs/explanation/fixes).
+### **(v0.261.277)**
+
+#### User Interface Enhancements
+
+* **Agent Editor No Longer Shows GUIDs**
+ * The agent editor for personal, group and global agents no longer shows internal identifiers. The header shows the agent's own icon, such as the Microsoft 365 logo, and its description instead of a stable ID.
+ * Model & connection › Current selection lists the deployment, model, connection name and provider (for example "Azure OpenAI") instead of endpoint and model IDs.
+ * Action choices, knowledge workspaces and documents no longer show IDs. Workspaces show their type, and documents show their workspace, file name and tags.
+ * (Ref: `AgentEditorPage.tsx`, `AgentModelFields.tsx`, `AgentActionPicker.tsx`, `AgentKnowledgeFields.tsx`, [Agent Editor Internal ID Exposure Fix](fixes/AGENT_EDITOR_INTERNAL_ID_EXPOSURE_FIX.md))
+
### **(v0.261.276)**
#### New Features
diff --git a/functional_tests/test_agent_editor_hides_internal_ids.py b/functional_tests/test_agent_editor_hides_internal_ids.py
new file mode 100644
index 000000000..d0fc60313
--- /dev/null
+++ b/functional_tests/test_agent_editor_hides_internal_ids.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python3
+# test_agent_editor_hides_internal_ids.py
+"""
+Functional test for hiding internal identifiers in the shared agent editor.
+Version: 0.261.277
+Implemented in: 0.261.277
+
+This test ensures that the shared v2 agent editor (personal, group and global agents)
+does not render GUIDs or internal IDs: no stable ID in the header or identity section,
+no endpoint/model IDs in Current selection, no action IDs in the action picker, and no
+workspace or document IDs in the knowledge fields. It also checks that the header shows
+the agent's own icon and that documents show their file name.
+"""
+
+import os
+import sys
+
+sys.path.append(os.path.dirname(os.path.abspath(__file__)))
+
+from test_support.versioning import assert_app_version_at_least
+
+REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+SRC = os.path.join(REPO_ROOT, "application", "v2_ui", "src")
+
+
+def read_source(*parts):
+ with open(os.path.join(SRC, *parts), encoding="utf-8") as handle:
+ return handle.read()
+
+
+def test_header_shows_agent_icon_without_stable_id():
+ """The editor header renders the agent icon and never the stable ID."""
+ page = read_source("pages", "workspace", "AgentEditorPage.tsx")
+ frame = read_source("components", "workspace", "WorkspaceEditorFrame.tsx")
+ identity = read_source("components", "workspaceAgents", "AgentIdentityFields.tsx")
+ assert "Model ID<", "'Model ID'"):
+ assert removed not in model, removed
+ for label in ("'Deployment'", "'Model'", "'Connection'", "'Provider'"):
+ assert label in model, label
+ assert "PROVIDER_OPTIONS" in model
+ assert "GUID_PATTERN" in model
+ return True
+
+
+def test_action_picker_hides_action_ids():
+ """Action choices and unavailable references never render IDs."""
+ picker = read_source("components", "workspaceAgents", "AgentActionPicker.tsx")
+ assert "ID: {action.id}" not in picker
+ assert "Unavailable action" in picker
+ return True
+
+
+def test_knowledge_fields_hide_ids_and_show_file_names():
+ """Workspaces show a friendly type, documents show file names, and no IDs render."""
+ knowledge = read_source("components", "workspaceAgents", "AgentKnowledgeFields.tsx")
+ assert "{source.scope} · {source.id}" not in knowledge
+ assert "{document.source_name} · {document.id}" not in knowledge
+ assert "Unavailable document: {id}" not in knowledge
+ assert "Saved source unavailable here: {key}" not in knowledge
+ assert "${document.id}`.toLowerCase()" not in knowledge
+ assert "sourceTypeLabel" in knowledge
+ assert "document.file_name" in knowledge
+ return True
+
+
+def test_version_floor():
+ assert_app_version_at_least("0.261.277")
+ return True
+
+
+if __name__ == "__main__":
+ tests = [
+ test_header_shows_agent_icon_without_stable_id,
+ test_current_selection_uses_readable_names,
+ test_action_picker_hides_action_ids,
+ test_knowledge_fields_hide_ids_and_show_file_names,
+ test_version_floor,
+ ]
+ results = []
+ for test in tests:
+ print(f"Running {test.__name__}...")
+ try:
+ results.append(test())
+ except AssertionError as error:
+ print(f"Failed: {test.__name__}: {error}")
+ results.append(False)
+ print(f"Results: {sum(bool(r) for r in results)}/{len(results)} tests passed")
+ sys.exit(0 if all(results) else 1)