From 38dde6b3df5576d1b992c6cf31ef608512f0ccda Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Sun, 28 Jun 2026 11:01:07 +0200 Subject: [PATCH] [chore] Rename agent config to agent template Rename the agent authoring type from agent_config / AgentConfig to agent template, naming-layer only, mirroring the skill-template rename and the prompt-template precedent. No behavior change. - AgentConfig -> AgentTemplate, AgentConfigSchema -> AgentTemplateSchema - harness-config family: Pi/Harness/Claude/Agenta/Resolved AgentConfig -> ...AgentTemplate - catalog __ag_type__ / x-ag-type-ref "agent_config" -> "agent-template" - web AgentConfigControl -> AgentTemplateControl (+ layout, props, atoms) - services AGENT_CONFIG_SCHEMA / _DEFAULT_AGENT_CONFIG -> AGENT_TEMPLATE_* - env var AGENTA_AGENT_CONFIG_DIR -> AGENTA_AGENT_TEMPLATE_DIR (static on-file template dir) Kept: AgentaClientConfig, AgentaConfig (env), AgentRunnerConfigurationError. Co-Authored-By: Claude Opus 4.8 --- sdks/python/agenta/__init__.py | 2 +- sdks/python/agenta/sdk/agents/__init__.py | 24 +++---- .../sdk/agents/adapters/agenta_builtins.py | 2 +- .../agenta/sdk/agents/adapters/harnesses.py | 20 +++--- .../agenta/sdk/agents/adapters/local.py | 4 +- .../sdk/agents/adapters/sandbox_agent.py | 6 +- sdks/python/agenta/sdk/agents/dtos.py | 50 +++++++-------- sdks/python/agenta/sdk/agents/interfaces.py | 8 +-- sdks/python/agenta/sdk/agents/utils/wire.py | 4 +- sdks/python/agenta/sdk/agents/wire_models.py | 4 +- .../agenta/sdk/engines/running/interfaces.py | 6 +- sdks/python/agenta/sdk/utils/types.py | 24 +++---- .../agents/_fake_runner_backend.py | 6 +- .../agents/test_transport_roundtrip.py | 12 ++-- .../agents/connections/test_dtos_model_ref.py | 26 ++++---- .../unit/agents/skills/test_skills_e2e.py | 22 +++---- ..._config.py => test_dtos_agent_template.py} | 58 ++++++++--------- .../unit/agents/test_dtos_harness_configs.py | 32 +++++----- .../unit/agents/test_environment_lifecycle.py | 4 +- .../unit/agents/test_harness_adapters.py | 34 +++++----- .../unit/agents/test_harness_identity.py | 10 +-- .../pytest/unit/agents/test_wire_contract.py | 42 ++++++------- .../pytest/unit/agents/test_wire_models.py | 2 +- .../pytest/unit/test_skill_config_catalog.py | 62 +++++++++---------- services/oss/src/agent/app.py | 42 +++++++------ services/oss/src/agent/config.py | 14 ++--- services/oss/src/agent/schemas.py | 16 ++--- .../oss/tests/pytest/unit/agent/conftest.py | 2 +- ...nfig.py => test_default_agent_template.py} | 18 +++--- .../unit/agent/test_inspect_catalog_refs.py | 6 +- .../pytest/unit/agent/test_invoke_handler.py | 14 +++-- .../pytest/unit/agent/test_select_backend.py | 4 +- .../AgentChatSlice/assets/agConfig.ts | 6 +- .../PlaygroundVariantHeaderMenu/index.tsx | 8 +-- ...igControl.tsx => AgentTemplateControl.tsx} | 18 +++--- .../ClaudePermissionsControl.tsx | 2 +- .../SchemaControls/HarnessSelectControl.tsx | 2 +- .../SchemaControls/McpServerItemControl.tsx | 2 +- .../SandboxPermissionControl.tsx | 2 +- .../SchemaControls/SchemaPropertyRenderer.tsx | 12 ++-- ...ConfigLayout.ts => agentTemplateLayout.ts} | 8 +-- .../SchemaControls/connectionUtils.ts | 4 +- .../src/DrillInView/SchemaControls/index.ts | 8 +-- .../agenta-entity-ui/src/DrillInView/index.ts | 4 +- .../src/state/execution/agentRequest.ts | 2 +- 45 files changed, 335 insertions(+), 323 deletions(-) rename sdks/python/oss/tests/pytest/unit/agents/{test_dtos_agent_config.py => test_dtos_agent_template.py} (78%) rename services/oss/tests/pytest/unit/agent/{test_default_agent_config.py => test_default_agent_template.py} (84%) rename web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/{AgentConfigControl.tsx => AgentTemplateControl.tsx} (99%) rename web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/{agentConfigLayout.ts => agentTemplateLayout.ts} (66%) diff --git a/sdks/python/agenta/__init__.py b/sdks/python/agenta/__init__.py index b051d103a3..e8f3cf9754 100644 --- a/sdks/python/agenta/__init__.py +++ b/sdks/python/agenta/__init__.py @@ -57,7 +57,7 @@ # `agenta.sdk.agents` when needed. from .sdk.agents import ( # noqa: F401 AgentaHarness, - AgentConfig, + AgentTemplate, ClaudeHarness, Environment, LocalBackend, diff --git a/sdks/python/agenta/sdk/agents/__init__.py b/sdks/python/agenta/sdk/agents/__init__.py index da405ed6d6..3cc6106f51 100644 --- a/sdks/python/agenta/sdk/agents/__init__.py +++ b/sdks/python/agenta/sdk/agents/__init__.py @@ -2,7 +2,7 @@ Layers (Agenta's hexagonal vocabulary): -- ``dtos.py`` — data contracts (``AgentConfig``, ``SessionConfig``, ``Message``, ...). +- ``dtos.py`` — data contracts (``AgentTemplate``, ``SessionConfig``, ``Message``, ...). - ``interfaces.py`` — the ports (ABCs): ``Backend``, ``Environment``, ``Sandbox``, ``Session``, ``Harness``. - ``adapters/`` — implementations: ``SandboxAgentBackend`` / ``LocalBackend`` @@ -15,7 +15,7 @@ from agenta.sdk.agents import Message cfg = ag.ConfigManager.get_from_registry(app_slug="my-agent") - agent = ag.AgentConfig.from_params(cfg) + agent = ag.AgentTemplate.from_params(cfg) harness = ag.PiHarness(ag.Environment(ag.SandboxAgentBackend())) result = await harness.prompt(ag.SessionConfig(agent=agent), [Message(role="user", content="hi")]) """ @@ -53,21 +53,21 @@ UnsupportedProviderError, ) from .dtos import ( - AgentaAgentConfig, - AgentConfig, + AgentaAgentTemplate, + AgentTemplate, Event, AgentResult, - ClaudeAgentConfig, + ClaudeAgentTemplate, ContentBlock, HARNESS_IDENTITIES, - HarnessAgentConfig, + HarnessAgentTemplate, HarnessCapabilities, HarnessIdentity, HarnessType, Message, NetworkEgress, PermissionPolicy, - PiAgentConfig, + PiAgentTemplate, RunContext, RunContextReference, RunContextTrace, @@ -150,12 +150,12 @@ __all__ = [ # DTOs - "AgentConfig", + "AgentTemplate", "SessionConfig", - "HarnessAgentConfig", - "PiAgentConfig", - "ClaudeAgentConfig", - "AgentaAgentConfig", + "HarnessAgentTemplate", + "PiAgentTemplate", + "ClaudeAgentTemplate", + "AgentaAgentTemplate", "HarnessType", "HarnessIdentity", "HARNESS_IDENTITIES", diff --git a/sdks/python/agenta/sdk/agents/adapters/agenta_builtins.py b/sdks/python/agenta/sdk/agents/adapters/agenta_builtins.py index ca628f6619..d654bafdbb 100644 --- a/sdks/python/agenta/sdk/agents/adapters/agenta_builtins.py +++ b/sdks/python/agenta/sdk/agents/adapters/agenta_builtins.py @@ -17,7 +17,7 @@ layer); the server-side ``StaticWorkflowCatalog`` imports the same constant so the embed path and the forced path stay one source of truth. -Two layers, kept distinct on purpose (matching Pi's own split, see :class:`PiAgentConfig`): +Two layers, kept distinct on purpose (matching Pi's own split, see :class:`PiAgentTemplate`): the *persona* is an ``append_system`` (changes Pi's base prompt), while *project conventions* belong in ``AGENTS.md``. ``AGENTA_PREAMBLE`` is the AGENTS.md layer; ``AGENTA_FORCED_APPEND_SYSTEM`` is the persona layer. diff --git a/sdks/python/agenta/sdk/agents/adapters/harnesses.py b/sdks/python/agenta/sdk/agents/adapters/harnesses.py index 93922f4957..4452f6765b 100644 --- a/sdks/python/agenta/sdk/agents/adapters/harnesses.py +++ b/sdks/python/agenta/sdk/agents/adapters/harnesses.py @@ -24,10 +24,10 @@ from agenta.sdk.utils.logging import get_module_logger from ..dtos import ( - AgentaAgentConfig, - ClaudeAgentConfig, + AgentaAgentTemplate, + ClaudeAgentTemplate, HarnessType, - PiAgentConfig, + PiAgentTemplate, SessionConfig, ) from ..interfaces import Environment, Harness @@ -58,14 +58,14 @@ def _normalize_tool_specs(specs: List[Dict[str, Any]]) -> List[ToolSpec]: class PiHarness(Harness): harness_type = HarnessType.PI - def _to_harness_config(self, config: SessionConfig) -> PiAgentConfig: + def _to_harness_config(self, config: SessionConfig) -> PiAgentTemplate: # Pi delivers tools natively: built-in names plus resolved specs registered through # the Pi extension. Pi does not gate tool use, so the permission policy is dropped. # Pi reads its own slice of the neutral harness_kwargs bag (the `pi_core` key, shared # by both Pi-family harnesses): `system` replaces Pi's base prompt, `append_system` # extends it (both leave AGENTS.md untouched). pi_options = config.agent.harness_kwargs.get(HarnessType.PI.value, {}) - return PiAgentConfig( + return PiAgentTemplate( agents_md=config.agent.instructions, model=config.agent.model, resolved_connection=config.resolved_connection, @@ -84,7 +84,7 @@ def _to_harness_config(self, config: SessionConfig) -> PiAgentConfig: class ClaudeHarness(Harness): harness_type = HarnessType.CLAUDE - def _to_harness_config(self, config: SessionConfig) -> ClaudeAgentConfig: + def _to_harness_config(self, config: SessionConfig) -> ClaudeAgentTemplate: # Claude has no Pi built-in tools; drop them rather than ship a name Claude cannot # honor. Tools go over MCP, and Claude gates tool use, so the permission policy is # carried through. @@ -96,11 +96,11 @@ def _to_harness_config(self, config: SessionConfig) -> ClaudeAgentConfig: # Skills stay on the harness config; the runner materializes them under `.claude/skills` # in the session cwd so Claude ACP can load the same resolved inline packages. # The whole neutral harness_kwargs bag (plus sandbox_permission + mcp_servers) is threaded - # onto the ClaudeAgentConfig; the config's `wire_harness_files` (the Python claude adapter) + # onto the ClaudeAgentTemplate; the config's `wire_harness_files` (the Python claude adapter) # parses the `claude.permissions` slice and renders `.claude/settings.json` as a generic # `harnessFiles` entry. No claude-specific parsing happens here; the runner just writes the # files into the cwd. - return ClaudeAgentConfig( + return ClaudeAgentTemplate( agents_md=config.agent.instructions, model=config.agent.model, resolved_connection=config.resolved_connection, @@ -125,11 +125,11 @@ class AgentaHarness(Harness): harness_type = HarnessType.AGENTA - def _to_harness_config(self, config: SessionConfig) -> AgentaAgentConfig: + def _to_harness_config(self, config: SessionConfig) -> AgentaAgentTemplate: # The author's Pi options still apply; the pi_agenta harness reads the same `pi_core` # slice as PiHarness (it drives Pi) and layers its forced extras on top. pi_options = config.agent.harness_kwargs.get(HarnessType.PI.value, {}) - return AgentaAgentConfig( + return AgentaAgentTemplate( agents_md=compose_instructions(config.agent.instructions), model=config.agent.model, resolved_connection=config.resolved_connection, diff --git a/sdks/python/agenta/sdk/agents/adapters/local.py b/sdks/python/agenta/sdk/agents/adapters/local.py index 0883304578..d497195469 100644 --- a/sdks/python/agenta/sdk/agents/adapters/local.py +++ b/sdks/python/agenta/sdk/agents/adapters/local.py @@ -22,7 +22,7 @@ from typing import Mapping, Optional -from ..dtos import HarnessAgentConfig, HarnessType, RunContext, TraceContext +from ..dtos import HarnessAgentTemplate, HarnessType, RunContext, TraceContext from ..interfaces import Backend, Sandbox, Session @@ -40,7 +40,7 @@ async def create_sandbox(self) -> Sandbox: async def create_session( self, sandbox: Sandbox, - config: HarnessAgentConfig, + config: HarnessAgentTemplate, *, harness: HarnessType, secrets: Optional[Mapping[str, str]] = None, diff --git a/sdks/python/agenta/sdk/agents/adapters/sandbox_agent.py b/sdks/python/agenta/sdk/agents/adapters/sandbox_agent.py index aca2b6f560..7a1dea314d 100644 --- a/sdks/python/agenta/sdk/agents/adapters/sandbox_agent.py +++ b/sdks/python/agenta/sdk/agents/adapters/sandbox_agent.py @@ -20,7 +20,7 @@ from ..dtos import ( AgentResult, EventSink, - HarnessAgentConfig, + HarnessAgentTemplate, HarnessType, Message, RunContext, @@ -61,7 +61,7 @@ def __init__( self, backend: "SandboxAgentBackend", sandbox: SandboxAgentSandbox, - config: HarnessAgentConfig, + config: HarnessAgentTemplate, *, harness: HarnessType, secrets: Optional[Mapping[str, str]], @@ -153,7 +153,7 @@ async def create_sandbox(self) -> SandboxAgentSandbox: async def create_session( self, sandbox: Sandbox, - config: HarnessAgentConfig, + config: HarnessAgentTemplate, *, harness: HarnessType, secrets: Optional[Mapping[str, str]] = None, diff --git a/sdks/python/agenta/sdk/agents/dtos.py b/sdks/python/agenta/sdk/agents/dtos.py index 51e8324bf7..dbb5ce4296 100644 --- a/sdks/python/agenta/sdk/agents/dtos.py +++ b/sdks/python/agenta/sdk/agents/dtos.py @@ -2,7 +2,7 @@ Everything the ports and adapters pass around: harness identity, capabilities, content blocks, messages, run events, the run result, trace/tool-callback plumbing, the neutral -``AgentConfig``, the per-harness configs a backend plumbs, and the ``SessionConfig`` bundle. +``AgentTemplate``, the per-harness configs a backend plumbs, and the ``SessionConfig`` bundle. These are Pydantic models (the SDK already depends on Pydantic), kept neutral: an adapter translates them to and from its engine's own shapes at its edge. @@ -66,7 +66,7 @@ def coerce(cls, value: "HarnessType | str") -> "HarnessType": # (``agenta:::v``, mirroring ``agenta:builtin:agent:v0`` in # ``engines/running/interfaces.py``). The namespace is ``harness`` and the trailing ``v0`` is # bumped only when the harness contract shape breaks. This is purely the INTERFACE identity the -# agent_config schema advertises; the stored/wire harness VALUE stays the bare enum string +# agent_template schema advertises; the stored/wire harness VALUE stays the bare enum string # (``pi_core`` / ``pi_agenta`` / ``claude``), which the runner reads as the runtime selector. @@ -75,7 +75,7 @@ class HarnessIdentity(BaseModel): ``value`` is the wire/runtime selector (the ``HarnessType`` value); ``slug`` is the versioned contract identity in the repo's slug grammar; ``name`` is the human-facing label - the playground dropdown shows. This is the single source the agent_config schema builds the + the playground dropdown shows. This is the single source the agent_template schema builds the harness ``oneOf`` from, so the slug, name, and value never drift across the SDK, the service schema, and the frontend control.""" @@ -132,7 +132,7 @@ class SandboxPermission(BaseModel): ``network`` is the outbound-egress policy; ``filesystem`` is declared but not enforced today; ``enforcement`` picks ``strict`` (fail the run when the boundary cannot be applied) - or ``best_effort``. Optional on :class:`AgentConfig`: an unset value never reaches the wire, + or ``best_effort``. Optional on :class:`AgentTemplate`: an unset value never reaches the wire, so existing configs are unaffected.""" network: NetworkEgress = Field(default_factory=NetworkEgress) @@ -494,14 +494,14 @@ class AgentResult(BaseModel): # --------------------------------------------------------------------------- -class AgentConfig(BaseModel): +class AgentTemplate(BaseModel): """What an agent is and how it runs — the single agent definition. ``instructions`` becomes ``AGENTS.md``. ``tools`` are provider-agnostic references; resolving them into runnable specs is the caller's job (the Agenta service does it server-side). ``harness`` / ``sandbox`` / ``permission_policy`` are the run-selection fields: which coding agent to drive, where it runs, and how a permission-gating harness answers tool-use - prompts in a headless run. They live on ``AgentConfig`` (under ``data.parameters.agent``) + prompts in a headless run. They live on ``AgentTemplate`` (under ``data.parameters.agent``) rather than a separate object — there is one agent definition, not an agent plus a sidecar selection. ``sandbox`` is a backend/environment concern the caller reads to pick a backend; it never enters ``SessionConfig`` or the neutral run. @@ -562,9 +562,9 @@ def from_params( cls, params: Dict[str, Any], *, - defaults: Optional["AgentConfig"] = None, - ) -> "AgentConfig": - """Build an :class:`AgentConfig` from a request/config dict. + defaults: Optional["AgentTemplate"] = None, + ) -> "AgentTemplate": + """Build an :class:`AgentTemplate` from a request/config dict. Accepts three shapes, in priority order: the dedicated ``agent`` element, the playground ``prompt`` prompt-template (system message -> instructions, ``llm_config`` @@ -595,7 +595,7 @@ def from_params( # --------------------------------------------------------------------------- -class HarnessAgentConfig(BaseModel): +class HarnessAgentTemplate(BaseModel): """Base for a harness-specific config. A Harness produces one of these from the neutral config; a backend plumbs it as-is, with no business logic about how the harness works. @@ -628,7 +628,7 @@ class HarnessAgentConfig(BaseModel): skills: List[SkillConfig] = Field(default_factory=list) sandbox_permission: Optional[SandboxPermission] = None # The neutral per-harness options bag (a map keyed by harness name), carried verbatim from - # ``AgentConfig.harness_kwargs`` by the harness adapter. The active harness's CONFIG translates + # ``AgentTemplate.harness_kwargs`` by the harness adapter. The active harness's CONFIG translates # its own slice into rendered files for the wire (see :meth:`wire_harness_files`); the raw bag # itself does not ride the wire anymore. harness_kwargs: Dict[str, Dict[str, Any]] = Field(default_factory=dict) @@ -745,7 +745,7 @@ def wire_resolved_connection(self) -> Dict[str, Any]: return self.resolved_connection.to_wire() -class PiAgentConfig(HarnessAgentConfig): +class PiAgentTemplate(HarnessAgentTemplate): """Pi's config. Built-in tools by name plus resolved specs delivered natively (Pi has no MCP; the runner registers them through the Pi extension). Pi does not gate tool use, so no permission policy applies. @@ -803,7 +803,7 @@ def wire_prompt(self) -> Dict[str, Any]: return out -class ClaudeAgentConfig(HarnessAgentConfig): +class ClaudeAgentTemplate(HarnessAgentTemplate): """Claude's config. No Pi built-ins; tools are delivered over MCP, and ``permission_policy`` answers Claude's tool-use prompts in a headless run.""" @@ -860,7 +860,7 @@ def wire_harness_files(self) -> Dict[str, Any]: return {"harnessFiles": files} -class AgentaAgentConfig(PiAgentConfig): +class AgentaAgentTemplate(PiAgentTemplate): """The Agenta harness's config. It *is* a Pi config (same engine, same tool delivery and system-prompt layers). ``skills`` ride the inherited :meth:`wire_skills` seam as resolved inline packages, not through ``wire_tools`` (skills are not tools).""" @@ -885,7 +885,7 @@ class SessionConfig(BaseModel): model_config = ConfigDict(populate_by_name=True) - agent: AgentConfig + agent: AgentTemplate secrets: Dict[str, str] = Field(default_factory=dict) # ``resolved_connection`` carries the least-privilege output of a ``ConnectionResolver``. # ``secrets`` is the compatibility alias for ``resolved_connection.env`` during the @@ -949,8 +949,8 @@ def _as_list(raw: Any) -> List[Any]: def _split_model_ref(data: Any) -> Any: """Populate ``model_ref`` from a structured ``model`` and keep ``model`` a plain string. - Shared ``mode="before"`` validator body for :class:`AgentConfig` and - :class:`HarnessAgentConfig`. The lowest-risk wiring (no behavior change in Slice 1): + Shared ``mode="before"`` validator body for :class:`AgentTemplate` and + :class:`HarnessAgentTemplate`. The lowest-risk wiring (no behavior change in Slice 1): - ``model`` is a dict or a :class:`ModelRef` -> set ``model_ref`` from it and project ``model`` to its plain ``provider/model`` string. A structured config gains a typed ref @@ -976,12 +976,12 @@ def _split_model_ref(data: Any) -> Any: def _parse_mcp_servers_raw( params: Dict[str, Any], - defaults: AgentConfig, + defaults: AgentTemplate, ) -> List[Any]: """Pull the raw ``mcp_servers`` list from a request/config dict, falling back to defaults. Reads ``mcp_servers`` from the ``agent`` element when present, else the flat request. - Canonical validation happens on :class:`AgentConfig` construction.""" + Canonical validation happens on :class:`AgentTemplate` construction.""" agent = params.get("agent") source = agent if isinstance(agent, dict) else params raw = source.get("mcp_servers") @@ -992,13 +992,13 @@ def _parse_mcp_servers_raw( def _parse_skills_raw( params: Dict[str, Any], - defaults: AgentConfig, + defaults: AgentTemplate, ) -> List[Any]: """Pull the raw ``skills`` list from a request/config dict, falling back to defaults. Reads ``skills`` from the ``agent`` element when present, else the flat request. Mirrors the MCP path so an unparsed ``skills`` is not silently dropped; canonical validation happens - on :class:`AgentConfig` construction. Each entry is a concrete inline ``SkillConfig`` by the + on :class:`AgentTemplate` construction. Each entry is a concrete inline ``SkillConfig`` by the time the request is built (any ``@ag.embed`` reference resolved server-side first).""" agent = params.get("agent") source = agent if isinstance(agent, dict) else params @@ -1010,7 +1010,7 @@ def _parse_skills_raw( def _parse_harness_kwargs( params: Dict[str, Any], - defaults: AgentConfig, + defaults: AgentTemplate, ) -> Dict[str, Dict[str, Any]]: """Pull the per-harness options bag from a request/config dict, falling back to defaults. @@ -1035,7 +1035,7 @@ def _parse_harness_kwargs( def _parse_run_selection( params: Dict[str, Any], - defaults: AgentConfig, + defaults: AgentTemplate, ) -> Tuple[str, str, "PermissionPolicy"]: """Pull the run-selection trio (harness / sandbox / permission_policy) from a request dict. @@ -1054,7 +1054,7 @@ def _parse_run_selection( def _parse_sandbox_permission( params: Dict[str, Any], - defaults: AgentConfig, + defaults: AgentTemplate, ) -> Optional[SandboxPermission]: """Pull the sandbox permission object from a request/config dict, falling back to defaults. @@ -1093,7 +1093,7 @@ def _system_text(messages: Optional[List[Any]]) -> str: def _parse_agent_fields( params: Dict[str, Any], - defaults: AgentConfig, + defaults: AgentTemplate, ) -> Tuple[Optional[str], Optional[str], Any]: """Pull (instructions, model, tools) from a request/config dict, with fallbacks.""" agent = params.get("agent") diff --git a/sdks/python/agenta/sdk/agents/interfaces.py b/sdks/python/agenta/sdk/agents/interfaces.py index d89e73eb92..a0f9987eb9 100644 --- a/sdks/python/agenta/sdk/agents/interfaces.py +++ b/sdks/python/agenta/sdk/agents/interfaces.py @@ -24,7 +24,7 @@ from .dtos import ( AgentResult, EventSink, - HarnessAgentConfig, + HarnessAgentTemplate, HarnessType, Message, RunContext, @@ -122,7 +122,7 @@ async def create_sandbox(self) -> Sandbox: async def create_session( self, sandbox: Sandbox, - config: HarnessAgentConfig, + config: HarnessAgentTemplate, *, harness: HarnessType, secrets: Optional[Mapping[str, str]] = None, @@ -176,7 +176,7 @@ async def _sandbox(self) -> Sandbox: async def create_session( self, - config: HarnessAgentConfig, + config: HarnessAgentTemplate, *, harness: HarnessType, session_config: SessionConfig, @@ -229,7 +229,7 @@ async def cleanup(self) -> None: await self._env.shutdown() @abstractmethod - def _to_harness_config(self, config: SessionConfig) -> HarnessAgentConfig: + def _to_harness_config(self, config: SessionConfig) -> HarnessAgentTemplate: """Map the neutral config into this harness's own config (the mapping logic).""" def _provisioning(self, config: SessionConfig) -> Mapping[str, bytes]: diff --git a/sdks/python/agenta/sdk/agents/utils/wire.py b/sdks/python/agenta/sdk/agents/utils/wire.py index 3231538b7c..7c2f13d71b 100644 --- a/sdks/python/agenta/sdk/agents/utils/wire.py +++ b/sdks/python/agenta/sdk/agents/utils/wire.py @@ -25,7 +25,7 @@ from ..dtos import ( Event, AgentResult, - HarnessAgentConfig, + HarnessAgentTemplate, HarnessCapabilities, HarnessType, Message, @@ -70,7 +70,7 @@ def request_to_wire( *, harness: HarnessType, sandbox: str, - config: HarnessAgentConfig, + config: HarnessAgentTemplate, messages: Sequence[Message], secrets: Optional[Dict[str, str]] = None, trace: Optional[TraceContext] = None, diff --git a/sdks/python/agenta/sdk/agents/wire_models.py b/sdks/python/agenta/sdk/agents/wire_models.py index fabcd81ff7..ebbdc3725b 100644 --- a/sdks/python/agenta/sdk/agents/wire_models.py +++ b/sdks/python/agenta/sdk/agents/wire_models.py @@ -11,7 +11,7 @@ What these models are for in this phase (a pre-production POC): - They are the schema authority: ``run_contract_schemas()`` exports their JSON Schema, which - ships in the SDK through ``CATALOG_TYPES`` (the same mechanism ``AgentConfigSchema`` uses to + ships in the SDK through ``CATALOG_TYPES`` (the same mechanism ``AgentTemplateSchema`` uses to reach the SDK / clients / ``/inspect``). A test asserts the committed catalog entry matches a fresh export, so the schema cannot drift from these models. - They validate the golden fixtures and ``request_to_wire`` output in tests, proving the schema @@ -417,7 +417,7 @@ class WireRunResult(_WireModel): # --------------------------------------------------------------------------- # The top-level wire models whose JSON Schema ships in the SDK. Each is keyed by its -# ``x-ag-type`` so ``CATALOG_TYPES`` can carry it the same way it carries ``agent_config``. +# ``x-ag-type`` so ``CATALOG_TYPES`` can carry it the same way it carries ``agent-template``. WIRE_CONTRACT_MODELS = (WireRunRequest, WireRunResult) diff --git a/sdks/python/agenta/sdk/engines/running/interfaces.py b/sdks/python/agenta/sdk/engines/running/interfaces.py index 1cb915c852..9bd5507db4 100644 --- a/sdks/python/agenta/sdk/engines/running/interfaces.py +++ b/sdks/python/agenta/sdk/engines/running/interfaces.py @@ -526,10 +526,10 @@ def llm_inputs_schema( parameters=obj( properties={ # One composite control for the whole agent config. The field shape lives in - # `AgentConfigSchema` (agenta.sdk.utils.types), registered as the `agent_config` - # catalog type; the playground resolves this ref and renders the AgentConfigControl. + # `AgentTemplateSchema` (agenta.sdk.utils.types), registered as the `agent-template` + # catalog type; the playground resolves this ref and renders the AgentTemplateControl. "agent": semantic_field( - x_ag_type_ref="agent_config", + x_ag_type_ref="agent-template", jtype="object", description="The agent's instructions, model, tools, MCP servers, and runtime.", # The minimal builtin default: no platform skill, no sandbox_permission. The diff --git a/sdks/python/agenta/sdk/utils/types.py b/sdks/python/agenta/sdk/utils/types.py index 24430b26e7..1f5525ee0b 100644 --- a/sdks/python/agenta/sdk/utils/types.py +++ b/sdks/python/agenta/sdk/utils/types.py @@ -1065,8 +1065,8 @@ def _model_catalog_type() -> dict: # The single source of the run-selection defaults. The SDK builtin interface # (`agenta:builtin:agent:v0`) and the agent service (`AGENT_SCHEMAS` / the value -# `AgentConfig.from_params` falls back to) both consume these via `build_agent_v0_default`, so a new -# default changes one place. The harness default also seeds `AgentConfigSchema.harness`. +# `AgentTemplate.from_params` falls back to) both consume these via `build_agent_v0_default`, so a new +# default changes one place. The harness default also seeds `AgentTemplateSchema.harness`. _DEFAULT_HARNESS = "pi_core" _DEFAULT_SANDBOX = "local" _DEFAULT_PERMISSION_POLICY = "auto" @@ -1103,21 +1103,21 @@ def _harness_field_schema_extra() -> Dict[str, Any]: } -class AgentConfigSchema(AgSchemaMixin): +class AgentTemplateSchema(AgSchemaMixin): """The playground's editable agent config (the ``agent`` element), as one semantic type. - This is the schema-generation counterpart to the runtime :class:`agenta.sdk.agents.AgentConfig` - parser: it exists only to emit a rich JSON Schema for the ``agent_config`` control, so the + This is the schema-generation counterpart to the runtime :class:`agenta.sdk.agents.AgentTemplate` + parser: it exists only to emit a rich JSON Schema for the ``agent-template`` control, so the field shapes live in Pydantic (single source of truth) instead of a hand-written literal. It composes every editable field the control surfaces — the definition (``agents_md``/``model``/``tools``/``mcp_servers``) and the run-selection fields (``harness``/``sandbox``/``permission_policy``), all one config — and types ``tools``/``mcp_servers`` with the real tool-def models so the playground gets typed editors. - The runtime ``AgentConfig`` stays permissive (``List[Any]``) because its job is to coerce the + The runtime ``AgentTemplate`` stays permissive (``List[Any]``) because its job is to coerce the loose shapes the playground emits; this model is strict because its job is to describe them. """ - __ag_type__ = "agent_config" + __ag_type__ = "agent-template" agents_md: str = Field( default=_DEFAULT_AGENTS_MD, @@ -1200,7 +1200,7 @@ def build_agent_v0_default( skill_slug: Optional[str] = None, include_sandbox_permission: bool = False, ) -> Dict[str, Any]: - """The default `agent_config` value, shared by the builtin interface and the service. + """The default `agent-template` value, shared by the builtin interface and the service. Base shape (always): instructions, model, empty tools/MCP, the run selection (harness/sandbox/permission policy). ``include_sandbox_permission`` adds the declared @@ -1367,9 +1367,9 @@ class _ToolEmbedRefSchema(BaseModel): ) -# Resolve the forward references on AgentConfigSchema.skills + tools (inline / embed). +# Resolve the forward references on AgentTemplateSchema.skills + tools (inline / embed). # A workflow referenced as a tool is the ``type:"reference"`` arm of ``ToolConfig`` itself. -AgentConfigSchema.model_rebuild() +AgentTemplateSchema.model_rebuild() CATALOG_TYPES = { @@ -1385,8 +1385,8 @@ class _ToolEmbedRefSchema(BaseModel): AgPermissions.ag_type(): _dereference_schema(AgPermissions.model_json_schema()), AgResponse.ag_type(): _dereference_schema(AgResponse.model_json_schema()), PromptTemplate.ag_type(): _dereference_schema(PromptTemplate.model_json_schema()), - AgentConfigSchema.ag_type(): _dereference_schema( - AgentConfigSchema.model_json_schema() + AgentTemplateSchema.ag_type(): _dereference_schema( + AgentTemplateSchema.model_json_schema() ), SkillConfigSchema.ag_type(): _dereference_schema( SkillConfigSchema.model_json_schema() diff --git a/sdks/python/oss/tests/pytest/integration/agents/_fake_runner_backend.py b/sdks/python/oss/tests/pytest/integration/agents/_fake_runner_backend.py index e71c09822e..75ed1fe291 100644 --- a/sdks/python/oss/tests/pytest/integration/agents/_fake_runner_backend.py +++ b/sdks/python/oss/tests/pytest/integration/agents/_fake_runner_backend.py @@ -16,7 +16,7 @@ from agenta.sdk.agents.dtos import ( AgentResult, EventSink, - HarnessAgentConfig, + HarnessAgentTemplate, HarnessType, Message, TraceContext, @@ -50,7 +50,7 @@ class FakeRunnerSession(Session): def __init__( self, backend: "FakeRunnerBackend", - config: HarnessAgentConfig, + config: HarnessAgentTemplate, *, harness: HarnessType, secrets: Optional[Mapping[str, str]], @@ -143,7 +143,7 @@ async def create_sandbox(self) -> FakeRunnerSandbox: async def create_session( self, sandbox: Sandbox, - config: HarnessAgentConfig, + config: HarnessAgentTemplate, *, harness: HarnessType, secrets: Optional[Mapping[str, str]] = None, diff --git a/sdks/python/oss/tests/pytest/integration/agents/test_transport_roundtrip.py b/sdks/python/oss/tests/pytest/integration/agents/test_transport_roundtrip.py index 97b6ce7343..deab63bfe0 100644 --- a/sdks/python/oss/tests/pytest/integration/agents/test_transport_roundtrip.py +++ b/sdks/python/oss/tests/pytest/integration/agents/test_transport_roundtrip.py @@ -15,7 +15,7 @@ import pytest from agenta.sdk.agents import ( - AgentConfig, + AgentTemplate, Environment, Message, PiHarness, @@ -107,7 +107,7 @@ def _backend(tmp_path, body: str) -> FakeRunnerBackend: async def test_prompt_round_trips_through_the_real_transport(tmp_path): harness = PiHarness(Environment(_backend(tmp_path, _ECHO_RUNNER))) - config = SessionConfig(agent=AgentConfig(instructions="hi", model="gpt-5.5")) + config = SessionConfig(agent=AgentTemplate(instructions="hi", model="gpt-5.5")) result = await harness.prompt(config, [Message(role="user", content="ping")]) @@ -123,7 +123,7 @@ async def test_prompt_round_trips_through_the_real_transport(tmp_path): async def test_runner_failure_surfaces_as_runtime_error(tmp_path): harness = PiHarness(Environment(_backend(tmp_path, _FAIL_RUNNER))) - config = SessionConfig(agent=AgentConfig(instructions="hi")) + config = SessionConfig(agent=AgentTemplate(instructions="hi")) with pytest.raises(RuntimeError, match="model exploded"): await harness.prompt(config, [Message(role="user", content="hi")]) @@ -131,14 +131,14 @@ async def test_runner_failure_surfaces_as_runtime_error(tmp_path): async def test_runner_empty_output_raises(tmp_path): harness = PiHarness(Environment(_backend(tmp_path, _SILENT_RUNNER))) - config = SessionConfig(agent=AgentConfig(instructions="hi")) + config = SessionConfig(agent=AgentTemplate(instructions="hi")) with pytest.raises(RuntimeError, match="no output"): await harness.prompt(config, [Message(role="user", content="hi")]) async def test_resolved_skill_reaches_the_runner_over_the_wire(tmp_path): - # An AgentConfig carrying a resolved inline skill (the post-@ag.embed-resolution shape) must + # An AgentTemplate carrying a resolved inline skill (the post-@ag.embed-resolution shape) must # arrive at the runner as a concrete `skills` package over the real wire + transport, not as # an embed and not dropped. The skill-echo runner reports the `skills` it saw. harness = PiHarness(Environment(_backend(tmp_path, _SKILL_ECHO_RUNNER))) @@ -151,7 +151,7 @@ async def test_resolved_skill_reaches_the_runner_over_the_wire(tmp_path): allow_executable_files=True, ) config = SessionConfig( - agent=AgentConfig(instructions="hi", model="gpt-5.5", skills=[skill]) + agent=AgentTemplate(instructions="hi", model="gpt-5.5", skills=[skill]) ) result = await harness.prompt(config, [Message(role="user", content="ping")]) diff --git a/sdks/python/oss/tests/pytest/unit/agents/connections/test_dtos_model_ref.py b/sdks/python/oss/tests/pytest/unit/agents/connections/test_dtos_model_ref.py index 55eb5b487a..4d9ae6377f 100644 --- a/sdks/python/oss/tests/pytest/unit/agents/connections/test_dtos_model_ref.py +++ b/sdks/python/oss/tests/pytest/unit/agents/connections/test_dtos_model_ref.py @@ -9,27 +9,27 @@ from __future__ import annotations from agenta.sdk.agents import ( - AgentConfig, + AgentTemplate, Connection, HarnessType, Message, ModelRef, - PiAgentConfig, + PiAgentTemplate, ) from agenta.sdk.agents.utils.wire import request_to_wire -# --------------------------------------------------------------- AgentConfig.model_ref +# --------------------------------------------------------------- AgentTemplate.model_ref def test_plain_string_model_leaves_model_ref_unset(): - config = AgentConfig(model="openai-codex/gpt-5.5") + config = AgentTemplate(model="openai-codex/gpt-5.5") assert config.model == "openai-codex/gpt-5.5" assert config.model_ref is None def test_dict_model_populates_model_ref_and_projects_string(): - config = AgentConfig( + config = AgentTemplate( model={ "provider": "openai", "model": "gpt-5.5", @@ -44,13 +44,13 @@ def test_dict_model_populates_model_ref_and_projects_string(): def test_model_ref_instance_populates_and_projects(): ref = ModelRef(provider="anthropic", model="claude-opus-4-8") - config = AgentConfig(model=ref) + config = AgentTemplate(model=ref) assert config.model == "anthropic/claude-opus-4-8" assert config.model_ref is ref or config.model_ref == ref def test_explicit_model_ref_is_respected(): - config = AgentConfig( + config = AgentTemplate( model="gpt-5.5", model_ref=ModelRef(provider="openai", model="gpt-5.5"), ) @@ -62,12 +62,12 @@ def test_explicit_model_ref_is_respected(): def test_wire_model_ref_empty_for_string_only_config(): - config = PiAgentConfig(model="openai-codex/gpt-5.5") + config = PiAgentTemplate(model="openai-codex/gpt-5.5") assert config.wire_model_ref() == {} def test_wire_model_ref_emits_provider_and_connection_for_structured(): - config = PiAgentConfig( + config = PiAgentTemplate( model={ "provider": "openai", "model": "gpt-5.5", @@ -81,7 +81,7 @@ def test_wire_model_ref_emits_provider_and_connection_for_structured(): def test_wire_model_ref_omits_default_connection(): - config = PiAgentConfig( + config = PiAgentTemplate( model={"provider": "openai", "model": "gpt-5.5"}, ) # Default connection carries no non-default info, so only the provider rides the wire. @@ -89,7 +89,7 @@ def test_wire_model_ref_omits_default_connection(): def test_wire_model_ref_emits_self_managed_connection_without_slug(): - config = PiAgentConfig( + config = PiAgentTemplate( model={ "provider": "openai", "model": "gpt-5.5", @@ -107,7 +107,7 @@ def test_string_only_config_wire_has_no_new_keys(): payload = request_to_wire( harness=HarnessType.PI, sandbox="local", - config=PiAgentConfig(model="openai-codex/gpt-5.5"), + config=PiAgentTemplate(model="openai-codex/gpt-5.5"), messages=[Message(role="user", content="hi")], ) assert "provider" not in payload @@ -119,7 +119,7 @@ def test_structured_config_wire_carries_provider_and_connection(): payload = request_to_wire( harness=HarnessType.PI, sandbox="local", - config=PiAgentConfig( + config=PiAgentTemplate( model={ "provider": "openai", "model": "gpt-5.5", diff --git a/sdks/python/oss/tests/pytest/unit/agents/skills/test_skills_e2e.py b/sdks/python/oss/tests/pytest/unit/agents/skills/test_skills_e2e.py index 764bb4654c..0f201ddd0f 100644 --- a/sdks/python/oss/tests/pytest/unit/agents/skills/test_skills_e2e.py +++ b/sdks/python/oss/tests/pytest/unit/agents/skills/test_skills_e2e.py @@ -1,13 +1,13 @@ -"""End-to-end SDK skill coverage: an `AgentConfig`'s skills land on the `/run` wire as +"""End-to-end SDK skill coverage: an `AgentTemplate`'s skills land on the `/run` wire as concrete inline packages, whether they were authored inline or pulled in via an `@ag.embed`. These lock the two author shapes the skills feature ships: -1. **Inline skill -> wire.** An `AgentConfig` carrying an inline `SkillConfig` produces a runner +1. **Inline skill -> wire.** An `AgentTemplate` carrying an inline `SkillConfig` produces a runner request whose `skills[0]` is the materialized inline package (name/description/body/files + camelCase flags), via the `wire_skills()` seam that `request_to_wire` spreads. -2. **Embed skill -> resolve -> wire.** An `AgentConfig` whose `skills` list holds an `@ag.embed` +2. **Embed skill -> resolve -> wire.** An `AgentTemplate` whose `skills` list holds an `@ag.embed` entry, run through the resolution middleware against a MOCKED resolve endpoint that returns a `SkillConfig`-shaped `parameters.skill`, ends up on the wire as a concrete inline package (the embed is gone). This mirrors how the resolver tests mock `/workflows/revisions/resolve`, then @@ -24,7 +24,7 @@ import agenta as ag from agenta.sdk.agents import ( - AgentConfig, + AgentTemplate, Environment, HarnessType, Message, @@ -50,8 +50,8 @@ } -def _pi_wire(env: Environment, agent: AgentConfig) -> dict: - """Translate an `AgentConfig` through the Pi harness and serialize one turn to the wire.""" +def _pi_wire(env: Environment, agent: AgentTemplate) -> dict: + """Translate an `AgentTemplate` through the Pi harness and serialize one turn to the wire.""" harness = PiHarness(env) pi_config = harness._to_harness_config(SessionConfig(agent=agent)) return request_to_wire( @@ -67,7 +67,7 @@ def _pi_wire(env: Environment, agent: AgentConfig) -> dict: def test_inline_skill_materializes_on_the_wire(make_env): env = make_env(supported=[HarnessType.PI]) - agent = AgentConfig(instructions="hi", model="gpt-5.5", skills=[_INLINE_SKILL]) + agent = AgentTemplate(instructions="hi", model="gpt-5.5", skills=[_INLINE_SKILL]) wire = _pi_wire(env, agent) @@ -93,7 +93,7 @@ def test_inline_skill_materializes_on_the_wire(make_env): def test_minimal_inline_skill_omits_optional_flags_on_the_wire(make_env): env = make_env(supported=[HarnessType.PI]) - agent = AgentConfig( + agent = AgentTemplate( instructions="hi", model="gpt-5.5", skills=[SkillConfig(name="a", description="d", body="b")], @@ -115,7 +115,7 @@ def test_minimal_inline_skill_omits_optional_flags_on_the_wire(make_env): @pytest.mark.asyncio async def test_embed_skill_resolves_to_a_concrete_package_on_the_wire(make_env): # The author config references a skill by an `@ag.embed` inside the skills list (the platform default-config shape). The resolver inlines the stored `SkillConfig` BEFORE the handler - # builds the AgentConfig, so the runner must never see the embed -- only a concrete package. + # builds the AgentTemplate, so the runner must never see the embed -- only a concrete package. params_with_embed = { "skills": [ { @@ -188,9 +188,9 @@ async def post(self, *args, **kwargs): assert request.data.parameters == resolved_params # Now carry the resolved params the rest of the way, exactly as the handler does: build the - # AgentConfig from them, translate through the harness, and serialize the wire. + # AgentTemplate from them, translate through the harness, and serialize the wire. env = make_env(supported=[HarnessType.PI]) - agent = AgentConfig.from_params(request.data.parameters) + agent = AgentTemplate.from_params(request.data.parameters) wire = _pi_wire(env, agent) # The embed is gone; a concrete inline package rides the wire. diff --git a/sdks/python/oss/tests/pytest/unit/agents/test_dtos_agent_config.py b/sdks/python/oss/tests/pytest/unit/agents/test_dtos_agent_template.py similarity index 78% rename from sdks/python/oss/tests/pytest/unit/agents/test_dtos_agent_config.py rename to sdks/python/oss/tests/pytest/unit/agents/test_dtos_agent_template.py index d0913e03df..39e662df8e 100644 --- a/sdks/python/oss/tests/pytest/unit/agents/test_dtos_agent_config.py +++ b/sdks/python/oss/tests/pytest/unit/agents/test_dtos_agent_template.py @@ -1,26 +1,26 @@ -"""``AgentConfig.from_params`` (the three request shapes), including the run-selection fields. +"""``AgentTemplate.from_params`` (the three request shapes), including the run-selection fields. -The handler parses whatever the playground or a stored config sends into one ``AgentConfig``. +The handler parses whatever the playground or a stored config sends into one ``AgentTemplate``. This file locks the three accepted shapes, the defaults fall-through, the ``harness_kwargs`` escape hatch, and the run-selection parsing (``harness`` / ``sandbox`` / ``permission_policy``, -which now live on ``AgentConfig`` rather than a separate ``RunSelection``). +which now live on ``AgentTemplate`` rather than a separate ``RunSelection``). """ from __future__ import annotations from agenta.sdk.agents import ( - AgentConfig, + AgentTemplate, BuiltinToolConfig, ) -_DEFAULTS = AgentConfig(instructions="default-md", model="default-model", tools=["d"]) +_DEFAULTS = AgentTemplate(instructions="default-md", model="default-model", tools=["d"]) -# ----------------------------------------------------------- AgentConfig shapes +# ----------------------------------------------------------- AgentTemplate shapes def test_from_params_agent_element_shape(): - config = AgentConfig.from_params( + config = AgentTemplate.from_params( { "agent": { "instructions": "I", @@ -38,7 +38,7 @@ def test_from_params_agent_element_shape(): def test_from_params_prompt_template_shape(): - config = AgentConfig.from_params( + config = AgentTemplate.from_params( { "prompt": { "messages": [ @@ -56,7 +56,7 @@ def test_from_params_prompt_template_shape(): def test_from_params_prompt_template_joins_multiple_system_messages(): - config = AgentConfig.from_params( + config = AgentTemplate.from_params( { "prompt": { "messages": [ @@ -74,7 +74,7 @@ def test_from_params_prompt_template_joins_multiple_system_messages(): def test_from_params_flat_shape(): - config = AgentConfig.from_params( + config = AgentTemplate.from_params( {"model": "M", "agents_md": "A", "tools": [{"name": "x"}]}, defaults=_DEFAULTS, ) @@ -84,14 +84,14 @@ def test_from_params_flat_shape(): def test_from_params_falls_back_to_defaults(): - config = AgentConfig.from_params({}, defaults=_DEFAULTS) + config = AgentTemplate.from_params({}, defaults=_DEFAULTS) assert config.instructions == "default-md" assert config.model == "default-model" assert config.tools == [BuiltinToolConfig(name="d")] def test_from_params_agent_element_preserves_default_tools_when_absent(): - config = AgentConfig.from_params( + config = AgentTemplate.from_params( {"agent": {"instructions": "I", "model": "M"}}, defaults=_DEFAULTS, ) @@ -102,7 +102,7 @@ def test_from_params_agent_element_preserves_default_tools_when_absent(): def test_from_params_agent_element_empty_tools_clears_defaults(): - config = AgentConfig.from_params( + config = AgentTemplate.from_params( {"agent": {"tools": []}}, defaults=_DEFAULTS, ) @@ -111,7 +111,7 @@ def test_from_params_agent_element_empty_tools_clears_defaults(): def test_from_params_coerces_single_tool_dict_to_list(): - config = AgentConfig.from_params({"agent": {"tools": {"name": "solo"}}}) + config = AgentTemplate.from_params({"agent": {"tools": {"name": "solo"}}}) assert config.tools == [BuiltinToolConfig(name="solo")] @@ -126,31 +126,31 @@ def test_from_params_coerces_single_tool_dict_to_list(): def test_from_params_parses_skills_from_agent_element(): - config = AgentConfig.from_params({"agent": {"skills": [dict(_SKILL)]}}) + config = AgentTemplate.from_params({"agent": {"skills": [dict(_SKILL)]}}) assert [s.name for s in config.skills] == ["release-notes"] def test_from_params_parses_skills_from_flat_request(): - config = AgentConfig.from_params({"skills": [dict(_SKILL)]}) + config = AgentTemplate.from_params({"skills": [dict(_SKILL)]}) assert [s.name for s in config.skills] == ["release-notes"] def test_from_params_skills_default_empty(): # An absent `skills` is not silently dropped into a default it never had; it is just empty. - config = AgentConfig.from_params({"agent": {"instructions": "I"}}) + config = AgentTemplate.from_params({"agent": {"instructions": "I"}}) assert config.skills == [] def test_from_params_skills_falls_back_to_defaults_when_absent(): - defaults = AgentConfig(skills=[dict(_SKILL)]) - config = AgentConfig.from_params( + defaults = AgentTemplate(skills=[dict(_SKILL)]) + config = AgentTemplate.from_params( {"agent": {"instructions": "I"}}, defaults=defaults ) assert [s.name for s in config.skills] == ["release-notes"] def test_harness_kwargs_drops_malformed_and_lowercases_keys(): - config = AgentConfig.from_params( + config = AgentTemplate.from_params( { "agent": { "harness_kwargs": { @@ -164,8 +164,8 @@ def test_harness_kwargs_drops_malformed_and_lowercases_keys(): def test_harness_kwargs_falls_back_to_defaults_when_absent(): - defaults = AgentConfig(harness_kwargs={"pi_core": {"system": "D"}}) - config = AgentConfig.from_params( + defaults = AgentTemplate(harness_kwargs={"pi_core": {"system": "D"}}) + config = AgentTemplate.from_params( {"agent": {"instructions": "I"}}, defaults=defaults ) assert config.harness_kwargs == {"pi_core": {"system": "D"}} @@ -174,8 +174,8 @@ def test_harness_kwargs_falls_back_to_defaults_when_absent(): def test_harness_kwargs_explicit_empty_clears_defaults(): # An explicit empty dict clears inherited per-harness options; only an absent # key falls back to defaults. - defaults = AgentConfig(harness_kwargs={"pi_core": {"system": "D"}}) - config = AgentConfig.from_params( + defaults = AgentTemplate(harness_kwargs={"pi_core": {"system": "D"}}) + config = AgentTemplate.from_params( {"agent": {"harness_kwargs": {}}}, defaults=defaults ) assert config.harness_kwargs == {} @@ -185,7 +185,7 @@ def test_harness_kwargs_explicit_empty_clears_defaults(): def test_run_selection_defaults(): - config = AgentConfig.from_params({}) + config = AgentTemplate.from_params({}) assert (config.harness, config.sandbox, config.permission_policy) == ( "pi_core", "local", @@ -194,7 +194,7 @@ def test_run_selection_defaults(): def test_run_selection_reads_agent_subdict_and_lowercases(): - config = AgentConfig.from_params( + config = AgentTemplate.from_params( { "agent": { "harness": "Claude", @@ -211,12 +211,12 @@ def test_run_selection_reads_agent_subdict_and_lowercases(): def test_run_selection_honors_defaults(): - defaults = AgentConfig(harness="claude", sandbox="daytona") - config = AgentConfig.from_params({}, defaults=defaults) + defaults = AgentTemplate(harness="claude", sandbox="daytona") + config = AgentTemplate.from_params({}, defaults=defaults) assert config.harness == "claude" assert config.sandbox == "daytona" def test_run_selection_reads_flat_request(): - config = AgentConfig.from_params({"harness": "claude"}) + config = AgentTemplate.from_params({"harness": "claude"}) assert config.harness == "claude" diff --git a/sdks/python/oss/tests/pytest/unit/agents/test_dtos_harness_configs.py b/sdks/python/oss/tests/pytest/unit/agents/test_dtos_harness_configs.py index 1d53c8f469..196f9fc0d5 100644 --- a/sdks/python/oss/tests/pytest/unit/agents/test_dtos_harness_configs.py +++ b/sdks/python/oss/tests/pytest/unit/agents/test_dtos_harness_configs.py @@ -10,10 +10,10 @@ import pytest from agenta.sdk.agents import ( - ClaudeAgentConfig, + ClaudeAgentTemplate, ClientToolSpec, - HarnessAgentConfig, - PiAgentConfig, + HarnessAgentTemplate, + PiAgentTemplate, ToolCallback, ) @@ -21,7 +21,7 @@ def test_pi_wire_tools_is_native_and_never_gates(): - config = PiAgentConfig( + config = PiAgentTemplate( builtin_tools=["read"], tool_specs=[ ClientToolSpec( @@ -50,21 +50,23 @@ def test_pi_wire_tools_is_native_and_never_gates(): def test_pi_wire_tools_without_callback(): - assert PiAgentConfig().wire_tools()["toolCallback"] is None + assert PiAgentTemplate().wire_tools()["toolCallback"] is None def test_pi_wire_prompt_emits_only_set_overrides(): - assert PiAgentConfig().wire_prompt() == {} - assert PiAgentConfig(system="s").wire_prompt() == {"systemPrompt": "s"} - assert PiAgentConfig(append_system="a").wire_prompt() == {"appendSystemPrompt": "a"} - assert PiAgentConfig(system="", append_system="a").wire_prompt() == { + assert PiAgentTemplate().wire_prompt() == {} + assert PiAgentTemplate(system="s").wire_prompt() == {"systemPrompt": "s"} + assert PiAgentTemplate(append_system="a").wire_prompt() == { + "appendSystemPrompt": "a" + } + assert PiAgentTemplate(system="", append_system="a").wire_prompt() == { "systemPrompt": "", # an explicit empty string is still an override here "appendSystemPrompt": "a", } def test_claude_wire_tools_has_no_builtins_and_carries_policy(): - config = ClaudeAgentConfig( + config = ClaudeAgentTemplate( tool_specs=[ ClientToolSpec( name="t", @@ -88,12 +90,14 @@ def test_claude_wire_tools_has_no_builtins_and_carries_policy(): def test_claude_defaults_to_auto_policy_and_empty_prompt(): - assert ClaudeAgentConfig().wire_tools()["permissionPolicy"] == "auto" - assert ClaudeAgentConfig().wire_prompt() == {} # Claude exposes no prompt overrides + assert ClaudeAgentTemplate().wire_tools()["permissionPolicy"] == "auto" + assert ( + ClaudeAgentTemplate().wire_prompt() == {} + ) # Claude exposes no prompt overrides def test_base_config_wire_tools_is_abstract(): # The base class does not know any engine's tool shape. with pytest.raises(NotImplementedError): - HarnessAgentConfig().wire_tools() - assert HarnessAgentConfig().wire_prompt() == {} + HarnessAgentTemplate().wire_tools() + assert HarnessAgentTemplate().wire_prompt() == {} diff --git a/sdks/python/oss/tests/pytest/unit/agents/test_environment_lifecycle.py b/sdks/python/oss/tests/pytest/unit/agents/test_environment_lifecycle.py index c84761885f..1ae77d7d96 100644 --- a/sdks/python/oss/tests/pytest/unit/agents/test_environment_lifecycle.py +++ b/sdks/python/oss/tests/pytest/unit/agents/test_environment_lifecycle.py @@ -10,7 +10,7 @@ import pytest from agenta.sdk.agents import ( - AgentConfig, + AgentTemplate, AgentResult, HarnessType, Message, @@ -20,7 +20,7 @@ def _config(instructions="hi") -> SessionConfig: - return SessionConfig(agent=AgentConfig(instructions=instructions, model="m")) + return SessionConfig(agent=AgentTemplate(instructions=instructions, model="m")) # ------------------------------------------------------------- Environment policy diff --git a/sdks/python/oss/tests/pytest/unit/agents/test_harness_adapters.py b/sdks/python/oss/tests/pytest/unit/agents/test_harness_adapters.py index d00663561f..31434e693a 100644 --- a/sdks/python/oss/tests/pytest/unit/agents/test_harness_adapters.py +++ b/sdks/python/oss/tests/pytest/unit/agents/test_harness_adapters.py @@ -13,14 +13,14 @@ import pytest from agenta.sdk.agents import ( - AgentaAgentConfig, + AgentaAgentTemplate, AgentaHarness, - AgentConfig, - ClaudeAgentConfig, + AgentTemplate, + ClaudeAgentTemplate, ClaudeHarness, ClientToolSpec, HarnessType, - PiAgentConfig, + PiAgentTemplate, PiHarness, SessionConfig, ToolCallback, @@ -42,7 +42,7 @@ def _session_config(**kwargs) -> SessionConfig: - agent = kwargs.pop("agent", AgentConfig(instructions="hi", model="m")) + agent = kwargs.pop("agent", AgentTemplate(instructions="hi", model="m")) return SessionConfig(agent=agent, **kwargs) @@ -59,7 +59,7 @@ def test_pi_keeps_builtins_and_native_tools(make_env): result = harness._to_harness_config(config) - assert isinstance(result, PiAgentConfig) + assert isinstance(result, PiAgentTemplate) assert result.builtin_tools == ["read", "write"] assert result.custom_tools[0]["name"] == "t" assert result.tool_callback is _CALLBACK @@ -69,7 +69,7 @@ def test_pi_keeps_builtins_and_native_tools(make_env): def test_pi_reads_its_harness_kwargs_slice(make_env): harness = PiHarness(make_env(supported=[HarnessType.PI])) - agent = AgentConfig( + agent = AgentTemplate( instructions="hi", harness_kwargs={ "pi_core": {"system": "You are Pi.", "append_system": "Be terse."}, @@ -91,7 +91,7 @@ def test_pi_reads_its_harness_kwargs_slice(make_env): def test_pi_drops_blank_harness_kwargs(make_env): harness = PiHarness(make_env(supported=[HarnessType.PI])) - agent = AgentConfig( + agent = AgentTemplate( instructions="hi", harness_kwargs={"pi_core": {"system": " ", "append_system": ""}}, ) @@ -114,7 +114,9 @@ def test_agenta_forces_tools_preamble_and_persona_and_carries_skills(make_env): "body": "Read the changelog, then write notes.", } config = _session_config( - agent=AgentConfig(instructions="My project rules.", model="m", skills=[skill]), + agent=AgentTemplate( + instructions="My project rules.", model="m", skills=[skill] + ), builtin_tools=["web_search"], custom_tools=[{"name": "t", "callRef": "ref"}], tool_callback=_CALLBACK, @@ -122,7 +124,7 @@ def test_agenta_forces_tools_preamble_and_persona_and_carries_skills(make_env): result = harness._to_harness_config(config) - assert isinstance(result, AgentaAgentConfig) + assert isinstance(result, AgentaAgentTemplate) # AGENTS.md is the base preamble with the author's instructions appended after it. assert result.agents_md.startswith(AGENTA_PREAMBLE) assert result.agents_md.endswith("My project rules.") @@ -149,7 +151,7 @@ def test_agenta_forces_platform_skill_on_a_skill_less_config(make_env): # template's `_agenta` embed dropped) still carries the platform skill on every run. harness = AgentaHarness(make_env(supported=[HarnessType.AGENTA])) config = _session_config( - agent=AgentConfig(instructions="My project rules.", model="m", skills=[]) + agent=AgentTemplate(instructions="My project rules.", model="m", skills=[]) ) result = harness._to_harness_config(config) @@ -163,7 +165,7 @@ def test_agenta_does_not_duplicate_an_already_present_platform_skill(make_env): harness = AgentaHarness(make_env(supported=[HarnessType.AGENTA])) existing = GETTING_STARTED_WITH_AGENTA_SKILL.model_dump(mode="json") config = _session_config( - agent=AgentConfig(instructions="hi", model="m", skills=[existing]) + agent=AgentTemplate(instructions="hi", model="m", skills=[existing]) ) result = harness._to_harness_config(config) @@ -199,7 +201,7 @@ def test_agenta_forces_tools_without_duplicates(make_env): def test_agenta_passes_through_user_pi_options(make_env): harness = AgentaHarness(make_env(supported=[HarnessType.AGENTA])) - agent = AgentConfig( + agent = AgentTemplate( instructions="hi", harness_kwargs={ "pi_core": {"system": "You are Pi.", "append_system": "Be terse."} @@ -242,7 +244,7 @@ def test_claude_drops_builtins_and_warns(make_env, monkeypatch): result = harness._to_harness_config(config) - assert isinstance(result, ClaudeAgentConfig) + assert isinstance(result, ClaudeAgentTemplate) assert not hasattr(result, "builtin_tools") # Claude has no built-in tools at all assert result.custom_tools[0]["name"] == "t" assert result.permission_policy == "deny" # Claude carries the policy @@ -257,7 +259,7 @@ def test_claude_carries_skills_for_project_local_materialization(make_env): "body": "Read the changelog, then write notes.", } config = _session_config( - agent=AgentConfig(instructions="hi", model="m", skills=[skill]) + agent=AgentTemplate(instructions="hi", model="m", skills=[skill]) ) result = harness._to_harness_config(config) @@ -296,7 +298,7 @@ def test_claude_threads_options_and_renders_settings_file(make_env): }, "pi_core": {"system": "ignored for Claude"}, } - agent = AgentConfig(instructions="hi", model="m", harness_kwargs=options) + agent = AgentTemplate(instructions="hi", model="m", harness_kwargs=options) result = harness._to_harness_config(_session_config(agent=agent)) diff --git a/sdks/python/oss/tests/pytest/unit/agents/test_harness_identity.py b/sdks/python/oss/tests/pytest/unit/agents/test_harness_identity.py index 1a3f050ef7..acd79feb0b 100644 --- a/sdks/python/oss/tests/pytest/unit/agents/test_harness_identity.py +++ b/sdks/python/oss/tests/pytest/unit/agents/test_harness_identity.py @@ -1,6 +1,6 @@ """The harness interface identity: a versioned slug + display name per harness. -The harness in the agent_config interface is structured as a slug (the repo's +The harness in the agent_template interface is structured as a slug (the repo's ``agenta:...:v0`` grammar, mirroring ``agenta:builtin:agent:v0``) plus a display name, built from one SDK source (``HARNESS_IDENTITIES``). The stored/wire harness VALUE stays the bare enum string, so the runtime selector and the golden wire contract are unchanged; only the interface @@ -34,11 +34,11 @@ def test_identity_value_is_the_bare_harness_string(): assert values == {"pi_core", "pi_agenta", "claude"} -def test_agent_config_harness_field_carries_enum_and_oneOf_from_the_registry(): - # The agent_config catalog type's harness field carries BOTH a flat `enum` (back-compat for +def test_agent_template_harness_field_carries_enum_and_oneOf_from_the_registry(): + # The agent_template catalog type's harness field carries BOTH a flat `enum` (back-compat for # every `schema.enum` consumer) and a `oneOf` of `{const, title, x-ag-harness-slug}` built from # the same registry, so the playground shows the display name + slug while writing the bare value. - harness = CATALOG_TYPES["agent_config"]["properties"]["harness"] + harness = CATALOG_TYPES["agent-template"]["properties"]["harness"] assert harness["type"] == "string" assert harness["default"] == "pi_core" @@ -55,5 +55,5 @@ def test_agent_config_harness_field_carries_enum_and_oneOf_from_the_registry(): def test_harness_oneOf_const_values_match_the_enum(): # The `oneOf` consts and the flat `enum` describe the same value set, so a control reading # either shape offers the same harnesses. - harness = CATALOG_TYPES["agent_config"]["properties"]["harness"] + harness = CATALOG_TYPES["agent-template"]["properties"]["harness"] assert [entry["const"] for entry in harness["oneOf"]] == harness["enum"] diff --git a/sdks/python/oss/tests/pytest/unit/agents/test_wire_contract.py b/sdks/python/oss/tests/pytest/unit/agents/test_wire_contract.py index bd6803d8c8..b966d7ef87 100644 --- a/sdks/python/oss/tests/pytest/unit/agents/test_wire_contract.py +++ b/sdks/python/oss/tests/pytest/unit/agents/test_wire_contract.py @@ -20,12 +20,12 @@ import pytest from agenta.sdk.agents import ( - AgentaAgentConfig, - ClaudeAgentConfig, + AgentaAgentTemplate, + ClaudeAgentTemplate, Endpoint, HarnessType, Message, - PiAgentConfig, + PiAgentTemplate, ResolvedConnection, RunContext, RunContextReference, @@ -113,7 +113,7 @@ def _pi_payload(): - config = PiAgentConfig( + config = PiAgentTemplate( agents_md="You are a helpful assistant.", model="openai-codex/gpt-5.5", builtin_tools=["read", "write"], @@ -158,7 +158,7 @@ def _pi_payload(): def _claude_payload(): - config = ClaudeAgentConfig( + config = ClaudeAgentTemplate( agents_md="You are a helpful assistant.", model="claude-sonnet-4-6", custom_tools=[dict(_CUSTOM_TOOL)], @@ -187,7 +187,7 @@ def _claude_payload(): def _agenta_payload(): - config = AgentaAgentConfig( + config = AgentaAgentTemplate( agents_md="Agenta preamble + project rules.", model="gpt-5.5", builtin_tools=["read", "bash"], @@ -218,7 +218,7 @@ def test_request_to_wire_agenta_carries_skills_and_pi_shape(): def test_request_to_wire_skills_ride_their_own_seam_not_tools(): # Skills are emitted by `wire_skills`, not folded into the tool wire. - config = PiAgentConfig(skills=[dict(_SKILL)]) + config = PiAgentTemplate(skills=[dict(_SKILL)]) assert "skills" not in config.wire_tools() assert config.wire_skills() == {"skills": [SkillConfig(**_SKILL).to_wire()]} @@ -228,7 +228,7 @@ def test_request_to_wire_omits_skills_when_none(): payload = request_to_wire( harness=HarnessType.PI, sandbox="local", - config=PiAgentConfig(), + config=PiAgentTemplate(), messages=[Message(role="user", content="hi")], ) assert "skills" not in payload @@ -287,7 +287,7 @@ def test_request_to_wire_omits_run_context_when_none(): payload = request_to_wire( harness=HarnessType.PI, sandbox="local", - config=PiAgentConfig(), + config=PiAgentTemplate(), messages=[Message(role="user", content="hi")], ) assert "runContext" not in payload @@ -299,7 +299,7 @@ def test_request_to_wire_omits_run_context_when_empty(): payload = request_to_wire( harness=HarnessType.PI, sandbox="local", - config=PiAgentConfig(), + config=PiAgentTemplate(), messages=[Message(role="user", content="hi")], run_context=RunContext(), ) @@ -358,7 +358,7 @@ def test_request_to_wire_has_no_prompt_key(): payload = request_to_wire( harness=HarnessType.PI, sandbox="local", - config=PiAgentConfig(), + config=PiAgentTemplate(), messages=[Message(role="user", content="hi")], ) assert "prompt" not in payload @@ -379,7 +379,7 @@ def test_request_to_wire_carries_resolved_connection_non_secret_descriptor(): # resolved `model` overrides the config-build `model`, `provider`/`deployment`/ # `credentialMode`/`endpoint.baseUrl` ride the wire, and the secret `key` NEVER does (it # rides `secrets`; `env` is masked from the wire by `ResolvedConnection.to_wire`). - config = PiAgentConfig( + config = PiAgentTemplate( model="openai/gpt-5.5", # the config-build model resolved_connection=ResolvedConnection( provider="openai", @@ -415,7 +415,7 @@ def test_request_to_wire_carries_resolved_connection_non_secret_descriptor(): def test_request_to_wire_omits_resolved_connection_when_none(): # No resolved connection -> no resolved-connection keys, so a config without one is # byte-identical to before (the golden contract; the golden fixtures set none). - config = PiAgentConfig(model="gpt-5.5") + config = PiAgentTemplate(model="gpt-5.5") payload = request_to_wire( harness=HarnessType.PI, sandbox="local", @@ -435,7 +435,7 @@ def test_pi_permission_policy_is_always_auto(): payload = request_to_wire( harness=HarnessType.PI, sandbox="local", - config=PiAgentConfig(), + config=PiAgentTemplate(), messages=[Message(role="user", content="hi")], ) assert payload["permissionPolicy"] == "auto" @@ -526,7 +526,7 @@ def test_request_to_wire_carries_code_client_and_mcp_specs(): # The three-axes surface reaches the wire intact: a code spec keeps its executor fields # (kind/runtime/code/env) and the orthogonal axes (needsApproval/render); a client spec # has no callRef; user MCP servers ride `mcpServers`. - config = PiAgentConfig( + config = PiAgentTemplate( custom_tools=[ { "name": "calc", @@ -589,7 +589,7 @@ def test_request_to_wire_omits_mcp_servers_when_none(): payload = request_to_wire( harness=HarnessType.PI, sandbox="local", - config=PiAgentConfig(), + config=PiAgentTemplate(), messages=[Message(role="user", content="hi")], ) assert "mcpServers" not in payload @@ -601,7 +601,7 @@ def test_request_to_wire_omits_sandbox_permission_when_none(): payload = request_to_wire( harness=HarnessType.PI, sandbox="local", - config=PiAgentConfig(), + config=PiAgentTemplate(), messages=[Message(role="user", content="hi")], ) assert "sandboxPermission" not in payload @@ -613,7 +613,7 @@ def test_request_to_wire_omits_harness_files_when_none(): payload = request_to_wire( harness=HarnessType.CLAUDE, sandbox="local", - config=ClaudeAgentConfig(), + config=ClaudeAgentTemplate(), messages=[Message(role="user", content="hi")], ) assert "harnessFiles" not in payload @@ -623,7 +623,7 @@ def test_request_to_wire_pi_renders_no_harness_files_from_its_options(): # The per-harness translation is now in Python and only the claude config renders files; a Pi # config carrying options (even a `claude` slice that is never its concern) emits no # `harnessFiles`. The raw options map no longer rides the wire. - config = PiAgentConfig( + config = PiAgentTemplate( harness_kwargs={ "pi": {"system": "You are Pi."}, "claude": {"permissions": {"default_mode": "plan"}}, @@ -645,7 +645,7 @@ def test_request_to_wire_claude_renders_settings_from_options_and_boundaries(): # permissions slice with the Layer-2 sandbox derivation and Layer-3 MCP permissions into one # `.claude/settings.json` file. network:off -> WebFetch/WebSearch deny; an `ask` MCP server -> # `mcp__` ask. The author's deny keeps its position; derived rules append (deduped). - config = ClaudeAgentConfig( + config = ClaudeAgentTemplate( sandbox_permission=SandboxPermission(network={"mode": "off"}), harness_kwargs={"claude": {"permissions": {"default_mode": "plan"}}}, mcp_servers=[ @@ -677,7 +677,7 @@ def test_request_to_wire_claude_renders_settings_from_options_and_boundaries(): def test_request_to_wire_carries_sandbox_permission_allowlist(): # The allowlist mode rides the wire with its CIDR ranges and the default enforcement. - config = PiAgentConfig( + config = PiAgentTemplate( sandbox_permission=SandboxPermission( network={"mode": "allowlist", "allowlist": ["10.0.0.0/8"]}, ) diff --git a/sdks/python/oss/tests/pytest/unit/agents/test_wire_models.py b/sdks/python/oss/tests/pytest/unit/agents/test_wire_models.py index 4c9d75f833..596ddbf0ad 100644 --- a/sdks/python/oss/tests/pytest/unit/agents/test_wire_models.py +++ b/sdks/python/oss/tests/pytest/unit/agents/test_wire_models.py @@ -42,7 +42,7 @@ def test_run_contract_ships_in_the_sdk_catalog(): # The exported JSON interface lives in the SDK alongside the other catalog types, so a - # client / the playground / `/inspect` can resolve it the same way as `agent_config`. + # client / the playground / `/inspect` can resolve it the same way as `agent-template`. assert "run_request" in CATALOG_TYPES assert "run_result" in CATALOG_TYPES assert CATALOG_TYPES["run_request"]["x-ag-type"] == "run_request" diff --git a/sdks/python/oss/tests/pytest/unit/test_skill_config_catalog.py b/sdks/python/oss/tests/pytest/unit/test_skill_config_catalog.py index cd24afc9c0..1ed7ec8895 100644 --- a/sdks/python/oss/tests/pytest/unit/test_skill_config_catalog.py +++ b/sdks/python/oss/tests/pytest/unit/test_skill_config_catalog.py @@ -36,11 +36,11 @@ def test_skill_config_schema_shape(): assert set(file_item["properties"]) == {"path", "content", "executable"} -def test_agent_config_catalog_exposes_skills_as_inline_or_embed_union(): - agent_config = CATALOG_TYPES["agent_config"] +def test_agent_template_catalog_exposes_skills_as_inline_or_embed_union(): + agent_template = CATALOG_TYPES["agent-template"] - assert "skills" in agent_config["properties"] - skills_item = agent_config["properties"]["skills"]["items"] + assert "skills" in agent_template["properties"] + skills_item = agent_template["properties"]["skills"]["items"] # Each entry is a union: an inline skill_config package, or an @ag.embed reference. variants = skills_item["anyOf"] @@ -53,8 +53,8 @@ def test_agent_config_catalog_exposes_skills_as_inline_or_embed_union(): assert embed["required"] == ["@ag.embed"] -def _base_agent_config() -> dict: - """The shape ``services/oss/src/agent/schemas.py::_DEFAULT_AGENT_CONFIG`` seeds, minus skills.""" +def _base_agent_template() -> dict: + """The shape ``services/oss/src/agent/schemas.py::_DEFAULT_AGENT_TEMPLATE`` seeds, minus skills.""" return { "agents_md": "hi", "model": "gpt-4o", @@ -70,11 +70,11 @@ def _base_agent_config() -> dict: } -def test_platform_default_agent_config_with_embed_skill_validates(): +def test_platform_default_agent_template_with_embed_skill_validates(): """The platform default ships an @ag.embed skill entry; the catalog schema must accept it.""" - agent_config = CATALOG_TYPES["agent_config"] + agent_template = CATALOG_TYPES["agent-template"] - config = _base_agent_config() + config = _base_agent_template() config["skills"] = [ { "@ag.embed": { @@ -86,13 +86,13 @@ def test_platform_default_agent_config_with_embed_skill_validates(): } ] - jsonschema.validate(config, agent_config) + jsonschema.validate(config, agent_template) def test_inline_skill_entry_validates(): - agent_config = CATALOG_TYPES["agent_config"] + agent_template = CATALOG_TYPES["agent-template"] - config = _base_agent_config() + config = _base_agent_template() config["skills"] = [ { "name": "release-notes", @@ -101,7 +101,7 @@ def test_inline_skill_entry_validates(): } ] - jsonschema.validate(config, agent_config) + jsonschema.validate(config, agent_template) # --- tools: @ag.embed (inline) + type:"reference" arms ----------------------- @@ -129,11 +129,11 @@ def _type_const(variant): return None -def test_agent_config_tools_accepts_embed_and_reference_arms(): +def test_agent_template_tools_accepts_embed_and_reference_arms(): """The tools field is a union: a concrete tool variant (incl. type:"reference", a workflow run server-side), or an @ag.embed (inline a client tool value).""" - agent_config = CATALOG_TYPES["agent_config"] - tools_item = agent_config["properties"]["tools"]["items"] + agent_template = CATALOG_TYPES["agent-template"] + tools_item = agent_template["properties"]["tools"]["items"] variants = list(_flatten_union(tools_item["anyOf"])) # The embed arm and the type:"reference" arm are present alongside the concrete tool variants. @@ -143,28 +143,28 @@ def test_agent_config_tools_accepts_embed_and_reference_arms(): assert has_reference, 'tools union must include a type:"reference" arm' -def test_agent_config_tools_accepts_platform_arm(): +def test_agent_template_tools_accepts_platform_arm(): """The tools union includes a type:"platform" arm (an existing Agenta endpoint exposed).""" - agent_config = CATALOG_TYPES["agent_config"] - tools_item = agent_config["properties"]["tools"]["items"] + agent_template = CATALOG_TYPES["agent-template"] + tools_item = agent_template["properties"]["tools"]["items"] variants = list(_flatten_union(tools_item["anyOf"])) has_platform = any(_type_const(v) == "platform" for v in variants) assert has_platform, 'tools union must include a type:"platform" arm' -def test_agent_config_with_platform_tool_validates(): - agent_config = CATALOG_TYPES["agent_config"] +def test_agent_template_with_platform_tool_validates(): + agent_template = CATALOG_TYPES["agent-template"] - config = _base_agent_config() + config = _base_agent_template() config["tools"] = [{"type": "platform", "op": "find_capabilities"}] - jsonschema.validate(config, agent_config) + jsonschema.validate(config, agent_template) -def test_agent_config_with_reference_tool_validates(): - agent_config = CATALOG_TYPES["agent_config"] +def test_agent_template_with_reference_tool_validates(): + agent_template = CATALOG_TYPES["agent-template"] - config = _base_agent_config() + config = _base_agent_template() config["tools"] = [ { "type": "reference", @@ -176,13 +176,13 @@ def test_agent_config_with_reference_tool_validates(): } ] - jsonschema.validate(config, agent_config) + jsonschema.validate(config, agent_template) -def test_agent_config_with_embed_tool_validates(): - agent_config = CATALOG_TYPES["agent_config"] +def test_agent_template_with_embed_tool_validates(): + agent_template = CATALOG_TYPES["agent-template"] - config = _base_agent_config() + config = _base_agent_template() config["tools"] = [ { "@ag.embed": { @@ -192,4 +192,4 @@ def test_agent_config_with_embed_tool_validates(): } ] - jsonschema.validate(config, agent_config) + jsonschema.validate(config, agent_template) diff --git a/services/oss/src/agent/app.py b/services/oss/src/agent/app.py index 8a07c788de..68d005a27b 100644 --- a/services/oss/src/agent/app.py +++ b/services/oss/src/agent/app.py @@ -2,7 +2,7 @@ Mirrors the chat/completion services: an Agenta app exposing ``/invoke`` and ``/inspect`` through ``ag.create_app`` + ``ag.workflow`` + ``ag.route``. The handler parses the request -into one neutral ``AgentConfig`` (``agenta.sdk.agents``), resolves tools (``tools``) and one +into one neutral ``AgentTemplate`` (``agenta.sdk.agents``), resolves tools (``tools``) and one least-privilege model connection (``resolve_connection``) server-side, threads the trace context (``tracing``), then runs one turn through a :class:`Harness` over a backend it picks from the config's run-selection fields, and records the run's usage. @@ -17,7 +17,7 @@ import agenta as ag from agenta.sdk.agents import ( - AgentConfig, + AgentTemplate, Backend, ConnectionResolutionError, Environment, @@ -64,17 +64,17 @@ log = get_module_logger(__name__) -def _default_agent_config() -> AgentConfig: - """The service's file defaults (AGENTS.md, model, tools) as a neutral AgentConfig.""" +def _default_agent_template() -> AgentTemplate: + """The service's file defaults (AGENTS.md, model, tools) as a neutral AgentTemplate.""" file_cfg = load_config() - return AgentConfig( + return AgentTemplate( instructions=file_cfg.agents_md, model=file_cfg.model, tools=file_cfg.tools, ) -def _agent_model_ref(agent_config: AgentConfig) -> Optional[ModelRef]: +def _agent_model_ref(agent_template: AgentTemplate) -> Optional[ModelRef]: """The structured model ref for the run, or ``None`` when no model is configured. Prefer the parsed ``model_ref`` (populated only when the config's ``model`` arrived as a @@ -82,10 +82,10 @@ def _agent_model_ref(agent_config: AgentConfig) -> Optional[ModelRef]: ``None`` means no model at all, in which case the harness uses its own default/login and no connection is resolved. """ - if agent_config.model_ref is not None: - return agent_config.model_ref - if isinstance(agent_config.model, str) and agent_config.model.strip(): - return ModelRef.coerce(agent_config.model) + if agent_template.model_ref is not None: + return agent_template.model_ref + if isinstance(agent_template.model, str) and agent_template.model.strip(): + return ModelRef.coerce(agent_template.model) return None @@ -189,7 +189,7 @@ async def _resolve_session_connection( return resolved -def select_backend(agent_config: AgentConfig) -> Backend: +def select_backend(agent_template: AgentTemplate) -> Backend: """Pick the backend for a run from the agent config's run-selection fields. The service always uses the sandbox-agent-backed runner. `AGENTA_AGENT_RUNNER_URL` @@ -198,7 +198,7 @@ def select_backend(agent_config: AgentConfig) -> Backend: it is a backend/environment concern that never enters ``SessionConfig``. """ return SandboxAgentBackend( - sandbox=agent_config.sandbox, + sandbox=agent_template.sandbox, url=runner_url(), cwd=str(runner_dir()), ) @@ -217,33 +217,35 @@ async def _agent( params = parameters or {} - agent_config = AgentConfig.from_params(params, defaults=_default_agent_config()) + agent_template = AgentTemplate.from_params( + params, defaults=_default_agent_template() + ) msgs = to_messages(messages or (inputs or {}).get("messages") or []) # Three independent resolutions (tools, MCP, the model's one connection), not one aggregate: # the boundary resolves; the backend later decides how each tool executes. - resolved_tools = await resolve_tools(agent_config.tools) - resolved_mcp = await resolve_mcp_servers(agent_config.mcp_servers) + resolved_tools = await resolve_tools(agent_template.tools) + resolved_mcp = await resolve_mcp_servers(agent_template.mcp_servers) # One least-privilege connection for the configured model. The connection rides the config # (inside `parameters`/`agent.model`); there is no new request field and no project id from # the body. project_id is filled server-side from the caller's auth on the resolve call, so # the client-side context leaves it None. - model_ref = _agent_model_ref(agent_config) + model_ref = _agent_model_ref(agent_template) resolved_connection: Optional[ResolvedConnection] = None secrets: Dict[str, str] = {} if model_ref is not None: ctx = RuntimeAuthContext( - harness=agent_config.harness, backend=agent_config.sandbox + harness=agent_template.harness, backend=agent_template.sandbox ) resolved_connection = await _resolve_session_connection(model_ref, ctx) secrets = resolved_connection.env session_config = SessionConfig( - agent=agent_config, + agent=agent_template, secrets=secrets, # the env compat alias the wire still reads resolved_connection=resolved_connection, - permission_policy=agent_config.permission_policy, + permission_policy=agent_template.permission_policy, trace=trace_context(), # The run's own context (trace + workflow identity), refreshed each turn and consumed only # by a tool's `call.context` binding at dispatch (direct-call tools, Phase 3a). The @@ -261,7 +263,7 @@ async def _agent( # three harnesses (pi_core, pi_agenta, claude). setup/cleanup own the backend lifecycle; # prompt/stream run one cold turn. harness = make_harness( - agent_config.harness, Environment(select_backend(agent_config)) + agent_template.harness, Environment(select_backend(agent_template)) ) # ONE path: always stream. The agent only ever runs the streaming transport; the per-call diff --git a/services/oss/src/agent/config.py b/services/oss/src/agent/config.py index 0f5814f6a9..90717bba93 100644 --- a/services/oss/src/agent/config.py +++ b/services/oss/src/agent/config.py @@ -1,6 +1,6 @@ -"""Hardcoded MVP agent config, read from ``services/agent/config``. +"""Static on-file agent template, read from ``services/agent/config``. -The config (AGENTS.md text, model, tools) lives in editable files so changing the +The template (AGENTS.md text, model, tools) lives in editable files so changing the agent does not need a code change. Paths can be overridden with env vars for Docker or alternate layouts. """ @@ -27,7 +27,7 @@ @dataclass -class AgentConfig: +class AgentTemplate: agents_md: str model: Optional[str] = None # Provider-agnostic tool references (WP-7). Each entry is either a plain string @@ -50,12 +50,12 @@ def runner_url() -> Optional[str]: def config_dir() -> Path: - """Directory holding AGENTS.md and agent.json.""" - override = os.getenv("AGENTA_AGENT_CONFIG_DIR") + """Directory holding the static on-file agent template (AGENTS.md and agent.json).""" + override = os.getenv("AGENTA_AGENT_TEMPLATE_DIR") return Path(override) if override else (_DEFAULT_AGENT_DIR / "config") -def load_config() -> AgentConfig: +def load_config() -> AgentTemplate: base = config_dir() # Read the editable AGENTS.md when present; otherwise fall back to the default @@ -75,4 +75,4 @@ def load_config() -> AgentConfig: model = meta.get("model") or DEFAULT_MODEL tools = meta.get("tools", []) or [] - return AgentConfig(agents_md=agents_md, model=model, tools=tools) + return AgentTemplate(agents_md=agents_md, model=model, tools=tools) diff --git a/services/oss/src/agent/schemas.py b/services/oss/src/agent/schemas.py index 61bb8637dc..ee70319f73 100644 --- a/services/oss/src/agent/schemas.py +++ b/services/oss/src/agent/schemas.py @@ -30,9 +30,9 @@ # The agent config element: one composite control the playground renders for the whole # agent config, instead of reusing `prompt-template` plus loose params. The field shape is -# the `agent_config` catalog type (AgentConfigSchema in agenta.sdk.utils.types), so this is a -# thin `x-ag-type-ref` the playground resolves against `/workflows/catalog/types/agent_config` -# and dispatches to the AgentConfigControl (web/packages/agenta-entity-ui/.../AgentConfigControl.tsx). +# the `agent-template` catalog type (AgentTemplateSchema in agenta.sdk.utils.types), so this is a +# thin `x-ag-type-ref` the playground resolves against `/workflows/catalog/types/agent_template` +# and dispatches to the AgentTemplateControl (web/packages/agenta-entity-ui/.../AgentTemplateControl.tsx). # The catalog type keeps the typed tools/mcp_servers shape in one place; this schema only # carries the default that the playground pre-fills. The agent handler reads it from # `parameters.agent` in app.py. @@ -48,24 +48,24 @@ # choices: the static default skill (inlined from the reserved slug) and the declared Layer-2 # sandbox boundary the playground pre-fills. The SDK builtin interface uses the same builder # without these, so a new default field changes one place. -_DEFAULT_AGENT_CONFIG = build_agent_v0_default( +_DEFAULT_AGENT_TEMPLATE = build_agent_v0_default( skill_slug=_DEFAULT_SKILL_SLUG, include_sandbox_permission=True, ) -AGENT_CONFIG_SCHEMA = { +AGENT_TEMPLATE_SCHEMA = { "type": "object", - "x-ag-type-ref": "agent_config", + "x-ag-type-ref": "agent-template", "title": "Agent", "description": "The agent's instructions, model, tools, MCP servers, and runtime.", - "default": _DEFAULT_AGENT_CONFIG, + "default": _DEFAULT_AGENT_TEMPLATE, } AGENT_PARAMETERS_SCHEMA = { "$schema": _SCHEMA, "type": "object", "additionalProperties": True, - "properties": {"agent": AGENT_CONFIG_SCHEMA}, + "properties": {"agent": AGENT_TEMPLATE_SCHEMA}, } # Outputs mirror inputs: an object with a `messages` field of type `messages` (NOT keyed by diff --git a/services/oss/tests/pytest/unit/agent/conftest.py b/services/oss/tests/pytest/unit/agent/conftest.py index 3ab70df8ad..8784e75cc9 100644 --- a/services/oss/tests/pytest/unit/agent/conftest.py +++ b/services/oss/tests/pytest/unit/agent/conftest.py @@ -70,7 +70,7 @@ class FakeBackend(Backend): """Echoes a fixed result, regardless of harness. Records lifecycle for assertions. Crucially it also records the *harness-shaped* config each ``create_session`` receives - (the ``PiAgentConfig`` / ``ClaudeAgentConfig`` / ``AgentaAgentConfig`` the harness + (the ``PiAgentTemplate`` / ``ClaudeAgentTemplate`` / ``AgentaAgentTemplate`` the harness produced). This is the backend boundary where per-harness translation surfaces, so a handler test can assert the response body is identical across harnesses *and* that the translated configs diverge as designed (Pi keeps built-ins and forces auto; Claude drops diff --git a/services/oss/tests/pytest/unit/agent/test_default_agent_config.py b/services/oss/tests/pytest/unit/agent/test_default_agent_template.py similarity index 84% rename from services/oss/tests/pytest/unit/agent/test_default_agent_config.py rename to services/oss/tests/pytest/unit/agent/test_default_agent_template.py index aa1e495327..eab25d0849 100644 --- a/services/oss/tests/pytest/unit/agent/test_default_agent_config.py +++ b/services/oss/tests/pytest/unit/agent/test_default_agent_template.py @@ -1,4 +1,4 @@ -"""The `agent_config` default has ONE source: `build_agent_v0_default` in the SDK. +"""The `agent-template` default has ONE source: `build_agent_v0_default` in the SDK. Before this, the default was hand-maintained in three places (the SDK builtin interface, the service `/inspect` schema, and the catalog field defaults), so a new default field had to be @@ -8,20 +8,20 @@ from __future__ import annotations -from agenta.sdk.agents import AgentConfig +from agenta.sdk.agents import AgentTemplate from agenta.sdk.engines.running.interfaces import agent_v0_interface -from agenta.sdk.utils.types import AgentConfigSchema, build_agent_v0_default +from agenta.sdk.utils.types import AgentTemplateSchema, build_agent_v0_default from oss.src.agent.schemas import AGENT_SCHEMAS, _DEFAULT_SKILL_SLUG def _inspect_agent_default() -> dict: - """The `agent_config` default the service advertises on `/inspect`.""" + """The `agent-template` default the service advertises on `/inspect`.""" return AGENT_SCHEMAS["parameters"]["properties"]["agent"]["default"] def _builtin_agent_default() -> dict: - """The `agent_config` default the SDK builtin interface (`agenta:builtin:agent:v0`) carries.""" + """The `agent-template` default the SDK builtin interface (`agenta:builtin:agent:v0`) carries.""" return agent_v0_interface.schemas.parameters["properties"]["agent"]["default"] @@ -41,14 +41,14 @@ def test_service_default_is_the_builder_plus_service_only_choices(): def test_inspect_default_parses_into_the_runtime_selection(): # The default the playground pre-fills on `/inspect` must parse cleanly into the same runtime - # values `AgentConfig.from_params` produces, so what the user sees is what the agent runs. The + # values `AgentTemplate.from_params` produces, so what the user sees is what the agent runs. The # `@ag.embed` skill resolves server-side before this parse, so the config-level round-trip is - # asserted on the non-skill fields plus the run-selection fields (now on `AgentConfig`). + # asserted on the non-skill fields plus the run-selection fields (now on `AgentTemplate`). inspect_default = _inspect_agent_default() no_skill = {k: v for k, v in inspect_default.items() if k != "skills"} params = {"agent": no_skill} - config = AgentConfig.from_params(params) + config = AgentTemplate.from_params(params) assert config.model == inspect_default["model"] assert config.instructions == inspect_default["agents_md"] @@ -84,4 +84,4 @@ def test_harness_default_is_pi_core_in_every_source(): assert build_agent_v0_default()["harness"] == "pi_core" assert _builtin_agent_default()["harness"] == "pi_core" assert _inspect_agent_default()["harness"] == "pi_core" - assert AgentConfigSchema.model_fields["harness"].default == "pi_core" + assert AgentTemplateSchema.model_fields["harness"].default == "pi_core" diff --git a/services/oss/tests/pytest/unit/agent/test_inspect_catalog_refs.py b/services/oss/tests/pytest/unit/agent/test_inspect_catalog_refs.py index 162c3b9320..d6fc6afe94 100644 --- a/services/oss/tests/pytest/unit/agent/test_inspect_catalog_refs.py +++ b/services/oss/tests/pytest/unit/agent/test_inspect_catalog_refs.py @@ -2,7 +2,7 @@ The agent self-describes its interface in ``AGENT_SCHEMAS`` (``services/oss/src/agent/schemas.py``) instead of registering a static SDK interface. Its input/output/parameter schemas carry -``x-ag-type-ref`` markers (``messages``, ``message``, ``agent_config``) that the playground +``x-ag-type-ref`` markers (``messages``, ``message``, ``agent-template``) that the playground resolves against ``GET /workflows/catalog/types/{type}`` to pick a control. That endpoint resolves a marker via ``CATALOG_TYPES`` (``agenta.sdk.utils.types``): the router calls ``get_workflow_catalog_type(ag_type=...)``, which is ``CATALOG_TYPES.get(ag_type)`` and 404s @@ -25,8 +25,8 @@ # (not just renaming it to something unresolvable) is caught too. Update this set # deliberately when the agent interface changes. # `message` (singular) is gone with the keyed-outputs surface: the agent's one output schema -# is the plural `messages` list. Inputs use `messages`, parameters use `agent_config`. -EXPECTED_INSPECT_REFS = {"messages", "agent_config"} +# is the plural `messages` list. Inputs use `messages`, parameters use `agent-template`. +EXPECTED_INSPECT_REFS = {"messages", "agent-template"} def _collect_type_refs(node: Any, acc: Set[str]) -> Set[str]: diff --git a/services/oss/tests/pytest/unit/agent/test_invoke_handler.py b/services/oss/tests/pytest/unit/agent/test_invoke_handler.py index ec6778c223..3e690696aa 100644 --- a/services/oss/tests/pytest/unit/agent/test_invoke_handler.py +++ b/services/oss/tests/pytest/unit/agent/test_invoke_handler.py @@ -10,7 +10,7 @@ import pytest from agenta.sdk.agents import ( - AgentConfig, + AgentTemplate, AgentResult, ConnectionNotFoundError, ConnectionResolutionError, @@ -74,7 +74,9 @@ async def _no_connection(*, model, context): ) monkeypatch.setattr(app, "select_backend", lambda selection: backend) monkeypatch.setattr( - app, "_default_agent_config", lambda: AgentConfig(instructions="x", model="m") + app, + "_default_agent_template", + lambda: AgentTemplate(instructions="x", model="m"), ) return recorded @@ -224,8 +226,8 @@ async def _failure(tools, **_kw): monkeypatch.setattr(app, "resolve_tools", _failure) monkeypatch.setattr( app, - "_default_agent_config", - lambda: AgentConfig( + "_default_agent_template", + lambda: AgentTemplate( tools=[ { "type": "gateway", @@ -291,7 +293,9 @@ async def _no_mcp(mcp_servers, **_kw): monkeypatch.setattr(app, "record_usage", lambda usage: None) monkeypatch.setattr(app, "select_backend", lambda selection: backend) monkeypatch.setattr( - app, "_default_agent_config", lambda: AgentConfig(instructions="x", model="m") + app, + "_default_agent_template", + lambda: AgentTemplate(instructions="x", model="m"), ) return built diff --git a/services/oss/tests/pytest/unit/agent/test_select_backend.py b/services/oss/tests/pytest/unit/agent/test_select_backend.py index e81e9ce550..ff0b4a308e 100644 --- a/services/oss/tests/pytest/unit/agent/test_select_backend.py +++ b/services/oss/tests/pytest/unit/agent/test_select_backend.py @@ -7,7 +7,7 @@ import pytest from agenta.sdk.agents import ( - AgentConfig, + AgentTemplate, AgentRunnerConfigurationError, SandboxAgentBackend, ) @@ -31,7 +31,7 @@ def _clean_env(monkeypatch, runner_wrapper: Path): def _sel(harness="pi_core", sandbox="local"): - return AgentConfig(harness=harness, sandbox=sandbox) + return AgentTemplate(harness=harness, sandbox=sandbox) @pytest.mark.parametrize("harness", ["pi_core", "pi_agenta", "claude"]) diff --git a/web/oss/src/components/AgentChatSlice/assets/agConfig.ts b/web/oss/src/components/AgentChatSlice/assets/agConfig.ts index 54269ef068..0ee4bb51ca 100644 --- a/web/oss/src/components/AgentChatSlice/assets/agConfig.ts +++ b/web/oss/src/components/AgentChatSlice/assets/agConfig.ts @@ -42,7 +42,7 @@ interface RevisionLike { data?: {parameters?: Record | null} | null } -export interface ResolvedAgentConfig { +export interface ResolvedAgentTemplate { ag_config: Record references: Record | null version: number | null @@ -80,7 +80,7 @@ function buildReferences(rev: RevisionLike): Record | null { return Object.keys(refs).length > 0 ? refs : null } -function fromRevision(rev: RevisionLike | null | undefined): ResolvedAgentConfig | null { +function fromRevision(rev: RevisionLike | null | undefined): ResolvedAgentTemplate | null { const params = rev?.data?.parameters if (!rev || !params || Object.keys(params).length === 0) return null return { @@ -90,7 +90,7 @@ function fromRevision(rev: RevisionLike | null | undefined): ResolvedAgentConfig } } -export function resolveAppAgConfig(appId: string | null | undefined): ResolvedAgentConfig | null { +export function resolveAppAgConfig(appId: string | null | undefined): ResolvedAgentTemplate | null { if (!appId) return null const query = getDefaultStore().get(workflowLatestRevisionQueryAtomFamily(appId)) return fromRevision(query?.data as RevisionLike | null | undefined) diff --git a/web/oss/src/components/Playground/Components/Menus/PlaygroundVariantHeaderMenu/index.tsx b/web/oss/src/components/Playground/Components/Menus/PlaygroundVariantHeaderMenu/index.tsx index 58749783f0..8818ed81a2 100644 --- a/web/oss/src/components/Playground/Components/Menus/PlaygroundVariantHeaderMenu/index.tsx +++ b/web/oss/src/components/Playground/Components/Menus/PlaygroundVariantHeaderMenu/index.tsx @@ -1,7 +1,7 @@ import {useCallback, useMemo} from "react" import {workflowMolecule} from "@agenta/entities/workflow" -import {agentConfigLayoutAtom, AGENT_CONFIG_LAYOUTS} from "@agenta/entity-ui/drill-in" +import {agentTemplateLayoutAtom, AGENT_TEMPLATE_LAYOUTS} from "@agenta/entity-ui/drill-in" import { playgroundController, isAgentModeAtomFamily, @@ -35,8 +35,8 @@ const PlaygroundVariantHeaderMenu: React.FC = // reads the same persisted atom. Agent mode derives from the backend is_agent flag // (via workflowType). Non-agent variants hide it. const isAgent = useAtomValue(isAgentModeAtomFamily(variantId || "")) - const layout = useAtomValue(agentConfigLayoutAtom) - const setLayout = useSetAtom(agentConfigLayoutAtom) + const layout = useAtomValue(agentTemplateLayoutAtom) + const setLayout = useSetAtom(agentTemplateLayoutAtom) // Stream (token-by-token) vs batch (one-shot) response channel for the agent run lane. const channelMode = useAtomValue(agentChannelModeAtom) const setChannelMode = useSetAtom(agentChannelModeAtom) @@ -69,7 +69,7 @@ const PlaygroundVariantHeaderMenu: React.FC = key: "view", type: "group" as const, label: "View", - children: AGENT_CONFIG_LAYOUTS.map((option) => ({ + children: AGENT_TEMPLATE_LAYOUTS.map((option) => ({ key: `view-${option.value}`, label: option.label, icon: diff --git a/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/AgentConfigControl.tsx b/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/AgentTemplateControl.tsx similarity index 99% rename from web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/AgentConfigControl.tsx rename to web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/AgentTemplateControl.tsx index 4b175e34e0..9616a14e5a 100644 --- a/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/AgentConfigControl.tsx +++ b/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/AgentTemplateControl.tsx @@ -1,17 +1,17 @@ /** - * AgentConfigControl + * AgentTemplateControl * * The agent playground's left config panel. It renders the whole agent config as a set * of collapsible accordion sections (Model & harness, Instructions, Tools, MCP servers, * Advanced), built on the reusable {@link ConfigAccordionSection} primitive so the same * pattern can roll out to other config surfaces. * - * Dispatched from `x-ag-type: "agent_config"` / `x-ag-type-ref: "agent_config"` (see + * Dispatched from `x-ag-type: "agent-template"` / `x-ag-type-ref: "agent-template"` (see * SchemaPropertyRenderer). It reuses the existing schema controls rather than inventing * new ones: the model selector (GroupedChoiceControl), the tool picker (ToolSelectorPopover * + ToolItemControl), the MCP server editor (McpServerItemControl), enum selects (harness, * sandbox, permission policy), and a textarea (agents_md). The field shape is the - * `agent_config` catalog type generated from the SDK model (AgentConfigSchema in + * `agent-template` catalog type generated from the SDK model (AgentTemplateSchema in * agenta.sdk.utils.types); the agent service ships a thin `x-ag-type-ref` the playground * resolves and reads back (services/oss/src/agent). * @@ -44,7 +44,7 @@ import {useAtomValue} from "jotai" import {useOptionalDrillIn} from "../components/MoleculeDrillInContext" -import {agentConfigLayoutAtom} from "./agentConfigLayout" +import {agentTemplateLayoutAtom} from "./agentTemplateLayout" import {ClaudePermissionsControl} from "./ClaudePermissionsControl" import {CodeEditor} from "./CodeEditor" import {ConfigItemDrawer, type ConfigItemView} from "./ConfigItemDrawer" @@ -73,7 +73,7 @@ import {ToolSelectorPopover, type ToolSelectionMeta} from "./ToolSelectorPopover import {parseGatewayFunctionName, type ToolObj} from "./toolUtils" import {WorkflowReferenceSelector} from "./WorkflowReferenceSelector" -export interface AgentConfigControlProps { +export interface AgentTemplateControlProps { schema?: SchemaProperty | null label?: string value?: Record | null @@ -439,14 +439,14 @@ function ItemRow({ ) } -export function AgentConfigControl({ +export function AgentTemplateControl({ schema, value, onChange, withTooltip, disabled, className, -}: AgentConfigControlProps) { +}: AgentTemplateControlProps) { const {gatewayTools, workflowReference} = useDrillInUI() const config = (value ?? {}) as Record @@ -487,8 +487,8 @@ export function AgentConfigControl({ // How the config sections are laid out: stacked accordion (default), tabs, or cards. // Layout is a global, persisted preference set from the variant header menu (see - // agentConfigLayout); the panel only reads it. - const layout = useAtomValue(agentConfigLayoutAtom) + // agentTemplateLayout); the panel only reads it. + const layout = useAtomValue(agentTemplateLayoutAtom) const props = (schema?.properties ?? {}) as Record // Update a single field of the agent config, leaving the rest intact. diff --git a/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/ClaudePermissionsControl.tsx b/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/ClaudePermissionsControl.tsx index e8d39807e3..f47cfd1854 100644 --- a/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/ClaudePermissionsControl.tsx +++ b/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/ClaudePermissionsControl.tsx @@ -8,7 +8,7 @@ * { default_mode?: "default"|"acceptEdits"|"plan"|"bypassPermissions", * allow: string[], deny: string[], ask: string[] } * - * It is rendered as a collapsed "advanced" section by AgentConfigControl and is hidden when the + * It is rendered as a collapsed "advanced" section by AgentTemplateControl and is hidden when the * harness is not Claude (nothing here applies to Pi, which never gates tool use). Non-destructive: * an author who touches nothing leaves `harness_kwargs` untouched. */ diff --git a/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/HarnessSelectControl.tsx b/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/HarnessSelectControl.tsx index 388b0a2df9..6a999a2640 100644 --- a/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/HarnessSelectControl.tsx +++ b/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/HarnessSelectControl.tsx @@ -1,7 +1,7 @@ /** * HarnessSelectControl * - * Harness picker for the agent config. The agent_config catalog schema ships `harness` as an + * Harness picker for the agent config. The agent_template catalog schema ships `harness` as an * enum (`pi_core` / `pi_agenta` / `claude`) PLUS a `oneOf` of `{const, title, * x-ag-harness-slug}` whose `title`s are the canonical display names (`Pi` / `Pi (Agenta)` / * `Claude Code`, from the backend's `HARNESS_IDENTITIES`). This control prefers that schema diff --git a/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/McpServerItemControl.tsx b/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/McpServerItemControl.tsx index dd73629e1b..880b955b86 100644 --- a/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/McpServerItemControl.tsx +++ b/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/McpServerItemControl.tsx @@ -6,7 +6,7 @@ * or a remote url), an optional tool allowlist, and the vault secret names the backend resolves * into its env at run time. The shape is open enough that a JSON editor is the pragmatic v1 — * the same approach ToolItemControl takes for tool definitions — with a name header and a delete - * control. The typed shape lives in the `agent_config` catalog type (AgentConfigSchema / + * control. The typed shape lives in the `agent-template` catalog type (AgentTemplateSchema / * McpServer in the SDK); this control just edits one entry of the `mcp_servers` array. */ import {memo, useCallback, useEffect, useRef, useState} from "react" diff --git a/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/SandboxPermissionControl.tsx b/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/SandboxPermissionControl.tsx index c3cb6764f1..7f62723de5 100644 --- a/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/SandboxPermissionControl.tsx +++ b/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/SandboxPermissionControl.tsx @@ -8,7 +8,7 @@ * filesystem?: "on" | "readonly" | "off", * enforcement: "strict" | "best_effort" } * - * The emitted `agent_config` schema exposes `sandbox_permission` as a nullable object, so the + * The emitted `agent-template` schema exposes `sandbox_permission` as a nullable object, so the * generic renderer can only offer a drill-in. This renders the four knobs as a real form: a * network-mode selector, a CIDR allowlist (only when mode = allowlist), an optional filesystem * selector, and an enforcement selector. Non-destructive: an unset value stays `null` (no declared diff --git a/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/SchemaPropertyRenderer.tsx b/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/SchemaPropertyRenderer.tsx index f38d9e7aed..c5d2c91935 100644 --- a/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/SchemaPropertyRenderer.tsx +++ b/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/SchemaPropertyRenderer.tsx @@ -19,7 +19,7 @@ import {formatLabel} from "@agenta/ui/drill-in" import {Typography} from "antd" import clsx from "clsx" -import {AgentConfigControl} from "./AgentConfigControl" +import {AgentTemplateControl} from "./AgentTemplateControl" import {BooleanToggleControl} from "./BooleanToggleControl" import {CodeEditorControl} from "./CodeEditorControl" import {EnumSelectControl} from "./EnumSelectControl" @@ -96,7 +96,7 @@ function getControlType( | "grouped_choice" | "feedback_config" | "fields_tags_editor" - | "agent_config" + | "agent-template" | "hidden" | "unknown" { if (forceType) return forceType @@ -127,8 +127,8 @@ function getControlType( if (xAgTypeRef === "code" || xAgType === "code") { return "code" } - if (xAgTypeRef === "agent_config" || xAgType === "agent_config") { - return "agent_config" + if (xAgTypeRef === "agent-template" || xAgType === "agent-template") { + return "agent-template" } // When schema is null, fall back to value-based detection @@ -427,11 +427,11 @@ export const SchemaPropertyRenderer = memo(function SchemaPropertyRenderer({ /> ) - case "agent_config": + case "agent-template": // Render the whole agent config (instructions, model, tools, runtime) as one // composite control that reuses the model selector, tool picker, and enums. return ( - | null} diff --git a/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/agentConfigLayout.ts b/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/agentTemplateLayout.ts similarity index 66% rename from web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/agentConfigLayout.ts rename to web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/agentTemplateLayout.ts index 90e343d65f..2f515956f0 100644 --- a/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/agentConfigLayout.ts +++ b/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/agentTemplateLayout.ts @@ -1,23 +1,23 @@ /** * Agent config-panel layout preference. * - * The agent config panel ({@link AgentConfigControl}) can render its sections as an accordion, + * The agent config panel ({@link AgentTemplateControl}) can render its sections as an accordion, * tabs, or cards. The chosen layout is a global, persisted UI preference rather than per-variant * state, so the selector can live in the variant header menu (away from the panel itself) while the * panel reads the same value. Persisted to localStorage so it survives reloads. */ import {atomWithStorage} from "jotai/utils" -export type AgentConfigLayout = "accordion" | "tabs" | "cards" +export type AgentTemplateLayout = "accordion" | "tabs" | "cards" /** The selectable layouts, in display order. Shared by the panel and the header-menu selector. */ -export const AGENT_CONFIG_LAYOUTS: {label: string; value: AgentConfigLayout}[] = [ +export const AGENT_TEMPLATE_LAYOUTS: {label: string; value: AgentTemplateLayout}[] = [ {label: "Accordion", value: "accordion"}, {label: "Tabs", value: "tabs"}, {label: "Cards", value: "cards"}, ] -export const agentConfigLayoutAtom = atomWithStorage( +export const agentTemplateLayoutAtom = atomWithStorage( "agenta:agent-config-layout", "accordion", ) diff --git a/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/connectionUtils.ts b/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/connectionUtils.ts index 3e65e1af32..36e5d7aafd 100644 --- a/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/connectionUtils.ts +++ b/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/connectionUtils.ts @@ -6,14 +6,14 @@ * `ModelRef` object — `{provider, model, params?, connection?: {mode, slug?}}` — never a free-text * string. The harness-filtered unified picker (provider + model + authentication + connection) is * the only way to set it, and it always produces a ModelRef. These helpers translate between the - * form fields the AgentConfigControl renders and that on-the-wire object. + * form fields the AgentTemplateControl renders and that on-the-wire object. * * The per-harness capability surface (which providers/models/connection-modes a harness can reach) * is published on the `/inspect` response `meta.harness_capabilities`; the frontend renders from it * via the passed-in `HarnessCapabilitiesMap` rather than a static FE copy. When the map is absent * (older agents, a standalone control) the helpers fall back permissively. * - * They live in their own module (not inline in AgentConfigControl) so the package unit tests can + * They live in their own module (not inline in AgentTemplateControl) so the package unit tests can * import and exercise them without a React harness. * * Design: docs/design/agent-workflows/projects/agent-model-picker/ (the picker UX + inspect model diff --git a/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/index.ts b/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/index.ts index 2700634954..ad76e3ac1b 100644 --- a/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/index.ts +++ b/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/index.ts @@ -82,8 +82,8 @@ export type {SandboxPermissionControlProps} from "./SandboxPermissionControl" export {ClaudePermissionsControl} from "./ClaudePermissionsControl" export type {ClaudePermissionsControlProps} from "./ClaudePermissionsControl" -export {AgentConfigControl} from "./AgentConfigControl" -export type {AgentConfigControlProps} from "./AgentConfigControl" +export {AgentTemplateControl} from "./AgentTemplateControl" +export type {AgentTemplateControlProps} from "./AgentTemplateControl" // Agent config redesign (drawer/accordion config view + ported backend-aligned controls). export {HarnessSelectControl} from "./HarnessSelectControl" @@ -102,8 +102,8 @@ export {McpServerFormView} from "./McpServerFormView" export type {McpServerFormViewProps} from "./McpServerFormView" export {SkillFormView} from "./SkillFormView" export type {SkillFormViewProps} from "./SkillFormView" -export {agentConfigLayoutAtom, AGENT_CONFIG_LAYOUTS} from "./agentConfigLayout" -export type {AgentConfigLayout} from "./agentConfigLayout" +export {agentTemplateLayoutAtom, AGENT_TEMPLATE_LAYOUTS} from "./agentTemplateLayout" +export type {AgentTemplateLayout} from "./agentTemplateLayout" // ============================================================================ // COMPOSITE CONTROLS diff --git a/web/packages/agenta-entity-ui/src/DrillInView/index.ts b/web/packages/agenta-entity-ui/src/DrillInView/index.ts index b9fd76f57f..0bc448e3b7 100644 --- a/web/packages/agenta-entity-ui/src/DrillInView/index.ts +++ b/web/packages/agenta-entity-ui/src/DrillInView/index.ts @@ -285,5 +285,5 @@ export type { } from "./SchemaControls" // Agent config layout preference (read by the variant header menu's View selector). -export {agentConfigLayoutAtom, AGENT_CONFIG_LAYOUTS} from "./SchemaControls" -export type {AgentConfigLayout} from "./SchemaControls" +export {agentTemplateLayoutAtom, AGENT_TEMPLATE_LAYOUTS} from "./SchemaControls" +export type {AgentTemplateLayout} from "./SchemaControls" diff --git a/web/packages/agenta-playground/src/state/execution/agentRequest.ts b/web/packages/agenta-playground/src/state/execution/agentRequest.ts index de5c6e153c..20866f6ae7 100644 --- a/web/packages/agenta-playground/src/state/execution/agentRequest.ts +++ b/web/packages/agenta-playground/src/state/execution/agentRequest.ts @@ -227,7 +227,7 @@ const hasAnswer = (message: unknown): boolean => { /** * Default the agent run-selection fields (`harness`/`sandbox`) onto the AGENT CONFIG - * (`parameters.agent`), not as top-level params siblings. They are part of one `AgentConfig` + * (`parameters.agent`), not as top-level params siblings. They are part of one `AgentTemplate` * now, so they belong inside the `agent` block. A value the resolved config already carries * always wins; the schema nests the config under `agent`, but a flat config (no `agent` key) * is still defaulted at the top level so a non-schema config keeps working.