"""The built-in agents: Claude Code's Explore, Plan and general-purpose with their tool sets, main's agent types from every described agent.""" from __future__ import annotations from typing import Any from noeta.agents import builtin_agent_files, default_agents from noeta.agents.agent_files import parse_agent_file from noeta.core.agent import Agent from noeta.core.extension import TaskView from noeta.core.loop.state import TaskState from noeta.plugins.mcp import Mcp from noeta.plugins.subtasks import Subtasks def _agents(**kw: Any) -> dict[str, Agent]: return {a.name: a for a in default_agents("m", **kw)} def _tools(agent: Agent, agents: dict[str, Agent], *, child: bool = True) -> list[str]: state = TaskState(agent=agent.name, parent_id="p" if child else None, config={"subagent_type": agent.name} if child else {}) view = TaskView(state=state, agents=agents) names = [t.name for e in agent.all_extensions() for t in e.tools(view)] return [n for n in names if (agent.allowed_tools is None or n in agent.allowed_tools) and n not in agent.denied_tools] def test_built_in_agent_files() -> None: names = [f.name for f in builtin_agent_files()] assert names == ["main", "Explore", "general-purpose", "Plan"] def test_tool_sets() -> None: agents = _agents() gp = _tools(agents["general-purpose"], agents) assert {"Agent", "Bash", "Edit", "Read", "Write", "SendMessage"} <= set(gp) assert gp[-1] == "SubagentHandback" and "ListAgents" not in gp for name in ("Explore", "Plan"): tools = _tools(agents[name], agents) assert {"Bash", "Read", "SendMessage"} <= set(tools) assert not {"Edit", "Write", "Agent", "AskUserQuestion", "ListAgents"} & set(tools) assert tools[-1] == "SubagentHandback" assert agents[name].denied_tools == ("Agent", "ExitPlanMode", "Edit", "Write", "NotebookEdit") main = _tools(agents["main"], agents, child=False) assert {"Agent", "ListAgents", "SendMessage"} <= set(main) assert "SubagentHandback" not in main def test_main_agent_types_come_from_described_agents() -> None: agents = _agents() [sub] = [e for e in agents["main"].extensions if isinstance(e, Subtasks)] roster = sub.roster_for(TaskView(state=TaskState(agent="main"), agents=agents)) assert sorted(roster) == ["Explore", "Plan", "general-purpose"] assert roster["Explore"].tools == ("All tools except Agent, ExitPlanMode, Edit, Write, " "NotebookEdit") assert roster["general-purpose"].tools == "*" assert roster["Explore"].description.startswith("Read-only search agent") def test_extension_sets_and_sharing() -> None: agents = _agents(plugins={"mcp": Mcp()}) names = {n: {e.name for e in a.all_extensions()} for n, a in agents.items()} common = {"compaction", "permissions", "shell", "web", "environment", "skills", "mcp"} assert common | {"instructions"} <= names["general-purpose"] for name in ("Explore", "Plan"): assert common <= names[name] # Claude Code's omitClaudeMd: only the machine's managed files. [instructions] = [e for e in agents[name].all_extensions() if e.name == "instructions"] assert getattr(instructions, "managed_only", False) assert "memory" not in names["main"] # memory is off until set shells = {id(e) for a in agents.values() for e in a.extensions if e.name == "shell"} assert len(shells) == 1 def test_main_kw_and_extra_files() -> None: reviewer = parse_agent_file( "---\nname: reviewer\ndescription: Reviews diffs.\ntools: Read, Grep\n---\nReview.\n") agents = {a.name: a for a in default_agents("m", extra_files=[reviewer], system_prompt="Short.\n")} assert agents["main"].system_prompt == "Short.\n" r = agents["reviewer"] assert r.system_prompt == "Review.\n" and r.allowed_tools == ("Read", "Grep") assert {e.name for e in r.extensions} >= {"fs", "shell", "subtasks"} [sub] = [e for e in agents["main"].extensions if isinstance(e, Subtasks)] roster = sub.roster_for(TaskView(state=TaskState(agent="main"), agents=agents)) assert roster["reviewer"].tools == "Read, Grep" def test_explore_runs_on_opus_under_a_fable_caller() -> None: """Claude Code's Explore ``inheritCap: "opus"``: a caller on a family above Opus (Fable) gets its Explore on Opus; only the built-in file.""" from noeta.plugins.subtasks.roster import MODEL_CAP_KEY, capped_model agents = _agents() assert agents["Explore"].extra["inheritCap"] == "opus" assert "inheritCap" not in agents["Plan"].extra assert capped_model("claude-fable-5-1", "opus") == "claude-opus-5-5" assert capped_model("fable", "opus") == "claude-opus-5-5" for model in ("claude-opus-5-5", "claude-sonnet-5-5", "anthropic.claude-fable-5-1", "gpt-5", ""): assert capped_model(model, "opus") is None sub = Subtasks() state = TaskState(agent="main", config={"model": "claude-fable-5-1"}) ctx: Any = None capped = sub.child_config(state, ctx, "Explore", {MODEL_CAP_KEY: "opus"}, "Explore", {}, fork=False) assert capped["model"] == "claude-opus-5-5" and MODEL_CAP_KEY not in capped asked = sub.child_config(state, ctx, "Explore", {MODEL_CAP_KEY: "opus"}, "Explore", {"model": "haiku"}, fork=False) assert asked["model"] == "claude-haiku-5-5" plain = sub.child_config(TaskState(agent="main"), ctx, "Explore", {MODEL_CAP_KEY: "opus"}, "Explore", {}, fork=False, parent_model="claude-fable-5-1") assert plain["model"] == "claude-opus-5-5" user_file = parse_agent_file("---\nname: Explore\ndescription: mine\n---\nlook") explores = [a for a in default_agents("m", extra_files=[user_file]) if a.name == "Explore"] assert explores and all(a.description != "mine" or "inheritCap" not in a.extra for a in explores) assert any(a.description == "mine" for a in explores) def test_the_plan_agents_prompt_looks_for_similar_features_and_ends_read_only() -> None: """Claude Code's Plan prompt: the exploring step looks for similar features to use as a reference, and a closing reminder after the report format repeats that it only explores and plans.""" prompt = str(_agents()["Plan"].system_prompt) explore = next(line for line in prompt.splitlines() if line.startswith("2. Explore")) assert "similar features" in explore critical = prompt.index("3-5 files most critical") closing = prompt.index("Remember: you only explore and plan.") assert critical < closing assert "must not write, edit or change any file" in prompt[closing:]