From 823cba51447b131f865ff94346d08c2232f8e2db Mon Sep 17 00:00:00 2001 From: Mahmoud Mabrouk Date: Sat, 29 Aug 2026 11:37:24 +0200 Subject: [PATCH 1/2] chore(agents): remove the pi_agenta harness experiment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pi_agenta (Pi plus a forced Agenta overlay: an AGENTS.md preamble, a persona append-system, force-unioned platform skills) was an experiment we never shipped to users. Remove it from the harness enum, the SDK adapter and its overlay constants, the capability and catalog tables, the runner's advertised harness list and subscription probes, the UI harness metadata, the platform skill bodies that taught it, and the reference docs. Compatibility, deliberately kept: revisions saved while the value existed may still carry it, so every read boundary maps 'pi_agenta' to 'pi_core' instead of refusing the config — HarnessKind.coerce in the SDK, buildRunPlan / isPiHarness / harnessKind in the runner, and the web picker filter. The runner replay fixtures that carry the old spelling still pass unchanged, which pins the alias. Not touched here: the generated Fern client's HarnessKind union (web/packages/agenta-api-client) regenerates from the API schema on its own codegen step; the stale extra member is types-only and harmless until then. Claude-Session: https://claude.ai/code/session_014s6jqKCsVKsNMmnrJVJkZt --- .../documentation/adapters/pi.md | 2 +- .../documentation/agent-configuration.md | 19 +-- .../documentation/architecture.md | 16 +- .../agent-workflows/documentation/skills.md | 2 +- .../agents/01-agent-configuration.mdx | 7 +- sdks/python/agenta/__init__.py | 1 - sdks/python/agenta/sdk/agents/__init__.py | 4 - .../agenta/sdk/agents/adapters/__init__.py | 4 +- .../sdk/agents/adapters/agenta_builtins.py | 122 +++----------- .../agenta/sdk/agents/adapters/harnesses.py | 55 +------ .../sdk/agents/adapters/sandbox_agent.py | 3 +- sdks/python/agenta/sdk/agents/capabilities.py | 18 +-- sdks/python/agenta/sdk/agents/dtos.py | 27 ++-- .../python/agenta/sdk/agents/model_catalog.py | 4 +- sdks/python/agenta/sdk/agents/wire_models.py | 2 +- sdks/python/agenta/sdk/utils/types.py | 5 +- .../agents/_fake_runner_backend.py | 2 +- .../agents/connections/test_capabilities.py | 21 ++- .../agents/connections/test_model_catalog.py | 4 +- .../agents/test_agent_composition_seam.py | 4 +- .../agents/test_dtos_capabilities_events.py | 3 +- .../unit/agents/test_harness_adapters.py | 151 +----------------- .../unit/agents/test_harness_identity.py | 2 +- .../pytest/unit/agents/test_wire_contract.py | 32 +--- .../pytest/unit/agents/test_wire_models.py | 3 +- ...test_batch_fold_stream_contract_routing.py | 4 +- ...nvoke_real_handlers_negotiation_routing.py | 4 +- .../oss/tests/pytest/unit/agent/conftest.py | 1 - .../unit/agent/test_builtin_uri_binding.py | 2 +- .../pytest/unit/agent/test_invoke_handler.py | 19 +-- .../engines/sandbox_agent/pi-model-config.ts | 1 + .../src/engines/sandbox_agent/run-plan.ts | 3 +- .../src/lifecycle/reconciliation-router.ts | 5 +- services/runner/src/protocol.ts | 5 +- services/runner/src/subscription-status.ts | 10 +- services/runner/src/version.ts | 2 +- services/runner/tests/unit/server.test.ts | 1 - .../tests/unit/subscription-status.test.ts | 27 +--- .../src/secret/core/agentModelCandidates.ts | 1 + .../SchemaControls/HarnessSelectControl.tsx | 2 +- .../agentTemplate/useModelHarness.tsx | 1 + .../DrillInView/SchemaControls/harnessMeta.ts | 6 +- .../agenta-ui/src/SelectLLMProvider/utils.ts | 1 - .../HarnessSelectControl.stories.tsx | 10 +- 44 files changed, 126 insertions(+), 492 deletions(-) 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..945d2e4aae 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, @@ -30,11 +28,7 @@ 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.harnesses import _normalize_tool_specs, _opt_str @@ -97,24 +91,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 +124,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 +287,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 +297,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 +330,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 +406,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 /> From 2c96b8e20aa8144516b068daf2a6261c400770ff Mon Sep 17 00:00:00 2001 From: Mahmoud Mabrouk Date: Mon, 31 Aug 2026 17:35:17 +0200 Subject: [PATCH 2/2] chore(agents): fix unused harness test import Removing the pi_agenta experiment dropped the last use of GETTING_STARTED_WITH_AGENTA_SKILL in this test but left the import, which fails the CI ruff lint. Mirrors fa9b1039 pushed on this branch. Claude-Session: https://claude.ai/code/session_0165tsjmvf3qvTPFcb9EV44g --- .../oss/tests/pytest/unit/agents/test_harness_adapters.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) 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 945d2e4aae..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 @@ -27,10 +27,7 @@ UnsupportedHarnessError, make_harness, ) -from agenta.sdk.agents.adapters.agenta_builtins import ( - GETTING_STARTED_WITH_AGENTA_SKILL, - 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,