mirror of
https://github.com/agent0ai/agent-zero.git
synced 2026-08-15 19:35:00 +00:00
Hide the utility agent from profile selectors
Centralize omission of the internal default profile in the shared presentation catalog and keep status reporting intact for existing chats. Align Settings, model-switcher, Connector, Telegram, and selector contracts with focused regressions.
This commit is contained in:
parent
2a2890d892
commit
9dab4f2460
12 changed files with 71 additions and 14 deletions
|
|
@ -20,6 +20,8 @@
|
|||
- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
|
||||
- `Agents` is an `ApiHandler`.
|
||||
- `Agents` defines `process(...)`.
|
||||
- The response uses the shared `subagents.get_all_agents_list()` presentation
|
||||
catalog; profile visibility rules belong to that helper.
|
||||
- Imported dependency areas include: `helpers`, `helpers.api`.
|
||||
|
||||
## Key Concepts
|
||||
|
|
|
|||
|
|
@ -20,7 +20,9 @@
|
|||
- Update this file whenever public functions, classes, persistence behavior, path/security assumptions, side effects, or cross-module contracts change.
|
||||
- Observed side-effect areas: filesystem writes, model calls, plugin state, settings/state persistence.
|
||||
- Imported dependency areas include: `__future__`, `helpers`, `helpers.persist_chat`, `helpers.state_monitor_integration`, `plugins._model_config.helpers`, `re`, `typing`.
|
||||
- `/agent` switches the top-level chat profile and preserves existing subordinate agent profiles.
|
||||
- `/agent` switches the top-level chat profile and preserves existing
|
||||
subordinate agent profiles. Choices come from the shared presentation
|
||||
catalog, though status may report an existing chat using the utility profile.
|
||||
- `/model <preset>` stores a per-chat global preset reference, `/model inherit` clears it, and status always reports the effective scoped-or-chat preset. `Default` is a real selectable preset, not an alias for clearing the chat selection.
|
||||
|
||||
## Key Concepts
|
||||
|
|
|
|||
|
|
@ -263,6 +263,7 @@ def convert_out(settings: Settings) -> SettingsOutput:
|
|||
for key, item in sorted(
|
||||
subagents.get_available_agents_dict(None).items()
|
||||
)
|
||||
if key != "default"
|
||||
],
|
||||
knowledge_subdirs=[{"value": subdir, "label": subdir}
|
||||
for subdir in files.get_subdirectories("knowledge", exclude="default")],
|
||||
|
|
@ -288,7 +289,7 @@ def convert_out(settings: Settings) -> SettingsOutput:
|
|||
}
|
||||
|
||||
current_profile = current.get("agent_profile")
|
||||
if current_profile and not any(
|
||||
if current_profile and current_profile != "default" and not any(
|
||||
option["value"] == current_profile
|
||||
for option in additional["agent_subdirs"]
|
||||
):
|
||||
|
|
|
|||
|
|
@ -69,7 +69,8 @@
|
|||
- `ui_control_visibility` stores validated mobile and desktop visibility flags for the project selector, clock, connection status, and right canvas rail; missing or malformed values fall back per device.
|
||||
- The Global default-profile selector lists only globally available profiles.
|
||||
A currently configured unavailable profile remains visible with an explicit
|
||||
unavailable label so settings can round-trip it truthfully.
|
||||
unavailable label so settings can round-trip it truthfully, except that the
|
||||
exact `default` utility profile is never offered as a selectable option.
|
||||
- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
|
||||
|
||||
## Work Guidance
|
||||
|
|
|
|||
|
|
@ -300,11 +300,11 @@ def get_all_agents_list() -> list[dict[str, str]]:
|
|||
else:
|
||||
merged[name] = item
|
||||
|
||||
result: list[dict[str, str]] = []
|
||||
for key in sorted(merged.keys()):
|
||||
item = merged[key]
|
||||
result.append({"key": key, "label": item.title or key})
|
||||
return result
|
||||
return [
|
||||
{"key": key, "label": item.title or key}
|
||||
for key, item in sorted(merged.items())
|
||||
if key != "default"
|
||||
]
|
||||
|
||||
|
||||
def get_default_promp_file_names() -> list[str]:
|
||||
|
|
|
|||
|
|
@ -51,6 +51,8 @@
|
|||
title fallbacks do not become authored overrides.
|
||||
- Available-profile resolution includes definitions from the selected project,
|
||||
then applies that project's sparse `agents.json` availability overrides.
|
||||
- `get_all_agents_list()` is the shared presentation catalog and omits the exact
|
||||
`default` utility profile. Runtime discovery and loading remain unchanged.
|
||||
- Bundled directories require an authored profile definition; the `_example`
|
||||
reference directory is never selectable. Every real profile, including
|
||||
`Default`, follows the same Global and project availability rules.
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@
|
|||
- Model preset definitions exposed through v1 are global; project arguments select scope but never create project-owned definitions. Model switcher state reports the effective main, utility, and embedding models and preserves embedding-change notifications.
|
||||
- The protected v1 `agent_editor` route delegates to the bundled Agent Editor
|
||||
API and must not define another profile schema or write profile files itself.
|
||||
- The protected v1 `agents_list` response uses the shared agent presentation
|
||||
catalog rather than applying connector-specific visibility rules.
|
||||
- Computer Use receipts describe transport success unless the connector returns explicit effect evidence. Linux target-bound typing requires a verified active/focused `window_id`; window activation uses focus, never a press action on an application or window node. Do not retry an identical failed Computer Use call.
|
||||
|
||||
## Work Guidance
|
||||
|
|
|
|||
|
|
@ -28,7 +28,11 @@
|
|||
- `modelConfig.createPresetEditor()` owns local preset drafts, row actions, and stable UI-only row keys so deletion or renaming cannot rebind nested model fields.
|
||||
- The preset editor maps each model provider's API-key field to the shared API-key store; saving the editor persists dirty keys separately and never writes secrets into preset YAML.
|
||||
- The compact chat selector label combines the effective preset with only the leaf name of its main model; utility and provider text stay out of the closed selector.
|
||||
- The adjacent agent-profile selector reads the always-enabled Agent Editor list endpoint directly so the active profile shows its effective title and avatar, and omits profiles disabled in the chat's current scope.
|
||||
- The adjacent agent-profile selector reads the always-enabled Agent Editor list
|
||||
endpoint directly so the active profile shows its effective title and avatar,
|
||||
and omits profiles disabled in the chat's current scope plus the exact
|
||||
`default` utility profile. A chat already using `default` may still show that
|
||||
current status without adding a selectable or editable row.
|
||||
- Reload the agent-profile selector catalog when a chat changes project or
|
||||
active profile so project-only profiles never linger in the visible choices.
|
||||
- When forced agent-profile catalog loads overlap, only the newest request may
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ export const switcherMethods = {
|
|||
});
|
||||
if (requestSeq !== this.agentProfilesLoadSeq) return this.agentProfiles;
|
||||
this.agentProfiles = (data.profiles || [])
|
||||
.filter(profile => profile.id && profile.id !== "_example" && profile.enabled !== false)
|
||||
.filter(profile => profile.id && !["_example", "default"].includes(profile.id) && profile.enabled !== false)
|
||||
.map(profile => ({
|
||||
key: profile.id,
|
||||
label: profile.title || profile.id,
|
||||
|
|
@ -127,7 +127,7 @@ export const switcherMethods = {
|
|||
|
||||
getAgentProfileList(activeKey = "", activeLabel = "") {
|
||||
const profiles = [...(this.agentProfiles || [])];
|
||||
if (activeKey && !profiles.some(profile => profile.key === activeKey)) {
|
||||
if (activeKey && activeKey !== "default" && !profiles.some(profile => profile.key === activeKey)) {
|
||||
profiles.unshift({ key: activeKey, label: activeLabel || activeKey });
|
||||
}
|
||||
return profiles;
|
||||
|
|
|
|||
|
|
@ -16,7 +16,10 @@
|
|||
- Treat bot tokens, chat IDs, attachments, and user data as sensitive.
|
||||
- Keep allowed-user, group-mode, project, model, and `/send` controls enforced.
|
||||
- Install Telegram dependencies into the framework runtime only when required.
|
||||
- Agent profile picker actions change the top-level chat profile and must preserve existing subordinate agent profiles.
|
||||
- Agent profile picker actions change the top-level chat profile and must
|
||||
preserve existing subordinate agent profiles. Picker rows and direct matches
|
||||
use the shared presentation catalog while current status may still report an
|
||||
existing chat that uses the utility profile.
|
||||
- Model picker status shows the effective preset; clearing a chat override returns to its scoped preset rather than assuming `Default`.
|
||||
|
||||
## Work Guidance
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from io import BytesIO
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
|
@ -1082,7 +1083,10 @@ def test_settings_default_profile_catalog_uses_global_availability(
|
|||
lambda _project: {
|
||||
"default": settings.subagents.SubAgentListItem(
|
||||
name="default", title="Default"
|
||||
)
|
||||
),
|
||||
"agent0": settings.subagents.SubAgentListItem(
|
||||
name="agent0", title="Agent 0"
|
||||
),
|
||||
},
|
||||
)
|
||||
configured = settings.get_default_settings().copy()
|
||||
|
|
@ -1091,7 +1095,7 @@ def test_settings_default_profile_catalog_uses_global_availability(
|
|||
options = settings.convert_out(configured)["additional"]["agent_subdirs"]
|
||||
|
||||
assert options == [
|
||||
{"value": "default", "label": "Default"},
|
||||
{"value": "agent0", "label": "Agent 0"},
|
||||
{
|
||||
"value": "disabled-profile",
|
||||
"label": "disabled-profile (unavailable)",
|
||||
|
|
@ -1099,6 +1103,30 @@ def test_settings_default_profile_catalog_uses_global_availability(
|
|||
]
|
||||
|
||||
|
||||
def test_shared_profile_catalog_drives_generic_and_web_selectors(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
from api import agents
|
||||
from helpers import integration_commands, subagents
|
||||
|
||||
profiles = [{"key": "agent0", "label": "Agent 0"}]
|
||||
monkeypatch.setattr(subagents, "get_all_agents_list", lambda: profiles)
|
||||
|
||||
handler = agents.Agents.__new__(agents.Agents)
|
||||
response = asyncio.run(handler.process({"action": "list"}, None))
|
||||
assert [item["key"] for item in response["data"]] == ["agent0"]
|
||||
|
||||
context = SimpleNamespace(
|
||||
agent0=SimpleNamespace(config=SimpleNamespace(profile="default")),
|
||||
is_running=lambda: False,
|
||||
)
|
||||
status = integration_commands._handle_agent(context, "")
|
||||
assert "Current agent: default" in status
|
||||
assert "Agent 0 (agent0)" in status
|
||||
assert "Default (default)" not in status
|
||||
assert "was not found" in integration_commands._handle_agent(context, "default")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("relative_path", "patch", "label"),
|
||||
(
|
||||
|
|
|
|||
|
|
@ -132,3 +132,15 @@ def test_available_agents_include_project_profiles_and_project_overrides(
|
|||
|
||||
assert requested == ["demo"]
|
||||
assert list(available) == ["project-only"]
|
||||
|
||||
|
||||
def test_all_agents_list_omits_default_utility_profile(
|
||||
tmp_path: Path, monkeypatch
|
||||
) -> None:
|
||||
root = tmp_path / "agents"
|
||||
_write_profile(root, "default", "title: Default\n")
|
||||
_write_profile(root, "agent0", "title: Agent 0\n")
|
||||
monkeypatch.setattr(subagents, "get_agents_roots", lambda: [str(root)])
|
||||
|
||||
assert "default" in subagents._get_agents_list_from_dir(str(root), "default")
|
||||
assert subagents.get_all_agents_list() == [{"key": "agent0", "label": "Agent 0"}]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue