Unify Anthropic and OpenAI runtimes on pi-agent-core - #3246
Conversation
Introduces a dual-runtime architecture so the WordPress.com provider can
serve OpenAI models (currently GPT 5.5) alongside Claude. Routes by model
family at dispatch time, splits the runtime per family under
apps/cli/ai/runtimes/{anthropic,openai}, and reorganizes Studio tools into
one-file-per-tool. UI gets a cross-family confirmation dialog on /model
swap, and the recorder side learns to clean up runtime sidecars.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI lint flagged the SdkMcpToolDefinition type import as out of order — external-package types must come before local-path types. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…s split
- AI_MODELS is now an array of `{id, label, family}` so `getAiModelFamily`
is a lookup instead of a string-prefix heuristic. New `getAiModelLabel`
helper replaces direct `AI_MODELS[id]` access at all call sites; new
models become a one-line data change instead of two.
- The cross-family confirmation dialog moves from `SessionView` into the
composer (where the model picker lives) — composer owns the orchestration
and renders the dialog inline; `FamilySwitchConfirmDialog` extracted to
its own file for testability.
- Single-caller helpers in `apps/cli/ai/tools/utils.ts` move to where
they're used: `splitCommandArgs` → `wp-cli.ts` (private), and
`formatInvalidBlocks` → `validate-blocks.ts` (private). The four
preview tools share `runPreviewCommand` via a new `preview-helpers.ts`
rather than the general utils grab-bag.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Run every model — Claude Opus/Sonnet and GPT 5.5 — through a single agent loop backed by @mariozechner/pi-agent-core. The runtime picks between pi-ai's anthropic-messages and openai-completions providers based on the active model id, so the family-specific dispatch is now an internal detail rather than a runtime fork. Why: the parallel Anthropic-via-Claude-Agent-SDK + OpenAI-via-pi runtimes meant duplicating tool registration, transcript persistence, permission gating, and result-message synthesis. Two different cross- family resume guards. Two SDK message-shape contracts to keep in sync with the recorder JSONL. Adding a third model family would have meant adding a third runtime. One pi-based loop closes that gap. What changes for users / the desktop UI: - Cost reporting in the "Thought for Xs · N turns" footer now shows real numbers for both families instead of zeros — pi-ai reports per-stream Usage with cost.total via the model registry; the synthetic SDKMessage `result` carries those through. - Reasoning content surfaces as proper `thinking` content blocks on the assistant SDKMessage so the UI's "Claude is thinking" affordance works on Claude turns and on the GPT-5+ thinking-only fallback path. - Sidecar transcripts move from .openai-state.json → .pi-state.json next to each session JSONL. The loader still reads the legacy suffix so resumed sessions don't lose pi memory across the migration; the writer always uses the new name. Delete sweeps both. - @anthropic-ai/claude-agent-sdk drops from apps/cli/package.json and the forge build no longer needs to prune its vendor binaries on Windows code-sign. Tool authoring is unchanged: each tool file still imports a `tool()` helper with the same `(name, description, zodInputSchema, handler)` signature. The local `apps/cli/ai/tools/define-tool.ts` produces the same `SdkMcpToolDefinition` shape the pi adapter expects, and the `studio mcp` standalone server registers tools directly through the MCP SDK instead of going through the agent SDK's createSdkMcpServer. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
I think this is a really great idea. It could eventually allow users the freedom to use any model they want. |
|
Yes, but we need to ensure that there's no regressions. The pi based custom hardness has a different feeling that the agent SDK based one. I'm going to need a bit of testing to be able to land this. |
Reconciles the unified pi runtime with trunk's `permissionMode: 'auto'` / ad-hoc-gating-removal change (#3242). pi-agent-core ships no equivalent to the SDK's auto classifier, so this merge takes the middle path: - Drops the allow-once / allow-always / deny prompt machinery from `apps/cli/ai/security.ts` (matches trunk's "drop ad-hoc gating" intent while keeping a stable trust-root contract on the pi side). - Keeps a minimal static path allowlist — `STUDIO_ROOT`, `os.tmpdir()`, `/tmp` — enforced via pi's `beforeToolCall` hook in `runtimes/pi/`. Tool calls whose path arguments land outside the allowlist are blocked silently with `ACCESS_DENIED_MESSAGE`. Bash is intentionally not gated (its `command` arg isn't a structured path). - Removes `autoApprove` from `AgentRuntimeConfig` and `AiAgentConfig` (no consumers; the new gate has no escape hatch by design). - Rewrites `tests/security.test.ts` for the trimmed surface (`isPathGatedTool`, `resolveToolPath`, `isPathWithinTrustedRoot`, `findFirstPathOutsideTrustedRoots`). - Resolves `tests/agent.test.ts` to keep the pi-runtime dispatch tests (Claude → anthropic-messages, GPT → openai-completions). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
I tested this locally through the Studio eval runner with a Homeboy cross-rig bench comparing trunk/Claude Agent SDK against this branch/pi-agent-core. The direction looks promising, but I found three pi adapter issues that made the initial result look faster while producing no usable assistant output on the WP.com Anthropic path:
I patched those locally and added focused tests around auth headers, pi error messages,
I would treat that as encouraging, not conclusive. Before landing this, I think we should also cover at least one tool-call scenario, one denial/error scenario, and one small site-building scenario, because the current proof only validates the simplest assistant-message path. The local files I changed are Edit: posted by Franklin, Chris's clanker agent, on Chris's behalf. |
|
Closing this one, starting with less disruptive #3328 |
Related issues
How AI was used in this PR
Substantial AI-generated migration. The plan, the runtime rewrite, the test updates, the documentation prose, and the commit message were all produced by Claude Opus 4.7 working from a detailed exploration spec. A human reviewed every file before commit, but the diff is large and AI-shaped.
The user explicitly opted for a direct cutover (no feature flag), so Studio runs every model — Claude Sonnet/Opus and GPT 5.5 — through the new unified runtime starting on first launch with this branch.
This is a Proof of Concept PR per the AGENTS.md guidance for substantial vibe-coded changes — opening it as draft + labeled `Proof of Concept` so the team can evaluate the architecture before considering merge. Expect rounds of refinement on cache hit rate parity, the wpcom auth path, and tuning the Claude-side skill appendix narrowing before this is merge-ready.
Proposed Changes
Testing Instructions
CLI smoke (after `npm run cli:build && node apps/cli/dist/cli/main.mjs ai`):
Local site interactions:
Standalone MCP:
Resume across forks (desktop):
Pre-merge Checklist
🤖 Generated with Claude Code