diff --git a/docs/design/agent-workflows/documentation/adapters/pi.md b/docs/design/agent-workflows/documentation/adapters/pi.md index b53b0fe9fd..0f87d27311 100644 --- a/docs/design/agent-workflows/documentation/adapters/pi.md +++ b/docs/design/agent-workflows/documentation/adapters/pi.md @@ -10,7 +10,7 @@ pages first. This page assumes the relay and the wire contract. ## How Pi runs Pi runs over ACP, through sandbox-agent (`engines/sandbox_agent.ts`). The harness value -`pi_core` (plain Pi) and `pi_agenta` (Pi with Agenta's forced opinion) both map to the `pi` +`pi_core` (and the legacy `pi_agenta` spelling of a removed experiment) both map to the `pi` ACP agent. This is the one engine the runner has. The sandbox-agent daemon starts the `pi-acp` adapter, which starts the `pi` CLI. diff --git a/docs/design/agent-workflows/documentation/agent-configuration.md b/docs/design/agent-workflows/documentation/agent-configuration.md index db4f6acd02..86d61d47ba 100644 --- a/docs/design/agent-workflows/documentation/agent-configuration.md +++ b/docs/design/agent-workflows/documentation/agent-configuration.md @@ -104,7 +104,7 @@ Its fields and defaults: | `model` | `str` | `"gpt-5.5"` | `x-parameter: grouped_choice`, plain string | | `tools` | `List[ToolConfig]` | empty list | typed discriminated union | | `mcp_servers` | `List[MCPServerConfig]` | empty list | typed | -| `harness` | `Literal["pi_core","claude","pi_agenta"]` | `"pi_core"` | enum | +| `harness` | `Literal["pi_core","claude","codex"]` | `"pi_core"` | enum | | `sandbox` | `Literal["local","daytona"]` | `"local"` | enum | | `runner.permissions.default` | `Literal["allow","ask","deny","allow_reads"]` | `"allow_reads"` | enum, four modes | @@ -218,8 +218,8 @@ Legend: (a) catalog/schema, (b) SDK neutral config, (c) runtime. | model / provider | yes, `model: str` | yes, `Optional[str]` | wired to the runner | Loose string. No `ModelRef`, no provider enum. There is no separate provider field. | | tools | yes, strict list | yes, lenient coercion | wired, resolved to builtin names + tool specs | Entries strict, list lenient. The shipped default template fills it with Pi's four default built-ins (`read`, `bash`, `edit`, `write`); see [Tools](tools.md). | | mcp_servers | yes, strict list | yes | wired, resolved to runner MCP servers | Strict per entry. Claude supports external HTTP servers; Pi refuses them until its bridge exists. | -| skills | yes, embed/inline list | yes | wired | Author-settable (`SkillConfig` inline or `@ag.embed` references). The playground build-kit overlay embeds one skill, the `build-an-agent` playbook; the `pi_agenta` harness additionally force-unions `getting-started`. See below. | -| persona | no | no | wired but forced only | Not a config field. The Agenta harness hardcodes an append-system preamble. See below. | +| skills | yes, embed/inline list | yes | wired | Author-settable (`SkillConfig` inline or `@ag.embed` references). The playground build-kit overlay embeds one skill, the `build-an-agent` playbook. See below. | +| persona | no | no | removed | Not a config field. It was the removed `pi_agenta` harness's hardcoded append-system preamble. See below. | | agents_md | yes, `agents_md: str` | yes, as `instructions` | wired to `agentsMd` | The schema names it `agents_md`. The neutral config names it `instructions`. | | harness | yes, enum | yes, on `AgentConfig` | wired, picks the harness class | Enum-enforced. The runtime validates via `make_harness`. | | sandbox | yes, enum | yes, on `AgentConfig` | wired to the backend, absent from `SessionConfig` | Backend concern, not agent identity. | @@ -227,14 +227,11 @@ Legend: (a) catalog/schema, (b) SDK neutral config, (c) runtime. ## Notable gaps and quirks -`persona` is not author config; it is a runtime injection of the Agenta harness only (a forced -append-system string). `skills` used to work the same way, but is author config now: inline -`SkillConfig` packages or `@ag.embed` references the backend inlines before the runner sees -them. Two platform skills still arrive without the author writing anything: the playground -build-kit overlay embeds the `build-an-agent` playbook, and the `pi_agenta` harness -force-unions the `getting-started` skill (`AGENTA_FORCED_SKILLS` in -`sdks/python/agenta/sdk/agents/adapters/agenta_builtins.py`). Each is delivered exactly once. -Pi (`pi_core`) and Claude harnesses get no forced skills or persona. +`persona` is gone with the removed `pi_agenta` harness (it was that harness's forced +append-system string). `skills` is author config: inline `SkillConfig` packages or +`@ag.embed` references the backend inlines before the runner sees them. One platform skill +still arrives without the author writing anything: the playground build-kit overlay embeds +the `build-an-agent` playbook. No harness forces skills or a persona. Per-harness divergence is real in other ways, but not in permission enforcement anymore: the permission policy is now enforced on both Claude and Pi. Builtin tool names are dropped for diff --git a/docs/design/agent-workflows/documentation/architecture.md b/docs/design/agent-workflows/documentation/architecture.md index e07a9d7d09..3816fce81b 100644 --- a/docs/design/agent-workflows/documentation/architecture.md +++ b/docs/design/agent-workflows/documentation/architecture.md @@ -13,8 +13,8 @@ The runtime keeps two run choices configurable as fields on `AgentConfig` (`sdks/python/agenta/sdk/agents/dtos.py`): - **Harness:** which agent runs. Supported values are `pi_core`, `claude`, and experimental - `pi_agenta`. Default `pi_core`. `pi_core` and `pi_agenta` both drive the `pi` ACP agent; - `pi_agenta` is Pi with Agenta's forced opinion. + Default `pi_core`, which drives the `pi` ACP agent. (`pi_agenta`, a removed experiment, + is still read as `pi_core` so old stored configs run.) - **Sandbox:** where the run happens. Supported values are `local` and `daytona`. Default `local`. @@ -66,11 +66,12 @@ and tool credentials and passes them only in the scoped `/run` payloads that nee The deployed handler always uses `SandboxAgentBackend`. `select_backend` in `services/oss/src/agent/app.py:49` constructs `SandboxAgentBackend` for every run, regardless -of harness. So `pi_core`, `claude`, and `pi_agenta` all run through the sandbox-agent daemon +of harness. So `pi_core`, `claude`, and `codex` all run through the sandbox-agent daemon over ACP. The runner has one engine, the sandbox-agent ACP path (`engines/sandbox_agent.ts`). The -`harness` field on the `/run` request selects the ACP agent: `pi_core` and `pi_agenta` both +`harness` field on the `/run` request selects the ACP agent: `pi_core` (and the legacy +`pi_agenta` spelling) both map to the `pi` ACP agent, `claude` maps to `claude`. There is no engine selector on the wire. A legacy in-process Pi engine and an `InProcessPiBackend` adapter existed during the POC; both were removed. @@ -82,7 +83,7 @@ The SDK runtime models engines as `Backend` adapters | Backend | Status | Harnesses | Sandbox support | Notes | | --- | --- | --- | --- | --- | -| `SandboxAgentBackend` | Implemented | `pi_core`, `claude`, `pi_agenta` | `local`, `daytona` | The deployed path and the only engine. Drives `engines/sandbox_agent.ts`: starts the sandbox-agent daemon and an ACP adapter. `supported_harnesses` is `{pi_core, claude, pi_agenta}` (`adapters/sandbox_agent.py:121`). | +| `SandboxAgentBackend` | Implemented | `pi_core`, `claude`, `codex` | `local`, `daytona` | The deployed path and the only engine. Drives `engines/sandbox_agent.ts`: starts the sandbox-agent daemon and an ACP adapter. `supported_harnesses` is `{pi_core, claude, codex}` (`adapters/sandbox_agent.py`). | | `LocalBackend` | Not implemented | Intended: `pi_core`, `claude` | Local machine | Public class exists; `create_sandbox` and `create_session` raise `NotImplementedError` (`adapters/local.py:34`). | ## Harnesses @@ -90,15 +91,14 @@ The SDK runtime models engines as `Backend` adapters The SDK runtime models agent-specific behavior as `Harness` adapters (`sdks/python/agenta/sdk/agents/adapters/harnesses.py`). The Python class names are unchanged; only the harness string values changed (`HarnessType.PI` is `"pi_core"`, `HarnessType.AGENTA` -is `"pi_agenta"`, `HarnessType.CLAUDE` is `"claude"`). +is removed, `HarnessType.CLAUDE` is `"claude"`). | Harness | Value | Status | Notes | | --- | --- | --- | --- | | `PiHarness` | `pi_core` | Implemented | Native Pi tools, Pi prompt overrides, Pi tracing extension. Drives the `pi` ACP agent. | | `ClaudeHarness` | `claude` | Implemented | MCP-delivered tools, permission policy, runner-built tracing. No Pi built-in tools. Drives the `claude` ACP agent. | -| `AgentaHarness` | `pi_agenta` | Experimental | Pi with forced tools, forced skills, a base AGENTS.md preamble, and a persona. Drives the `pi` ACP agent plus forced extras. Content is still placeholder. | -The `pi_agenta` harness runs on the sandbox-agent path. The runner treats it as the `pi` ACP +The removed `pi_agenta` value still reads as the `pi` ACP agent and layers the forced skills and prompt extras on top (`services/agent/src/engines/sandbox_agent/run-plan.ts`). The QA matrix verified it on sandbox-agent local and Daytona (`projects/qa/findings.md`, F-002). diff --git a/docs/design/agent-workflows/documentation/skills.md b/docs/design/agent-workflows/documentation/skills.md index 4692b814fe..7ed1c5b4e8 100644 --- a/docs/design/agent-workflows/documentation/skills.md +++ b/docs/design/agent-workflows/documentation/skills.md @@ -67,7 +67,7 @@ says. `agent-workflows-qa` (shared) defines the test matrix for the agent runtime. Its three axes are the environment (sandbox-agent local, sandbox-agent Daytona, and the local SDK), the -harness (`pi_core`, `pi_agenta`, `claude`), and the capability under test. "Test with daytona, +harness (`pi_core`, `claude`, `codex`), and the capability under test. "Test with daytona, local pi, and claude, on both the SDK and the UI" is exactly a walk across these cells. Each test forces a capability with a token the model cannot guess, so a pass proves the capability ran. diff --git a/docs/docs/reference/agents/01-agent-configuration.mdx b/docs/docs/reference/agents/01-agent-configuration.mdx index e14f7acdd1..2d9c902187 100644 --- a/docs/docs/reference/agents/01-agent-configuration.mdx +++ b/docs/docs/reference/agents/01-agent-configuration.mdx @@ -59,7 +59,7 @@ Which providers, deployments, and connection modes each harness can reach is ser GET /api/workflows/catalog/harnesses/{harness} ``` -In the shipped table, `pi_core` and `pi_agenta` reach `openai`, `anthropic`, `gemini`, `mistral`, `groq`, `minimax`, `together_ai`, and `openrouter`, with deployments `direct` and `custom`. `claude` reaches `anthropic` only, with deployments `direct`, `custom`, `bedrock`, and `vertex_ai`, and selects its model by alias rather than by a `provider/model` string. +In the shipped table, `pi_core` reaches `openai`, `anthropic`, `gemini`, `mistral`, `groq`, `minimax`, `together_ai`, and `openrouter`, with deployments `direct` and `custom`. `claude` reaches `anthropic` only, with deployments `direct`, `custom`, `bedrock`, and `vertex_ai`, and selects its model by alias rather than by a `provider/model` string. ## tools @@ -195,18 +195,17 @@ Each entry is an inline skill package. See [Skills](/concepts/skills). | Field | Type | Required | Default | Description | |---|---|---|---|---| -| `kind` | `"pi_core"` \| `"pi_agenta"` \| `"claude"` \| `"codex"` | No | `"pi_core"` | Which coding agent to drive. | +| `kind` | `"pi_core"` \| `"claude"` \| `"codex"` | No | `"pi_core"` | Which coding agent to drive. | | `permissions` | object | No | see [harness.permissions](#harnesspermissions) | Tool-use gating posture, applied by harnesses that gate. | | `extras` | object | No | `{}` | Per-harness knobs passed through unchanged. For Pi, `system` replaces the base system prompt and `append_system` adds to it. Both are independent of `instructions.agents_md`. | | `kind` | Display name | Versioned slug | |---|---|---| | `pi_core` | Pi | `agenta:harness:pi_core:v0` | -| `pi_agenta` | Pi (Agenta) | `agenta:harness:pi_agenta:v0` | | `claude` | Claude Code | `agenta:harness:claude:v0` | | `codex` | Codex | `agenta:harness:codex:v0` | -`pi_agenta` runs the same engine as `pi_core` with Agenta's own skills, tools, and base instructions forced on. +`pi_agenta` was an experimental Pi variant, removed in August 2026. A stored config that still carries the value runs as `pi_core`. ### harness.permissions diff --git a/sdks/python/agenta/__init__.py b/sdks/python/agenta/__init__.py index e8f3cf9754..aab28899ea 100644 --- a/sdks/python/agenta/__init__.py +++ b/sdks/python/agenta/__init__.py @@ -56,7 +56,6 @@ # `agenta.Message` already names the prompt message type; import the agents one from # `agenta.sdk.agents` when needed. from .sdk.agents import ( # noqa: F401 - AgentaHarness, AgentTemplate, ClaudeHarness, Environment, diff --git a/sdks/python/agenta/sdk/agents/__init__.py b/sdks/python/agenta/sdk/agents/__init__.py index 915b9ad134..9951700dc1 100644 --- a/sdks/python/agenta/sdk/agents/__init__.py +++ b/sdks/python/agenta/sdk/agents/__init__.py @@ -21,7 +21,6 @@ """ from .adapters import ( - AgentaHarness, ClaudeHarness, CodexHarness, LocalBackend, @@ -55,7 +54,6 @@ UnsupportedProviderError, ) from .dtos import ( - AgentaAgentTemplate, AgentTemplate, AgentTemplateShapeError, Event, @@ -166,7 +164,6 @@ "PiAgentTemplate", "ClaudeAgentTemplate", "CodexAgentTemplate", - "AgentaAgentTemplate", "HarnessKind", "HarnessIdentity", "HARNESS_IDENTITIES", @@ -284,6 +281,5 @@ "PiHarness", "ClaudeHarness", "CodexHarness", - "AgentaHarness", "make_harness", ] diff --git a/sdks/python/agenta/sdk/agents/adapters/__init__.py b/sdks/python/agenta/sdk/agents/adapters/__init__.py index 590e8cded2..2a1e3dc6f1 100644 --- a/sdks/python/agenta/sdk/agents/adapters/__init__.py +++ b/sdks/python/agenta/sdk/agents/adapters/__init__.py @@ -2,7 +2,7 @@ - Backend adapters: ``SandboxAgentBackend`` (sandbox-agent over ACP), ``LocalBackend`` (standalone SDK runs; not yet implemented). -- Harness adapters: ``PiHarness``, ``ClaudeHarness``, ``CodexHarness``, ``AgentaHarness`` +- Harness adapters: ``PiHarness``, ``ClaudeHarness``, ``CodexHarness`` (+ ``make_harness``). - HTTP/browser protocol adapters live in subpackages, e.g. ``adapters.vercel``. @@ -10,7 +10,6 @@ """ from .harnesses import ( - AgentaHarness, ClaudeHarness, CodexHarness, PiHarness, @@ -25,6 +24,5 @@ "PiHarness", "ClaudeHarness", "CodexHarness", - "AgentaHarness", "make_harness", ] diff --git a/sdks/python/agenta/sdk/agents/adapters/agenta_builtins.py b/sdks/python/agenta/sdk/agents/adapters/agenta_builtins.py index dd346945e6..14239195b6 100644 --- a/sdks/python/agenta/sdk/agents/adapters/agenta_builtins.py +++ b/sdks/python/agenta/sdk/agents/adapters/agenta_builtins.py @@ -1,38 +1,23 @@ -"""The Agenta harness's forced defaults: the things ``AgentaHarness`` always applies. -(``ClaudeHarness`` shares the AGENTS.md preamble and forced platform skills; the persona -remains Pi-only — see :mod:`.harnesses`.) - -``AgentaHarness`` is Pi with an opinion. It is the same engine as :class:`PiHarness`, but -every run carries a fixed set of Agenta-shipped extras the author cannot turn off: - -- a base **persona** appended to Pi's system prompt (``AGENTA_FORCED_APPEND_SYSTEM``), -- a base **AGENTS.md preamble** the author's instructions are appended to (``AGENTA_PREAMBLE``), -- a set of **forced platform skills** (``AGENTA_FORCED_SKILLS``). - -The forced platform skills are the actually-forced part of "forced skills". The default agent -config template embeds the platform default skill by reserved ``__ag__*`` slug, but that embed -only rides the *default* template: a custom ``pi_agenta`` config that drops the embed would -otherwise lose the platform skill entirely. To make "forced" mean forced, ``AgentaHarness`` -unions ``AGENTA_FORCED_SKILLS`` into every run's skills via :func:`force_skills`, regardless of -what the author's config carries. The canonical skill content lives here (in the SDK, the lowest -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:`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. - -One exception to "the Agenta harness's defaults": :func:`gateway_guidance` and -:func:`compose_gateway_guidance` are cross-harness. Every harness gets the same two derived -gateway tools, so every harness gets their instructions, and all four adapters import from -here. They live beside :func:`compose_instructions` because that function has to interleave -them with ``AGENTA_PREAMBLE``, which no other module owns. +"""Agenta-shipped agent content: the platform skills and the cross-harness gateway guidance. + +Two things live here: + +- The **platform skills** (getting started, build-an-agent) as concrete inline packages. The + canonical skill content is defined here (the SDK, the lowest layer); the server-side + ``StaticWorkflowCatalog`` imports the same constants so the embed path and the catalog stay + one source of truth. +- The **gateway guidance** (:func:`gateway_guidance` / :func:`compose_gateway_guidance`), + which is cross-harness: every harness gets the same two derived gateway tools, so every + harness gets their instructions, and all adapters import it from here. + +The ``pi_agenta`` harness (Pi plus a forced Agenta overlay: a preamble, a persona, forced +skills) was an experiment and was removed on 2026-08-29; the overlay constants and helpers +went with it. """ from __future__ import annotations -from typing import List, Optional, Sequence +from typing import Optional, Sequence from ..flags import ordered_operations_enabled from ..skills import SkillFile, SkillTemplate @@ -46,29 +31,6 @@ # skills list it meant to append to. _ORDERED = ordered_operations_enabled() -# The base AGENTS.md preamble. The author's own ``instructions`` are appended after this, so -# the final AGENTS.md is ``AGENTA_PREAMBLE`` + the author's project conventions. -# -# TODO(product): replace this placeholder with the real Agenta AGENTS.md preamble. -AGENTA_PREAMBLE = """\ -# Agenta agent - -You are an agent running on the Agenta platform. The instructions below are Agenta's -baseline; the user's own instructions follow and take precedence where they are more -specific. - -- Prefer the tools and skills provided to you over guessing. -- When a skill matches the task, read its SKILL.md fully before acting. -- Keep answers grounded in what the tools and skills actually return.""" - -# The base persona, always appended to Pi's built-in system prompt (never replaces it). This -# is the "who the agent is" layer, distinct from the AGENTS.md project-context layer above. -# -# TODO(product): replace this placeholder with the real Agenta persona framing. -AGENTA_FORCED_APPEND_SYSTEM = """\ -You are an Agenta agent. Be precise, cite what your tools and skills return, and do not -fabricate results.""" - # Reserved slug of the platform default skill. The default agent config template embeds the # skill by this slug; the server-side StaticWorkflowCatalog resolves the slug to the # SkillTemplate below. Kept here so the catalogue and the forced path share one slug constant. @@ -157,14 +119,14 @@ "tools": [], "mcps": [], "skills": [], - "harness": { "kind": "pi_agenta" }, + "harness": { "kind": "pi_core" }, "runner": { "kind": "sidecar", "permissions": { "default": "allow_reads" } }, "sandbox": { "kind": "local" } } ``` -The example above shows one common setup; your own `harness` may be `pi_agenta`, `claude`, or -`pi_core`. Whatever it is, keep `harness`, `runner`, `sandbox`, and `llm` as they are unless the +The example above shows one common setup; your own `harness` may be `pi_core`, `claude`, or +`codex`. Whatever it is, keep `harness`, `runner`, `sandbox`, and `llm` as they are unless the user explicitly asks to change one. ## The fields you decide @@ -172,8 +134,7 @@ ### instructions `instructions.agents_md` — a Markdown string, your AGENTS.md: who you are and what you do. Write -only your own project conventions here — the platform supplies its own baseline framing (on -`pi_agenta` and `claude`, a fixed Agenta preamble is prepended automatically). One or two +only your own project conventions here. One or two sentences for a simple agent; an explicit numbered procedure for a multi-tool or scheduled one (see the instruction-writing section of SKILL.md). @@ -183,7 +144,7 @@ to change the model, provider, or connection. The rules below matter only when they do ask: - `model` — the model. How you NAME it depends on the harness (this is the trap): - - `pi_core` / `pi_agenta`: a real model id, e.g. `gpt-5.5` or `anthropic/claude-...` + - `pi_core`: a real model id, e.g. `gpt-5.5` or `anthropic/claude-...` (provider/id selection). - `claude`: an alias — `default`, `sonnet`, `opus`, or `haiku` — never a raw model id. - `provider` — the provider family (`openai`, `anthropic`, ...); inferred from the model string @@ -299,7 +260,7 @@ ## The execution parts (keep as-is unless asked) -- `harness` — `{ "kind": "pi_core" | "pi_agenta" | "claude", "permissions": {...}, "extras": +- `harness` — `{ "kind": "pi_core" | "claude" | "codex", "permissions": {...}, "extras": {...} }`. `permissions` is `{ "default_mode": "default"|"acceptEdits"|"plan"| "bypassPermissions", "allow": [...], "ask": [...], "deny": [...] }`. The three rule lists name tools that run without asking, that ask first, and that are never allowed to run; each entry is @@ -405,7 +366,7 @@ - `harness.kind: "claude"` paired with a non-Anthropic `provider`. Claude reaches `anthropic` only. Bites at RUN time: the run's Model & Harness never resolves and the agent never runs. - A raw model id on the `claude` harness (Claude selects by alias) or an alias like `sonnet` on a - `pi_core`/`pi_agenta` harness (Pi selects by provider/id). Bites silently: the run falls back to + `pi_core` harness (Pi selects by provider/id). Bites silently: the run falls back to a default model with no error. Only `test_run`'s `resolved` block shows the fallback. - Naming an `@ag.embed` entry with a selector. An embed has no key, so no operation can address it. Leave those entries where they are. @@ -589,7 +550,7 @@ - `harness.kind: "claude"` paired with a non-Anthropic `provider`. Claude reaches `anthropic` only. Bites at RUN time: the run's Model & Harness never resolves and the agent never runs. - A raw model id on the `claude` harness (Claude selects by alias) or an alias like `sonnet` on a - `pi_core`/`pi_agenta` harness (Pi selects by provider/id). Bites silently: the run falls back + `pi_core` harness (Pi selects by provider/id). Bites silently: the run falls back to a default model with no error. Only `test_run`'s `resolved` block shows the fallback. - Sending a short `tools`/`skills`/`mcps` list. Bites on the NEXT run: lists replace wholesale, so every entry you left out is gone. @@ -1177,10 +1138,6 @@ ], ) -# Platform skills every pi_agenta run carries, regardless of the author's config. These are the -# actually-forced skills (see module docstring); unioned in by `force_skills`. -AGENTA_FORCED_SKILLS: List[SkillTemplate] = [GETTING_STARTED_WITH_AGENTA_SKILL] - def _join(*parts: Optional[str]) -> Optional[str]: """Join the non-empty parts with a blank line, or ``None`` when nothing remains.""" @@ -1238,34 +1195,3 @@ def compose_gateway_guidance( AGENTS.md is purely authored. """ return _join(gateway_guidance(integration_names), user) - - -def compose_instructions( - user: Optional[str], - integration_names: Sequence[str] = (), -) -> Optional[str]: - """The AGENTS.md the Agenta harness ships: the base preamble, then the gateway guidance - when the agent has a connection, then the author's instructions.""" - return _join(AGENTA_PREAMBLE, compose_gateway_guidance(user, integration_names)) - - -def compose_append_system(user: Optional[str]) -> Optional[str]: - """The ``append_system`` the harness ships: the forced base persona with the author's own - ``append_system`` appended after it.""" - return _join(AGENTA_FORCED_APPEND_SYSTEM, user) - - -def force_skills(skills: List[SkillTemplate]) -> List[SkillTemplate]: - """Union the author's skills with the forced platform skills, de-duplicated by name. - - The author's skills come first and win on a name clash (a config that already carries the - resolved platform skill — e.g. via the default template's embed — is not doubled), then any - forced platform skill not already present is appended. This is what makes the ``_agenta`` - platform skill actually forced on a custom ``pi_agenta`` config that drops the embed.""" - seen = {skill.name for skill in skills} - out: List[SkillTemplate] = list(skills) - for forced in AGENTA_FORCED_SKILLS: - if forced.name not in seen: - seen.add(forced.name) - out.append(forced) - return out diff --git a/sdks/python/agenta/sdk/agents/adapters/harnesses.py b/sdks/python/agenta/sdk/agents/adapters/harnesses.py index e1764d1c11..f0dfe700e2 100644 --- a/sdks/python/agenta/sdk/agents/adapters/harnesses.py +++ b/sdks/python/agenta/sdk/agents/adapters/harnesses.py @@ -9,11 +9,9 @@ shared permission plan. - **claude** has no built-in tools (they are a Pi concept), delivers tools over MCP, and receives the same runner permission plan. -- **pi_agenta** is Pi with an opinion: the same engine and config shape, plus a base AGENTS.md - preamble and a persona (see :mod:`.agenta_builtins`). - Skills ride the neutral config as resolved inline packages. Pi and Agenta install them - through Pi skill dirs; Claude carries them so the runner can write project-local - `.claude/skills` packages. Seeding platform default skills is a separate workstream. +- Skills ride the neutral config as resolved inline packages. Pi installs them through Pi + skill dirs; Claude carries them so the runner can write project-local `.claude/skills` + packages. Seeding platform default skills is a separate workstream. The backend below stays pure plumbing; this layer owns the harness knowledge. """ @@ -23,7 +21,6 @@ from typing import Any, Dict, List, Type from ..dtos import ( - AgentaAgentTemplate, ClaudeAgentTemplate, CodexAgentTemplate, HarnessKind, @@ -32,12 +29,7 @@ ) from ..interfaces import Environment, Harness from ..tools.models import ToolSpec, coerce_tool_spec -from .agenta_builtins import ( - compose_append_system, - compose_gateway_guidance, - compose_instructions, - force_skills, -) +from .agenta_builtins import compose_gateway_guidance def _opt_str(value: Any) -> Any: @@ -145,49 +137,10 @@ def _to_harness_config(self, config: SessionConfig) -> CodexAgentTemplate: ) -class AgentaHarness(Harness): - """Pi with an Agenta opinion. Same engine as :class:`PiHarness`, but every run carries the - forced Agenta extras (see :mod:`.agenta_builtins`): a base AGENTS.md preamble the author's - instructions are appended to, and a forced persona ``append_system``. The - author's own Pi ``harness.extras`` (``system`` / ``append_system``) still apply, layered - after the forced bits. The author's resolved inline skills ride the neutral config, and the - forced platform skill(s) are unioned in (de-duped by name) so a custom config that drops the - default template's ``_agenta`` embed still carries the platform skill.""" - - harness_type = HarnessKind.AGENTA - - def _to_harness_config(self, config: SessionConfig) -> AgentaAgentTemplate: - # The author's Pi options still apply; the pi_agenta harness reads the same harness - # `extras` as PiHarness (it drives Pi) and layers its forced extras on top. - extras = config.agent.harness_extras - return AgentaAgentTemplate( - agents_md=compose_instructions( - config.agent.instructions, config.gateway_integration_names - ), - model=config.agent.model, - # See PiHarness: thread the structured ref so a named custom connection's {mode, slug} - # reaches the /run wire and the runner can build its models.json plan. - model_ref=config.agent.model_ref, - resolved_connection=config.resolved_connection, - tool_specs=list(config.tool_specs), - tool_callback=config.tool_callback, - mcp_servers=list(config.mcp_servers), - # Force the platform skill(s) into every run, de-duped by name. A custom config that - # drops the default template's `_agenta` embed still gets the platform skill. - skills=force_skills(list(config.agent.skills)), - sandbox_permission=config.agent.sandbox_permission, - permission_default=config.permission_default, - harness_permissions=config.agent.harness_permissions, - system=_opt_str(extras.get("system")), - append_system=compose_append_system(_opt_str(extras.get("append_system"))), - ) - - _HARNESSES: Dict[HarnessKind, Type[Harness]] = { HarnessKind.PI: PiHarness, HarnessKind.CLAUDE: ClaudeHarness, HarnessKind.CODEX: CodexHarness, - HarnessKind.AGENTA: AgentaHarness, } diff --git a/sdks/python/agenta/sdk/agents/adapters/sandbox_agent.py b/sdks/python/agenta/sdk/agents/adapters/sandbox_agent.py index 5ffa4a55f9..a7f654bda5 100644 --- a/sdks/python/agenta/sdk/agents/adapters/sandbox_agent.py +++ b/sdks/python/agenta/sdk/agents/adapters/sandbox_agent.py @@ -125,14 +125,13 @@ def stream(self, messages: Sequence[Message]) -> AgentStream: class SandboxAgentBackend(Backend): - """The sandbox-agent engine: a harness over ACP through the TS runner. Pi, Claude, Codex, and Agenta.""" + """The sandbox-agent engine: a harness over ACP through the TS runner. Pi, Claude, and Codex.""" supported_harnesses = frozenset( { HarnessKind.PI, HarnessKind.CLAUDE, HarnessKind.CODEX, - HarnessKind.AGENTA, } ) diff --git a/sdks/python/agenta/sdk/agents/capabilities.py b/sdks/python/agenta/sdk/agents/capabilities.py index 2e45dd0efa..495e67b485 100644 --- a/sdks/python/agenta/sdk/agents/capabilities.py +++ b/sdks/python/agenta/sdk/agents/capabilities.py @@ -29,8 +29,6 @@ Bedrock/Vertex. The runner passes the selected model id through to Claude Code and lets the configured backend fail loudly if it rejects it. - **Codex** reaches openai only, direct, through managed keys or subscription OAuth. -- **pi_agenta** is Pi under the hood (Pi with Agenta's forced opinion), so it shares - ``pi_core``'s reach. The sibling ``docs/design/agent-workflows/projects/harness-capabilities/`` project owns the general capability-table mechanism; this module is the provider/model/auth contribution @@ -392,15 +390,6 @@ def _derive_default_models(self) -> "HarnessConnectionCapabilities": models=_pi_models(), model_catalog=_model_catalog("pi_core"), ), - "pi_agenta": HarnessConnectionCapabilities( - # See ``pi_core``: ``custom`` is UI-surface only; ``harness_allows_pair`` is authoritative. - providers=list(PI_VAULT_PROVIDERS) + list(PI_SUBSCRIPTION_PROVIDERS), - deployments=["direct", "custom"], - connection_modes=list(_ALL_MODES), - model_selection="provider/id", - models=_pi_models(), - model_catalog=_model_catalog("pi_agenta"), - ), "claude": HarnessConnectionCapabilities( providers=["anthropic"], deployments=["direct", "custom", "bedrock", "vertex_ai", "vertex"], @@ -491,7 +480,7 @@ def harness_allows_deployment(harness: str, deployment: str) -> bool: """Whether ``harness`` can CONSUME the resolved ``deployment`` in v1. A harness with no entry is unknown, so it gets no capability (closed). The cloud surfaces - are allowed only when the harness lists them as consumable. ``pi_core``/``pi_agenta`` list + are allowed only when the harness lists them as consumable. ``pi_core`` lists ``direct`` and ``custom`` (the OpenAI-compatible surface); Claude also lists ``bedrock``/``vertex_ai``. """ @@ -509,7 +498,6 @@ def harness_allows_deployment(harness: str, deployment: str) -> bool: # absent here accepts no ``custom`` deployment. HARNESS_CUSTOM_DEPLOYMENT_PROVIDERS: Dict[str, str] = { "pi_core": "openai", - "pi_agenta": "openai", "claude": "anthropic", } @@ -527,8 +515,8 @@ def harness_allows_pair(harness: str, provider: str, deployment: str) -> bool: The allowed triples: - - ``pi_core``/``pi_agenta`` + ``openai`` + ``direct`` or ``custom`` -> allowed; - - ``pi_core``/``pi_agenta`` + any other family + ``custom`` -> rejected; + - ``pi_core`` + ``openai`` + ``direct`` or ``custom`` -> allowed; + - ``pi_core`` + any other family + ``custom`` -> rejected; - ``claude`` + ``anthropic`` + ``direct``/``custom``/``bedrock``/``vertex_ai`` -> allowed; - ``claude`` + ``openai`` + anything -> rejected (Claude reaches anthropic only); - unknown harness -> rejected. diff --git a/sdks/python/agenta/sdk/agents/dtos.py b/sdks/python/agenta/sdk/agents/dtos.py index dd74fcefab..ce8678043c 100644 --- a/sdks/python/agenta/sdk/agents/dtos.py +++ b/sdks/python/agenta/sdk/agents/dtos.py @@ -46,13 +46,11 @@ class HarnessKind(str, Enum): """The coding agent program a run drives. A backend declares which it supports. - ``pi_core`` is plain Pi; ``pi_agenta`` is Pi with Agenta's forced skills, prompt, and - policy. Both drive the same ``pi`` ACP agent in the runner; ``claude`` drives Claude Code. + ``pi_core`` is Pi; ``claude`` drives Claude Code; ``codex`` drives Codex. """ PI = "pi_core" CLAUDE = "claude" - AGENTA = "pi_agenta" CODEX = "codex" @classmethod @@ -60,7 +58,13 @@ def coerce(cls, value: "HarnessKind | str") -> "HarnessKind": """Accept either an enum or a loose string (the playground sends a string).""" if isinstance(value, cls): return value - return cls(str(value).lower()) + normalized = str(value).lower() + # ``pi_agenta`` was a short-lived experiment (Pi plus a forced Agenta overlay), removed + # 2026-08-29. Revisions saved while it existed still carry the value, so reading maps it + # to plain Pi instead of refusing to load the config. + if normalized == "pi_agenta": + normalized = "pi_core" + return cls(normalized) # --------------------------------------------------------------------------- @@ -72,7 +76,7 @@ def coerce(cls, value: "HarnessKind | str") -> "HarnessKind": # ``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_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. +# (``pi_core`` / ``claude`` / ``codex``), which the runner reads as the runtime selector. class HarnessIdentity(BaseModel): @@ -97,11 +101,6 @@ class HarnessIdentity(BaseModel): slug=f"agenta:harness:{HarnessKind.PI.value}:v0", name="Pi", ), - HarnessIdentity( - value=HarnessKind.AGENTA.value, - slug=f"agenta:harness:{HarnessKind.AGENTA.value}:v0", - name="Pi (Agenta)", - ), HarnessIdentity( value=HarnessKind.CLAUDE.value, slug=f"agenta:harness:{HarnessKind.CLAUDE.value}:v0", @@ -1074,14 +1073,6 @@ def wire_harness_files(self) -> Dict[str, Any]: return {"harnessFiles": files} -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).""" - - harness: ClassVar[HarnessKind] = HarnessKind.AGENTA - - # --------------------------------------------------------------------------- # The session bundle # --------------------------------------------------------------------------- diff --git a/sdks/python/agenta/sdk/agents/model_catalog.py b/sdks/python/agenta/sdk/agents/model_catalog.py index c0214718d3..a42549a98b 100644 --- a/sdks/python/agenta/sdk/agents/model_catalog.py +++ b/sdks/python/agenta/sdk/agents/model_catalog.py @@ -202,7 +202,7 @@ def model_input_modalities( ) -> Optional[List[str]]: """Look up input modalities using the model id form accepted by ``harness``.""" entry: Optional[ModelCatalogEntry] - if harness in ("pi_core", "pi_agenta"): + if harness == "pi_core": catalog = pi_model_catalog() catalog_id = _catalog_id(provider, model_id) elif harness == "claude": @@ -235,7 +235,7 @@ def model_catalog_entries(harness: str) -> List[Dict[str, object]]: uses its curated model catalog. An unknown harness has an empty catalog (like the ``models`` map default). """ - if harness in ("pi_core", "pi_agenta"): + if harness == "pi_core": catalog = pi_model_catalog() elif harness == "claude": catalog = claude_model_catalog() diff --git a/sdks/python/agenta/sdk/agents/wire_models.py b/sdks/python/agenta/sdk/agents/wire_models.py index 0ab82af68f..4344e866b5 100644 --- a/sdks/python/agenta/sdk/agents/wire_models.py +++ b/sdks/python/agenta/sdk/agents/wire_models.py @@ -501,7 +501,7 @@ class WireRunRequest(_WireModel): Every field is optional on the wire (the contract is implicitly all-optional), so the schema expresses "optional" while the producer's omit-when-empty behavior stays in ``wire.py`` and - is pinned by the golden fixtures. The harness selects the agent (``pi_core`` / ``pi_agenta`` + is pinned by the golden fixtures. The harness selects the agent (``pi_core`` / ``claude``); there is no engine selector on the wire (A3 removed the legacy backend). """ diff --git a/sdks/python/agenta/sdk/utils/types.py b/sdks/python/agenta/sdk/utils/types.py index 082ae5fa72..db1aa75c11 100644 --- a/sdks/python/agenta/sdk/utils/types.py +++ b/sdks/python/agenta/sdk/utils/types.py @@ -1342,7 +1342,7 @@ class _HarnessSchema(BaseModel): """The coding agent to drive plus its execution knobs (was the flat ``harness`` scalar and its ``harness_kwargs`` slice). - ``kind`` is the harness selector (the bare ``pi_core`` / ``pi_agenta`` / ``claude`` value). + ``kind`` is the harness selector (the bare ``pi_core`` / ``claude`` / ``codex`` value). ``permissions`` is the allow/ask/deny rule lists that decide which tools may run. ``extras`` is the per-harness escape hatch (Pi's ``system`` / ``append_system`` prompt overrides).""" @@ -1352,8 +1352,7 @@ class _HarnessSchema(BaseModel): default=_DEFAULT_HARNESS, title="Harness", description=( - "Coding agent to drive: pi_core (plain Pi), claude, or pi_agenta (Pi with " - "Agenta's forced skills, tools, and base instructions)." + "Coding agent to drive: pi_core (Pi), claude (Claude Code), or codex (Codex)." ), json_schema_extra=_harness_field_schema_extra(), ) 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 fc17396835..d83f5d6977 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 @@ -124,7 +124,7 @@ class FakeRunnerBackend(Backend): """ supported_harnesses = frozenset( - {HarnessKind.PI, HarnessKind.CLAUDE, HarnessKind.AGENTA, HarnessKind.CODEX} + {HarnessKind.PI, HarnessKind.CLAUDE, HarnessKind.CODEX} ) def __init__( diff --git a/sdks/python/oss/tests/pytest/unit/agents/connections/test_capabilities.py b/sdks/python/oss/tests/pytest/unit/agents/connections/test_capabilities.py index 82cd288afb..d4b68bf63e 100644 --- a/sdks/python/oss/tests/pytest/unit/agents/connections/test_capabilities.py +++ b/sdks/python/oss/tests/pytest/unit/agents/connections/test_capabilities.py @@ -29,8 +29,8 @@ def test_claude_is_anthropic_only(): assert harness_allows_provider("claude", "OpenAI") is False # case-insensitive -def test_pi_and_agenta_reach_the_vault_providers_not_arbitrary_ones(): - for harness in ("pi_core", "pi_agenta"): +def test_pi_reaches_the_vault_providers_not_arbitrary_ones(): + for harness in ("pi_core",): # Real list, not "*": the eight vault-mapped providers are reachable... for provider in PI_VAULT_PROVIDERS: assert harness_allows_provider(harness, provider) is True @@ -38,14 +38,14 @@ def test_pi_and_agenta_reach_the_vault_providers_not_arbitrary_ones(): assert harness_allows_provider(harness, "anything-custom") is False -def test_pi_and_agenta_reach_the_openai_codex_subscription_provider(): +def test_pi_reaches_the_openai_codex_subscription_provider(): """The ChatGPT/Codex subscription provider is reachable (OAuth login, no vault key). Without this, an ``openai-codex`` model fails the agent-layer pre-resolve provider check even though the runner drives the subscription fine. ``self_managed`` is the subscription path; the provider must be allowed for that mode to ever reach the runner. """ - for harness in ("pi_core", "pi_agenta"): + for harness in ("pi_core",): for provider in PI_SUBSCRIPTION_PROVIDERS: assert harness_allows_provider(harness, provider) is True assert harness_allows_provider(harness, "openai-codex") is True @@ -77,7 +77,7 @@ def test_pi_consumes_direct_and_custom_deployment_in_v1(): # Pi now publishes `custom` (the OpenAI-compatible surface) alongside `direct` so the UI can # surface those connections. The cloud surfaces remain unconsumed in v1. The openai-only # pairing on `custom` is enforced by `harness_allows_pair`, not this per-axis list. - for harness in ("pi_core", "pi_agenta"): + for harness in ("pi_core",): assert harness_allows_deployment(harness, "direct") is True assert harness_allows_deployment(harness, "custom") is True for deployment in ("bedrock", "vertex_ai", "azure"): @@ -86,7 +86,7 @@ def test_pi_consumes_direct_and_custom_deployment_in_v1(): def test_resolved_pair_validation_matches_decision_3_table(): # Every row of design Decision 3's allowed-pairs table. - for harness in ("pi_core", "pi_agenta"): + for harness in ("pi_core",): # Pi + openai + direct/custom -> allowed. assert harness_allows_pair(harness, "openai", "direct") is True assert harness_allows_pair(harness, "openai", "custom") is True @@ -117,7 +117,7 @@ def test_claude_consumes_custom_gateway_bedrock_and_vertex(): def test_capabilities_document_shape(): doc = harness_capabilities_document() - assert set(doc) == {"pi_core", "pi_agenta", "claude", "codex"} + assert set(doc) == {"pi_core", "claude", "codex"} assert doc["claude"]["providers"] == ["anthropic"] assert doc["claude"]["model_selection"] == "alias" assert doc["pi_core"]["providers"] == list(PI_VAULT_PROVIDERS) + list( @@ -145,12 +145,11 @@ def test_capabilities_document_shape(): } } assert "mcp" not in doc["pi_core"] - assert "mcp" not in doc["pi_agenta"] def test_every_harness_publishes_a_models_map(): doc = harness_capabilities_document() - for harness in ("pi_core", "pi_agenta", "claude"): + for harness in ("pi_core", "claude"): assert isinstance(doc[harness]["models"], dict) assert doc[harness]["models"], f"{harness} has an empty models map" @@ -158,7 +157,7 @@ def test_every_harness_publishes_a_models_map(): def test_pi_models_are_a_subset_of_the_shared_catalog(): # Each Pi harness publishes, per vault provider, exactly that provider's catalog ids, plus the # subscription/OAuth providers' explicit ids (which the shared catalog does not list). - for harness in ("pi_core", "pi_agenta"): + for harness in ("pi_core",): models = HARNESS_CONNECTION_CAPABILITIES[harness].models # The published providers are the vault-mapped ones plus the subscription providers. assert set(models) == set(PI_VAULT_PROVIDERS) | set(PI_SUBSCRIPTION_PROVIDERS) @@ -177,7 +176,7 @@ def test_pi_models_are_a_subset_of_the_shared_catalog(): def test_pi_publishes_concrete_gpt_5_6_models_for_both_openai_providers(): expected = ["gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna"] - for harness in ("pi_core", "pi_agenta"): + for harness in ("pi_core",): models = HARNESS_CONNECTION_CAPABILITIES[harness].models for provider in ("openai", "openai-codex"): assert models[provider][:3] == expected diff --git a/sdks/python/oss/tests/pytest/unit/agents/connections/test_model_catalog.py b/sdks/python/oss/tests/pytest/unit/agents/connections/test_model_catalog.py index 2f36b13767..22f67bc779 100644 --- a/sdks/python/oss/tests/pytest/unit/agents/connections/test_model_catalog.py +++ b/sdks/python/oss/tests/pytest/unit/agents/connections/test_model_catalog.py @@ -31,7 +31,7 @@ pi_model_catalog, ) -_ALL_HARNESSES = ("pi_core", "pi_agenta", "claude", "codex") +_ALL_HARNESSES = ("pi_core", "claude", "codex") def test_data_files_load_and_validate(): @@ -255,7 +255,7 @@ def test_model_catalog_entries_helper_matches_the_published_field(): assert model_catalog_entries("some-future-harness") == [] -@pytest.mark.parametrize("harness", ["pi_core", "pi_agenta"]) +@pytest.mark.parametrize("harness", ["pi_core"]) def test_pi_input_modalities_lookup_joins_resolved_provider_and_model(harness): assert model_input_modalities(harness, "gpt-5.5", provider="openai") == [ "text", diff --git a/sdks/python/oss/tests/pytest/unit/agents/test_agent_composition_seam.py b/sdks/python/oss/tests/pytest/unit/agents/test_agent_composition_seam.py index 84bf67938e..63bd194db7 100644 --- a/sdks/python/oss/tests/pytest/unit/agents/test_agent_composition_seam.py +++ b/sdks/python/oss/tests/pytest/unit/agents/test_agent_composition_seam.py @@ -83,9 +83,7 @@ async def destroy(self) -> None: class _FakeBackend(Backend): - supported_harnesses = frozenset( - {HarnessKind.PI, HarnessKind.CLAUDE, HarnessKind.AGENTA} - ) + supported_harnesses = frozenset({HarnessKind.PI, HarnessKind.CLAUDE}) def __init__(self, *, output: str = "hi") -> None: self._output = output diff --git a/sdks/python/oss/tests/pytest/unit/agents/test_dtos_capabilities_events.py b/sdks/python/oss/tests/pytest/unit/agents/test_dtos_capabilities_events.py index 0bf431aebb..55ed542597 100644 --- a/sdks/python/oss/tests/pytest/unit/agents/test_dtos_capabilities_events.py +++ b/sdks/python/oss/tests/pytest/unit/agents/test_dtos_capabilities_events.py @@ -81,7 +81,8 @@ def test_harness_type_coerce(): assert HarnessKind.coerce(HarnessKind.PI) is HarnessKind.PI assert HarnessKind.coerce("pi_core") is HarnessKind.PI assert HarnessKind.coerce("PI_CORE") is HarnessKind.PI # case-insensitive - assert HarnessKind.coerce("pi_agenta") is HarnessKind.AGENTA + # The removed ``pi_agenta`` experiment's spelling still reads as Pi (old stored revisions). + assert HarnessKind.coerce("pi_agenta") is HarnessKind.PI assert HarnessKind.coerce("claude") is HarnessKind.CLAUDE with pytest.raises(ValueError): HarnessKind.coerce("bogus") 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 d25fc67779..4036c3c292 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,8 +13,6 @@ import pytest from agenta.sdk.agents import ( - AgentaAgentTemplate, - AgentaHarness, AgentTemplate, ClaudeAgentTemplate, ClaudeHarness, @@ -29,14 +27,7 @@ UnsupportedHarnessError, make_harness, ) -from agenta.sdk.agents.adapters.agenta_builtins import ( - AGENTA_FORCED_APPEND_SYSTEM, - AGENTA_FORCED_SKILLS, - GETTING_STARTED_WITH_AGENTA_SKILL, - AGENTA_PREAMBLE, - force_skills, - gateway_guidance, -) +from agenta.sdk.agents.adapters.agenta_builtins import gateway_guidance from agenta.sdk.agents.adapters.harnesses import _normalize_tool_specs, _opt_str from agenta.sdk.agents.tools import ( CompiledTool, @@ -97,24 +88,6 @@ def test_pi_threads_model_ref_so_connection_reaches_resolver(make_env): assert result.wire_model_connection() == {} -def test_agenta_threads_model_ref_so_connection_reaches_resolver(make_env): - """Same guarantee as Pi for the ``pi_agenta`` harness (it also runs Pi).""" - harness = AgentaHarness(make_env(supported=[HarnessKind.AGENTA])) - agent = AgentTemplate( - instructions="hi", - model={ - "model": "gpt-4o-mini", - "connection": {"mode": "agenta", "slug": "my-compat"}, - }, - ) - - result = harness._to_harness_config(_session_config(agent=agent)) - - assert result.model_ref is not None - assert result.model_ref.connection.slug == "my-compat" - assert result.wire_model_connection() == {} - - def test_pi_reads_its_harness_extras_slice(make_env): harness = PiHarness(make_env(supported=[HarnessKind.PI])) agent = AgentTemplate( @@ -148,110 +121,6 @@ def test_pi_drops_blank_harness_extras(make_env): assert result.wire_prompt() == {} -# ------------------------------------------------------------------------- Agenta - - -def test_agenta_forces_preamble_and_persona_and_carries_skills(make_env): - harness = AgentaHarness(make_env(supported=[HarnessKind.AGENTA])) - skill = { - "name": "release-notes", - "description": "Draft release notes.", - "body": "Read the changelog, then write notes.", - } - config = _session_config( - agent=AgentTemplate( - instructions="My project rules.", model="m", skills=[skill] - ), - custom_tools=[{"name": "t", "callRef": "ref"}], - tool_callback=_CALLBACK, - ) - - result = harness._to_harness_config(config) - - 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.") - # The author's resolved inline skills ride the config, plus the forced platform skill(s) the - # harness always injects. The author's skill comes first; the platform skill is appended. - skill_names = [s.name for s in result.skills] - assert skill_names[0] == "release-notes" - assert GETTING_STARTED_WITH_AGENTA_SKILL.name in skill_names - assert "skills" not in result.wire_tools() - assert result.wire_skills()["skills"][0]["name"] == "release-notes" - # The persona is forced onto append_system; custom tools and callback pass through. - assert result.append_system.startswith(AGENTA_FORCED_APPEND_SYSTEM) - assert result.custom_tools[0]["name"] == "t" - assert result.tool_callback is _CALLBACK - - -def test_agenta_forces_platform_skill_on_a_skill_less_config(make_env): - # The actually-forced behavior: a custom pi_agenta config with NO skills (the default - # template's `_agenta` embed dropped) still carries the platform skill on every run. - harness = AgentaHarness(make_env(supported=[HarnessKind.AGENTA])) - config = _session_config( - agent=AgentTemplate(instructions="My project rules.", model="m", skills=[]) - ) - - result = harness._to_harness_config(config) - - assert [s.name for s in result.skills] == [GETTING_STARTED_WITH_AGENTA_SKILL.name] - - -def test_agenta_does_not_duplicate_an_already_present_platform_skill(make_env): - # A config that already carries the resolved platform skill (e.g. via the default template's - # embed) is not doubled: the author's copy wins on the name clash. - harness = AgentaHarness(make_env(supported=[HarnessKind.AGENTA])) - existing = GETTING_STARTED_WITH_AGENTA_SKILL.model_dump(mode="json") - config = _session_config( - agent=AgentTemplate(instructions="hi", model="m", skills=[existing]) - ) - - result = harness._to_harness_config(config) - - names = [s.name for s in result.skills] - assert names.count(GETTING_STARTED_WITH_AGENTA_SKILL.name) == 1 - - -def test_force_skills_unions_forced_after_author_skills(): - from agenta.sdk.agents.skills import SkillTemplate - - author = SkillTemplate( - name="release-notes", description="Draft notes.", body="Do it." - ) - - out = force_skills([author]) - - assert out[0] is author - assert {s.name for s in out} == {"release-notes"} | { - s.name for s in AGENTA_FORCED_SKILLS - } - - -def test_agenta_passes_through_user_pi_options(make_env): - harness = AgentaHarness(make_env(supported=[HarnessKind.AGENTA])) - agent = AgentTemplate( - instructions="hi", - harness_extras={"system": "You are Pi.", "append_system": "Be terse."}, - ) - - result = harness._to_harness_config(_session_config(agent=agent)) - - # `system` passes through; the author's `append_system` is appended after the forced persona. - assert result.system == "You are Pi." - assert result.append_system.startswith(AGENTA_FORCED_APPEND_SYSTEM) - assert result.append_system.endswith("Be terse.") - - -def test_agenta_is_sandbox_agent_supported(): - # Agenta is Pi with an opinion, so the sandbox-agent backend drives it too (on the `pi` ACP - # agent, with the runner laying the forced skills into the sandbox). This is what lets - # `agenta` run on a non-local sandbox (e.g. daytona) instead of raising. - from agenta.sdk.agents import SandboxAgentBackend - - assert SandboxAgentBackend(url="http://runner").supports(HarnessKind.AGENTA) - - # ------------------------------------------------------------------------- Claude @@ -415,7 +284,6 @@ def test_make_harness_maps_string_to_class(make_env): HarnessKind.PI, HarnessKind.CLAUDE, HarnessKind.CODEX, - HarnessKind.AGENTA, ] ) assert isinstance(make_harness("pi_core", env), PiHarness) @@ -426,8 +294,8 @@ def test_make_harness_maps_string_to_class(make_env): assert isinstance(make_harness(HarnessKind.CLAUDE, env), ClaudeHarness) assert isinstance(make_harness("codex", env), CodexHarness) assert isinstance(make_harness(HarnessKind.CODEX, env), CodexHarness) - assert isinstance(make_harness("pi_agenta", env), AgentaHarness) - assert isinstance(make_harness(HarnessKind.AGENTA, env), AgentaHarness) + # The removed experiment's spelling still resolves (to plain Pi) for old stored configs. + assert isinstance(make_harness("pi_agenta", env), PiHarness) # ------------------------------------------------- gateway connection prompt guidance @@ -459,7 +327,6 @@ def test_make_harness_maps_string_to_class(make_env): (PiHarness, HarnessKind.PI, "append_system", _AUTHOR_APPEND), (ClaudeHarness, HarnessKind.CLAUDE, "agents_md", _AUTHOR_INSTRUCTIONS), (CodexHarness, HarnessKind.CODEX, "agents_md", _AUTHOR_INSTRUCTIONS), - (AgentaHarness, HarnessKind.AGENTA, "agents_md", _AUTHOR_INSTRUCTIONS), ] @@ -536,23 +403,6 @@ def test_pi_agents_md_stays_purely_authored(make_env): assert "search_tools" in result.append_system -def test_agenta_keeps_its_preamble_first_with_guidance(make_env): - """The existing prefix rule survives: preamble, then guidance, then the author.""" - harness = AgentaHarness(make_env(supported=[HarnessKind.AGENTA])) - config = _session_config( - agent=AgentTemplate(instructions="My project rules.", model="m"), - gateway_policy=_GATEWAY_POLICY, - ) - - result = harness._to_harness_config(config) - - assert result.agents_md.startswith(AGENTA_PREAMBLE) - assert result.agents_md.endswith("My project rules.") - assert result.agents_md.index(AGENTA_PREAMBLE) < result.agents_md.index( - "search_tools" - ) - - def test_gateway_guidance_is_never_stored_in_the_revision(make_env): """It is derived at resolve time, like the two tools it describes.""" harness = PiHarness(make_env(supported=[HarnessKind.PI])) 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 ab99fdb7dc..4e5a4e270a 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 @@ -31,7 +31,7 @@ def test_identity_value_is_the_bare_harness_string(): # The identity's `value` is the bare HarnessKind value (the runtime/wire selector), NOT the # slug — so the wire/runner contract is unchanged. values = {identity.value for identity in HARNESS_IDENTITIES} - assert values == {"pi_core", "pi_agenta", "claude", "codex"} + assert values == {"pi_core", "claude", "codex"} def _harness_kind_field(): 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 13f1d1f52a..f3ff55647c 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 @@ -10,7 +10,7 @@ to match. There is no engine selector on the wire: the runner drives one engine (the sandbox-agent ACP -path) and ``harness`` (``pi_core`` / ``pi_agenta`` / ``claude``) picks the agent. +path) and ``harness`` (``pi_core`` / ``claude`` / ``codex``) picks the agent. """ from __future__ import annotations @@ -23,7 +23,6 @@ from agenta.sdk.redaction.redactor import Redactor from agenta.sdk.agents import ( - AgentaAgentTemplate, AgentTemplate, ClaudeAgentTemplate, CodexAgentTemplate, @@ -334,23 +333,6 @@ def _gateway_connection_payload(): ) -def _agenta_payload(): - config = AgentaAgentTemplate( - agents_md="Agenta preamble + project rules.", - model="gpt-5.5", - custom_tools=[dict(_CUSTOM_TOOL)], - tool_callback=_CALLBACK, - append_system="You are an Agenta agent.", - skills=[dict(_SKILL)], - ) - return request_to_wire( - harness=HarnessKind.AGENTA, - sandbox="local", - config=config, - messages=[Message(role="user", content="hi")], - ) - - def _attachment_payload(): config = PiAgentTemplate( agents_md="Use the attached file.", @@ -433,18 +415,6 @@ def test_request_to_wire_omits_gateway_policy_without_a_connection(golden): assert payload == golden(name) -def test_request_to_wire_agenta_carries_skills_and_pi_shape(): - payload = _agenta_payload() - assert set(payload) <= KNOWN_REQUEST_KEYS - # Agenta is a Pi config: same tool shape and shared permission plan, plus prompt overrides. - assert payload["permissions"] == {"default": "allow_reads"} - assert payload["tools"] == list(PI_BUILTIN_TOOL_NAMES) - assert payload["appendSystemPrompt"] == "You are an Agenta agent." - # ...plus the resolved inline skill packages, on their own seam (not in `wire_tools`). - assert payload["skills"][0]["name"] == "release-notes" - assert payload["skills"][0]["files"][0]["path"] == "scripts/draft.py" - - 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 = PiAgentTemplate(skills=[dict(_SKILL)]) 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 4c7eeb9d31..48c8bb3152 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 @@ -36,7 +36,6 @@ from .test_wire_contract import ( KNOWN_REQUEST_KEYS, - _agenta_payload, _claude_payload, _pi_payload, ) @@ -113,7 +112,7 @@ def test_goldens_validate_against_the_exported_schema(golden, golden_name, ag_ty def test_request_to_wire_output_validates_against_the_schema(): # The producer and the schema agree: the dict `request_to_wire` builds for each harness # validates against the exported request schema and round-trips through the wire model. - for payload in (_pi_payload(), _claude_payload(), _agenta_payload()): + for payload in (_pi_payload(), _claude_payload()): jsonschema.validate(payload, CATALOG_TYPES["run_request"]) WireRunRequest.model_validate(payload) diff --git a/sdks/python/oss/tests/pytest/unit/test_batch_fold_stream_contract_routing.py b/sdks/python/oss/tests/pytest/unit/test_batch_fold_stream_contract_routing.py index 0738a96b0e..b5562e95a0 100644 --- a/sdks/python/oss/tests/pytest/unit/test_batch_fold_stream_contract_routing.py +++ b/sdks/python/oss/tests/pytest/unit/test_batch_fold_stream_contract_routing.py @@ -122,9 +122,7 @@ async def destroy(self) -> None: class _FakeBackend(Backend): - supported_harnesses = frozenset( - {HarnessKind.PI, HarnessKind.CLAUDE, HarnessKind.AGENTA} - ) + supported_harnesses = frozenset({HarnessKind.PI, HarnessKind.CLAUDE}) def __init__(self, *, events: List[Event]) -> None: self._events = events diff --git a/sdks/python/oss/tests/pytest/unit/test_invoke_real_handlers_negotiation_routing.py b/sdks/python/oss/tests/pytest/unit/test_invoke_real_handlers_negotiation_routing.py index 23e86c6d14..c177237ade 100644 --- a/sdks/python/oss/tests/pytest/unit/test_invoke_real_handlers_negotiation_routing.py +++ b/sdks/python/oss/tests/pytest/unit/test_invoke_real_handlers_negotiation_routing.py @@ -125,9 +125,7 @@ async def destroy(self) -> None: class _FakeBackend(Backend): - supported_harnesses = frozenset( - {HarnessKind.PI, HarnessKind.CLAUDE, HarnessKind.AGENTA} - ) + supported_harnesses = frozenset({HarnessKind.PI, HarnessKind.CLAUDE}) def __init__(self, *, events: List[Event], output: str = "") -> None: self._events = events diff --git a/services/oss/tests/pytest/unit/agent/conftest.py b/services/oss/tests/pytest/unit/agent/conftest.py index f29455100c..18309dd136 100644 --- a/services/oss/tests/pytest/unit/agent/conftest.py +++ b/services/oss/tests/pytest/unit/agent/conftest.py @@ -90,7 +90,6 @@ def __init__( supported: Sequence[HarnessKind] = ( HarnessKind.PI, HarnessKind.CLAUDE, - HarnessKind.AGENTA, ), ) -> None: self.supported_harnesses = frozenset(supported) diff --git a/services/oss/tests/pytest/unit/agent/test_builtin_uri_binding.py b/services/oss/tests/pytest/unit/agent/test_builtin_uri_binding.py index 0d23a3aef9..d35024c548 100644 --- a/services/oss/tests/pytest/unit/agent/test_builtin_uri_binding.py +++ b/services/oss/tests/pytest/unit/agent/test_builtin_uri_binding.py @@ -73,7 +73,7 @@ def test_harness_capabilities_live_in_the_catalog_not_inspect_meta(): # for agent vs non-agent). They live in the `harnesses` catalog, keyed by harness, with # `capabilities` as a field. The frontend resolves them via `x-ag-harness-ref`. doc = harness_catalog_document() - assert set(doc) == {"pi_core", "pi_agenta", "claude", "codex"} + assert set(doc) == {"pi_core", "claude", "codex"} # Each record is {harness, capabilities: {...}}; claude reaches anthropic, Pi per-provider. assert doc["claude"]["harness"] == "claude" assert doc["claude"]["capabilities"]["models"]["anthropic"] 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 0ffae99ada..bd9fafcbf7 100644 --- a/services/oss/tests/pytest/unit/agent/test_invoke_handler.py +++ b/services/oss/tests/pytest/unit/agent/test_invoke_handler.py @@ -286,14 +286,13 @@ async def test_invoke_cross_harness_same_body_divergent_configs( } bodies = [ await _invoke(harness, permission_default="deny", skills=[skill]) - for harness in ("pi_core", "pi_agenta", "claude") + for harness in ("pi_core", "claude") ] - pi_body, agenta_body, claude_body = bodies + pi_body, claude_body = bodies # (1) identical body regardless of harness assert ( pi_body - == agenta_body == claude_body == { "messages": [ @@ -305,11 +304,10 @@ async def test_invoke_cross_harness_same_body_divergent_configs( } ) - # (2) the three harness-shaped configs that reached the backend boundary, in call order - assert len(backend.created_configs) == 3 - pi_cfg, agenta_cfg, claude_cfg = backend.created_configs + # (2) the two harness-shaped configs that reached the backend boundary, in call order + assert len(backend.created_configs) == 2 + pi_cfg, claude_cfg = backend.created_configs pi_wire = pi_cfg.wire_tools() - agenta_wire = agenta_cfg.wire_tools() claude_wire = claude_cfg.wire_tools() # Pi carries its custom tool natively and always names every built-in on the deprecated @@ -324,19 +322,12 @@ async def test_invoke_cross_harness_same_body_divergent_configs( assert claude_wire["permissions"] == {"default": "deny"} assert "skills" not in claude_wire - # Agenta is Pi-with-an-opinion, and the opinion is prompt-shaped, not tool-shaped: the two - # share a tool wire. Skills are not tools, so they never appear in it either. - assert agenta_wire == pi_wire - assert "skills" not in agenta_wire - # skills ride the dedicated wire_skills seam, not the tool wire assert pi_cfg.wire_skills()["skills"][0]["name"] == "release-notes" - assert agenta_cfg.wire_skills()["skills"][0]["name"] == "release-notes" assert claude_cfg.wire_skills()["skills"][0]["name"] == "release-notes" # configs genuinely differ; the body's sameness is not a tautology assert pi_wire != claude_wire - assert agenta_cfg.wire_prompt() != pi_cfg.wire_prompt() async def test_stream_tool_resolution_failure_is_raised_before_backend_setup( diff --git a/services/runner/src/engines/sandbox_agent/pi-model-config.ts b/services/runner/src/engines/sandbox_agent/pi-model-config.ts index 2995fbf6e5..c7af03c59e 100644 --- a/services/runner/src/engines/sandbox_agent/pi-model-config.ts +++ b/services/runner/src/engines/sandbox_agent/pi-model-config.ts @@ -225,6 +225,7 @@ export class PiModelConfigError extends Error { /** Pi identity check, mirroring `buildRunPlan` (an empty harness defaults to `pi_core`). */ function isPiHarness(harness: string | undefined): boolean { const resolved = harness || "pi_core"; + // "pi_agenta" is the removed experiment's spelling, still read as Pi (see run-plan.ts). return resolved === "pi_core" || resolved === "pi_agenta"; } diff --git a/services/runner/src/engines/sandbox_agent/run-plan.ts b/services/runner/src/engines/sandbox_agent/run-plan.ts index dc76cd511d..21c50b1dcc 100644 --- a/services/runner/src/engines/sandbox_agent/run-plan.ts +++ b/services/runner/src/engines/sandbox_agent/run-plan.ts @@ -449,7 +449,8 @@ export function buildRunPlan( } // The harness identity maps to a real ACP agent the daemon knows (`pi` / `claude`). - // `pi_core` (plain Pi) and `pi_agenta` (Pi with Agenta's forced skills/prompt/policy) both + // `pi_agenta` (a removed experiment: Pi plus a forced Agenta overlay) is read as Pi so an + // old stored request or replay still runs; `pi_core` and that legacy spelling both // run on the `pi` ACP agent; `claude` runs on the `claude` ACP agent. `harness` remains the // selected identity for logs, traces, and user-facing errors. const acpAgent = diff --git a/services/runner/src/lifecycle/reconciliation-router.ts b/services/runner/src/lifecycle/reconciliation-router.ts index 559224da49..a168d56753 100644 --- a/services/runner/src/lifecycle/reconciliation-router.ts +++ b/services/runner/src/lifecycle/reconciliation-router.ts @@ -220,8 +220,9 @@ const V1_CAPABILITIES: Readonly< }; export function harnessKind(request: AgentRunRequest): HarnessKind { - // Mirror `buildRunPlan`'s normalization exactly: the WIRE carries `pi_core` / `pi_agenta` - // (an empty harness defaults to `pi_core`), never the bare "pi" this table is keyed by. + // Mirror `buildRunPlan`'s normalization exactly: the WIRE carries `pi_core` (an empty + // harness defaults to it; `pi_agenta` is a removed experiment's spelling, still read as + // Pi so an old stored request replays), never the bare "pi" this table is keyed by. // Matching only the literals sent every playground Pi run into the fail-closed `unknown` // row, whose every facet says rebuild — which disabled the live model-switch route and // made the shadow log plan a rebuild for every Pi config change. diff --git a/services/runner/src/protocol.ts b/services/runner/src/protocol.ts index 41066698e7..56511dd319 100644 --- a/services/runner/src/protocol.ts +++ b/services/runner/src/protocol.ts @@ -615,8 +615,9 @@ export interface GatewayPolicy { export interface AgentRunRequest { /** - * Harness id: "pi_core" | "pi_agenta" | "claude". `pi_core` and `pi_agenta` both drive the - * ACP agent "pi" (pi_agenta is Pi with Agenta's forced skills/prompt/policy); "claude" drives + * Harness id: "pi_core" | "claude" | "codex". `pi_core` drives the ACP agent "pi"; + * "pi_agenta" (a removed 2026 experiment) is still read as `pi_core` so an old + * stored request replays. "claude" drives * the ACP agent "claude". Selected by the request; there is no engine selector. */ harness?: string; diff --git a/services/runner/src/subscription-status.ts b/services/runner/src/subscription-status.ts index 01fd389d07..b9e46c2ca7 100644 --- a/services/runner/src/subscription-status.ts +++ b/services/runner/src/subscription-status.ts @@ -41,12 +41,7 @@ export type SubscriptionState = /** This runner version cannot check this harness. */ | "unsupported"; -export const SUBSCRIPTION_HARNESSES = [ - "codex", - "claude", - "pi_core", - "pi_agenta", -] as const; +export const SUBSCRIPTION_HARNESSES = ["codex", "claude", "pi_core"] as const; export type SubscriptionHarness = (typeof SUBSCRIPTION_HARNESSES)[number]; export interface HarnessSubscriptionStatus { @@ -139,10 +134,7 @@ const PROBES: Record = { file: ".credentials.json", provider: "anthropic", }, - // `pi_core` and `pi_agenta` both drive the ACP agent "pi" (see run-plan.ts), so they read the - // same login on the same mount: one probe, reported once per harness. pi_core: PI_PROBE, - pi_agenta: PI_PROBE, }; /** diff --git a/services/runner/src/version.ts b/services/runner/src/version.ts index 185460f3fe..1c1863a3ea 100644 --- a/services/runner/src/version.ts +++ b/services/runner/src/version.ts @@ -12,7 +12,7 @@ import pkg from "../package.json"; export const PROTOCOL_VERSION = 1; export const RUNNER_VERSION: string = pkg.version; export const ENGINES = ["sandbox-agent"] as const; -export const HARNESS_KINDS = ["pi_core", "claude", "pi_agenta"] as const; +export const HARNESS_KINDS = ["pi_core", "claude"] as const; export interface RunnerInfo { status: "ok"; diff --git a/services/runner/tests/unit/server.test.ts b/services/runner/tests/unit/server.test.ts index ebe2ccb96b..d452842d53 100644 --- a/services/runner/tests/unit/server.test.ts +++ b/services/runner/tests/unit/server.test.ts @@ -290,7 +290,6 @@ describe("createAgentServer", () => { claude: { state: "login_missing", provider: "anthropic" }, // One Pi mount, one login: both Pi harnesses read it. pi_core: { state: "ready" }, - pi_agenta: { state: "ready" }, }); // The fake credential sitting in the login file the route just read is not on the wire, // and neither is any path. diff --git a/services/runner/tests/unit/subscription-status.test.ts b/services/runner/tests/unit/subscription-status.test.ts index ec0fb0c4e8..f2bcc6b51c 100644 --- a/services/runner/tests/unit/subscription-status.test.ts +++ b/services/runner/tests/unit/subscription-status.test.ts @@ -66,14 +66,10 @@ describe("harnessSubscriptionStatus", () => { state: "not_configured", provider: "anthropic", }); - // Pi is not tied to one provider, so it reports no provider at all — and both Pi harnesses - // answer alike, because they read the same login. + // Pi is not tied to one provider, so it reports no provider at all. assert.deepEqual(await harnessSubscriptionStatus("pi_core", {}), { state: "not_configured", }); - assert.deepEqual(await harnessSubscriptionStatus("pi_agenta", {}), { - state: "not_configured", - }); }); it("treats a whitespace-only mount variable as not configured", async () => { @@ -166,14 +162,10 @@ describe("harnessSubscriptionStatus", () => { ), { state: "ready", provider: "anthropic" }, ); - // One Pi mount, one login file: `pi_core` and `pi_agenta` both read it. const piMount = mount("PI_CODING_AGENT_DIR", "auth.json"); assert.deepEqual(await harnessSubscriptionStatus("pi_core", piMount), { state: "ready", }); - assert.deepEqual(await harnessSubscriptionStatus("pi_agenta", piMount), { - state: "ready", - }); }); it("names the provider families a Pi login holds", async () => { @@ -187,12 +179,10 @@ describe("harnessSubscriptionStatus", () => { }), ); - for (const harness of ["pi_core", "pi_agenta"]) { - assert.deepEqual(await harnessSubscriptionStatus(harness, env), { - state: "ready", - providers: ["anthropic", "openai"], - }); - } + assert.deepEqual(await harnessSubscriptionStatus("pi_core", env), { + state: "ready", + providers: ["anthropic", "openai"], + }); }); it("ignores a login id it has no provider family for", async () => { @@ -344,24 +334,21 @@ describe("subscriptionStatusResponse", () => { assert.equal(response.harnesses.codex.state, "login_unusable"); assert.equal(response.harnesses.claude.state, "ready"); assert.equal(response.harnesses.pi_core.state, "not_configured"); - assert.equal(response.harnesses.pi_agenta.state, "not_configured"); }); - it("gives both Pi harnesses the same answer from the one Pi mount", async () => { + it("reports the Pi states from the one Pi mount", async () => { const piDir = join(root, "pi-agent"); mkdirSync(piDir, { recursive: true }); const env = { PI_CODING_AGENT_DIR: piDir } as NodeJS.ProcessEnv; - // No login file yet: both Pi harnesses say so. + // No login file yet. let response = await subscriptionStatusResponse(env); assert.equal(response.harnesses.pi_core.state, "login_missing"); - assert.equal(response.harnesses.pi_agenta.state, "login_missing"); writeFileSync(join(piDir, "auth.json"), FAKE_LOGIN); response = await subscriptionStatusResponse(env); assert.deepEqual(response.harnesses.pi_core, { state: "ready" }); - assert.deepEqual(response.harnesses.pi_agenta, { state: "ready" }); }); it("serializes nothing but state words and provider names", async () => { diff --git a/web/packages/agenta-entities/src/secret/core/agentModelCandidates.ts b/web/packages/agenta-entities/src/secret/core/agentModelCandidates.ts index 8d520041b0..48b2ae4a90 100644 --- a/web/packages/agenta-entities/src/secret/core/agentModelCandidates.ts +++ b/web/packages/agenta-entities/src/secret/core/agentModelCandidates.ts @@ -34,6 +34,7 @@ export interface BuildAgentModelCandidatesArgs { pairModelSelection?: Record | null } +// "pi_agenta" is a removed experiment; filter it defensively in case an older API still lists it. export const selectableAgentHarnesses = (harnessIds: string[]): string[] => harnessIds.filter((id) => id !== "pi_agenta") 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 b71d749bb6..db34189758 100644 --- a/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/HarnessSelectControl.tsx +++ b/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/HarnessSelectControl.tsx @@ -2,7 +2,7 @@ * HarnessSelectControl * * 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, + * enum (`pi_core` / `claude` / `codex`) 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 * `title` for each value's label, and supplies the avatar (brand colour + monogram) FE-side diff --git a/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/agentTemplate/useModelHarness.tsx b/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/agentTemplate/useModelHarness.tsx index bd52ba3bc1..3712ce51e9 100644 --- a/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/agentTemplate/useModelHarness.tsx +++ b/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/agentTemplate/useModelHarness.tsx @@ -149,6 +149,7 @@ export function useModelHarness({ // is harness-filtered: selecting a model sets BOTH the model id and its provider, fed by the // `/inspect` capability map below. const harnessValue = effectiveHarnessValue(harness) + // "pi_agenta" is a removed experiment; old stored revisions may still carry it. const isPiHarness = harnessValue === "pi_core" || harnessValue === "pi_agenta" const llm = config.llm const modelId = useMemo(() => modelIdFromConfig(llm), [llm]) diff --git a/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/harnessMeta.ts b/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/harnessMeta.ts index 604855595c..aaf14248ea 100644 --- a/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/harnessMeta.ts +++ b/web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/harnessMeta.ts @@ -16,16 +16,16 @@ export interface HarnessMeta { /** * Avatar identity (brand colour + monogram) per harness id. Labels come from the schema `oneOf` * title when present; these defaults only supply the avatar and a label fallback. Keyed by the real - * enum values `pi_core` / `pi_agenta` / `claude`. + * enum values `pi_core` / `claude` / `codex`. */ export const HARNESS_META: Record = { pi_core: {label: "Pi", short: "Pi", color: "#6b5bd6"}, - pi_agenta: {label: "Pi (Agenta)", short: "Ag", color: "#1c2c3d"}, claude: {label: "Claude Code", short: "CC", color: "#d97757"}, codex: {label: "Codex", short: "Cx", color: "#10a37f"}, } -/** Harnesses never offered in a picker. */ +/** Harnesses never offered in a picker. `pi_agenta` (a removed experiment) stays listed so a + * web build in front of an older API that still advertises it never shows it. */ export const HIDDEN_HARNESSES = new Set(["pi_agenta"]) /** Resolve display identity, deriving a sensible fallback for unknown harness ids. */ diff --git a/web/packages/agenta-ui/src/SelectLLMProvider/utils.ts b/web/packages/agenta-ui/src/SelectLLMProvider/utils.ts index 6e97c008dd..fa773918db 100644 --- a/web/packages/agenta-ui/src/SelectLLMProvider/utils.ts +++ b/web/packages/agenta-ui/src/SelectLLMProvider/utils.ts @@ -75,7 +75,6 @@ export const getProviderIcon = (key: string): React.FC<{className?: string}> | n */ const HARNESS_ICON_MAP: Record> = { pi_core: Pi, - pi_agenta: Pi, codex: OpenAi, claude: Anthropic, } diff --git a/web/storybook/stories/entity-ui/HarnessSelectControl.stories.tsx b/web/storybook/stories/entity-ui/HarnessSelectControl.stories.tsx index fbdacf86c7..3331c5f3db 100644 --- a/web/storybook/stories/entity-ui/HarnessSelectControl.stories.tsx +++ b/web/storybook/stories/entity-ui/HarnessSelectControl.stories.tsx @@ -33,19 +33,19 @@ type Story = StoryObj const HARNESS_META: Record = { pi_core: {label: "Pi", short: "Pi", color: "#6b5bd6"}, - pi_agenta: {label: "Pi (Agenta)", short: "Ag", color: "#1c2c3d"}, claude: {label: "Claude Code", short: "CC", color: "#d97757"}, + codex: {label: "Codex", short: "Cx", color: "#10a37f"}, } const SCHEMA = { type: "string", title: "Harness", description: "The runtime that executes the agent.", - enum: ["pi_core", "pi_agenta", "claude"], + enum: ["pi_core", "claude", "codex"], oneOf: [ {const: "pi_core", title: "Pi"}, - {const: "pi_agenta", title: "Pi (Agenta)"}, {const: "claude", title: "Claude Code"}, + {const: "codex", title: "Codex"}, ], } as never @@ -197,12 +197,12 @@ export const AntdVsAgenta: Story = { /> } + a={} s={ undefined} disabled />