@buihongduc132/pi-acp-agents

extensionmaintained

Pi extension: ACP agent client — spawn and control ACP-compatible agents (Gemini CLI, etc.) from within pi

by · v0.5.1 · published 2d ago

$ pi install npm:@buihongduc132/pi-acp-agents
downloads/mo
330
stars
8
last push
11h ago
open issues
9

Signals

license: MITtestspi manifest: missinginstall size: —deps: 0peer deps: 0

Download trend

1.5K downloads · last 12 weeks (weekly)

README

@buihongduc132/pi-acp-agents

Multi-agent orchestration for pi — spawn, control, and coordinate ACP-compatible agents (Gemini CLI, Claude, Codex, custom) as first-class tools within the pi coding agent.

npm version CI license


Table of Contents


What works vs what does not

Status as of 0.5.0. Verified by full test suite (npx vitest run → 2081 passed / 0 failed / 104 skipped / 1 todo). Consolidated tool surface: 9 tools registered (7 ACP core + 2 ACP hooks policy), down from the legacy 33-tool surface. See Tool surface.

✅ Working

CapabilityNotes
acp_spawn (single agent)Spawn a session + optional prompt; absorbs legacy acp_prompt / acp_session_new
acp_msg (messaging)Session prompt/steer/cancel + mailbox send/list; absorbs acp_message
acp_status (diagnostic)Agent list, sessions, circuit breaker; absorbs acp_doctor / acp_runtime_info
acp_fanout (broadcast/compare)Same prompt → N agents; absorbs acp_broadcast / acp_compare
acp_governancePlan request/resolve + model policy
acp_task (action: create|update)Unified task tool: status / assignee / deps / bulk * filter
acp_dag (action: submit|status|cancel)Wave-based topological DAG execution, persistent resume
acp_hooks_policy_get / acp_hooks_policy_setRuntime hooks failure-policy inspection + mutation
Alias resolver (failover + race)AliasResolver class — sequential fallback OR parallel first-wins with cancel of losers
Circuit breaker3 failures → open, 60s → half-open, auto-recover
Health monitor30s background polling; distinct no-response vs completed-idle timers
Session-scoped storestasks/mailboxes/governance/workers partitioned per host session ID; session-archive/session-name-registry/event-log global
Legacy migrationNon-destructive flat → legacy/ on first run after partitioning
DAG widgetdagIndexEntryToWidgetDag helper renders DAG state in TUI
TUI widgetReal-time session + DAG status panel
Gemini CLI adapterAuto-auth, default
Custom adapterAny ACP-speaking stdio agent
Stall timeoutPer-op with SIGTERM → SIGKILL escalation
EPIPE safetystdin/stdout broken-pipe handled
Tag-triggered CI publishgit push --follow-tags → npm publish with provenance

❌ Not working / not implemented

GapStatus
One-call parallel batch delegateNo single-call parallel batch — use acp_fanout (broadcast/compare) instead of spawning N sessions manually
Plan approval flow as toolacp_plan_request / acp_plan_resolve are command-only stubs, not tools
Hooks policyNo hooks_policy_* — retry governance absent
Model policy as toolacp_model_policy_get / _check are command-only
Predefined teamsNo predefined_teams_*
Workspace isolation (worktree mode)Workers use cwd only — no git worktree isolation
Context inheritanceNo contextMode: "branch" (clone leader session)
task_dep_lsOnly add/rm via task_update — cannot list blockers
task_get (single)Not exposed — only create + update
Diagnostics as toolsacp_doctor, acp_runtime_info, acp_event_log, acp_env, acp_cleanup are slash commands only
Session lifecycle toolsacp_session_list / _shutdown / _kill / _prune / _set_model / _set_mode are not exposed (automation-only)
Streaming responsesPlanned — currently returns after full prompt completes
Tool use forwardingPlanned — ACP agent tool calls not relayed back to pi
OAuth / token authPlanned — env vars only
Config hot-reloadManual restart required
Retry with backoffCircuit breaker handles fail-closed; no exponential backoff
Session sharing across pi instancesSingle-host only
Metrics export (Prometheus)Planned
Agent routing (auto-select)Manual via alias or explicit
Ensemble / chain-of-agentsManual via DAG composition
Cost trackingPlanned
Claude Code / Codex ACP adaptersPending upstream ACP mode

⚠️ Known surface drift

src/settings/config.ts ships a legacy ACP_TOOL_NAMES array of 42 entries (a settings toggle schema) while only 16 tools are actually registered in index.ts. The 26-entry delta is NOT missing tools — it is the per-tool enable/disable toggle schema for /acp settings. See ../pi-plugins/flow/intentions/pi-acp-agents/tool-consolidation.md for the planned consolidation (42 → 7 multiplexed tools).


Install

For humans

npm install @buihongduc132/pi-acp-agents

For AI agents

Add to ~/.pi/agent/settings.json:

{
  "packages": ["npm:@buihongduc132/pi-acp-agents"]
}

Or:

pi install npm:@buihongduc132/pi-acp-agents

Git-sourced

{
  "gitPackages": [
    { "url": "https://github.com/buihongduc132/pi-acp-agents.git" }
  ]
}

Quick start

  1. Install an ACP agent (Gemini CLI default):

    gemini --version
    gemini  # first run to authenticate
    
  2. (Optional) Configure:

    mkdir -p ~/.pi/acp-agents
    cat > ~/.pi/acp-agents/config.json << 'EOF'
    {
      "agent_servers": {
        "gemini": {
          "command": "gemini",
          "args": ["--acp"],
          "default_model": "gemini-2.5-pro"
        }
      },
      "defaultAgent": "gemini"
    }
    EOF
    
  3. Use in pi:

    Use the acp_spawn tool to spawn a gemini session, then acp_msg to ask "What is the capital of France?"
    

For full usage patterns, DAG examples, alias configuration, and worker orchestration see docs/USAGE.md.


Tool surface

9 tools registered (7 ACP core + 2 hooks policy, gated by /acp settings per-tool toggles):

ToolPurpose
acp_spawnSpawn a session + optional prompt (absorbs acp_prompt / acp_session_new)
acp_msgSession prompt/steer/cancel + mailbox send/list (absorbs acp_message / acp_cancel)
acp_governancePlan request/resolve + model policy (absorbs acp_plan_, acp_model_policy_)
acp_statusAgents, sessions, circuit breaker, cleanup, prune (absorbs acp_doctor / acp_runtime_info / acp_cleanup)
acp_fanoutBroadcast to N agents + compare mode (absorbs acp_broadcast / acp_compare)
acp_taskUnified task tool: action:'create' or action:'update' (absorbs acp_task_create / acp_task_update)
acp_dagDAG delegation: action:'submit'|'status'|'cancel' (absorbs acp_dag_submit / _status / _cancel)
acp_hooks_policy_getInspect the ACP hooks failure policy
acp_hooks_policy_setUpdate the ACP hooks failure policy

Slash command surface (/acp ...):

/acp session <new|load|list|shutdown|kill|prune|set-model|set-mode|cancel>
/acp prompt
/acp delegate
/acp broadcast
/acp compare
/acp task <create|list|get|assign|set-status|dep-add|dep-rm|clear>
/acp message <send|list>
/acp plan <request|resolve>
/acp runtime <status|config|env|info|event-log|cleanup|doctor>
/acp settings — configure tool visibility
Aliases: /acp-doctor, /acp-config

For tool-parameter reference and examples see docs/USAGE.md.


DAG delegation

Submit a complete DAG of ACP agent tasks in a single call. The DAG executor:

  • Validates statically (cycles, dangling refs, duplicate IDs, agent availability)
  • Persists state to disk under <runtimeDir>/dag/
  • Executes in topological wave-order (parallel within wave, serial across waves)
  • Resumes automatically after pi restart (running steps retried, completed steps skipped)

Submission JSON

{
  "tasks": [
    { "id": "analyze", "agent": "gemini", "prompt": "Analyze the codebase for security issues" },
    { "id": "fix", "agent": "claude", "prompt": "Fix the issues: {analyze.output}", "dependsOn": ["analyze"] },
    { "id": "verify", "agent": "gemini", "prompt": "Verify: {fix.output}", "dependsOn": ["fix"], "gate": "after" }
  ],
  "args": { "project": "my-app" },
  "options": { "failFast": true, "maxRetries": 0 },
  "cwd": "/path/to/project"
}

Template variables

VariableResolves to
{<step-id>.output}Output of referenced step (truncated to dagOutputTruncateChars)
{<step-id>.status}Terminal status (completed / failed / skipped / cancelled)
{dag.args.<key>}Workflow arg from args at submission

Unresolvable variables fail the step at dispatch time.

Gates

GateBehavior
needs (default)Success-gate — all deps must complete. Failure cascades.
afterCompletion-gate — deps just need a terminal state. Use for cleanup/verify steps.

Config

OptionDefaultDescription
dagStaleTimeoutMs3_600_000 (1h)No step transitions for this long → stale
dagOutputTruncateChars8000Max chars injected into downstream prompts

For DAG executor internals, persistence model, and resume semantics see docs/USAGE.md.


Alias resolver + fallback chains

Resolves an alias name to a concrete agent using configurable strategies. Used internally by acp_spawn / acp_fanout when called with an alias name.

StrategyBehavior
failover (sequential)Try agents in order; first success wins; throws AllAgentsFailedError if all fail
race (parallel)Send to all healthy agents in parallel; first success cancels losers (default race timeout 30s)

Both strategies consult the circuit breaker (isHealthyFn) before dispatching and skip unhealthy agents.

For alias configuration schema and examples see docs/USAGE.md.


Persistent workers

Spawn long-lived named workers via acp_spawn. Workers persist across task completions and are managed via the unified tools:

Workers share the caller's filesystem (no git worktree isolation). For parallel worktree-isolated delegation use the teams pi-plugin instead.


Async run lifecycle (telemetry, steer, interrupt, resume, worktree)

Async runs (spawned with async: true or acp_dag) now support full lifecycle management, aligning ACP with subagents:

Fleet view

acp_status({ action: "fleet" })  // or acp_status({ view: "fleet" })

Returns compact list of active runs with telemetry (turns, toolCalls, tokensUsed, filesWritten, lastActivityAt).

Interrupt / Resume

acp_status({ action: "interrupt", id: "<runId>" })   // abort in-flight turn → needs-attention
acp_status({ action: "resume", id: "<runId>", message: "Continue with X" })

Silent-failure detection

Runs that complete with zero tool calls, zero file writes, AND empty text are auto-flagged as failed with error: "silent-no-output".

Worktree isolation

acp_spawn({ agent: "gemini", prompt: "...", worktree: true })          // isolated git worktree
acp_spawn({ agent: "gemini", prompt: "...", worktree: true, keepWorktree: true })  // keep after run

Creates a git worktree at <cwd>/.worktrees/acp-<runId>. Cleaned up on run dispose unless keepWorktree: true.


Configuration

Config file: ~/.pi/acp-agents/config.json

{
  "agent_servers": {
    "gemini": { "command": "gemini", "args": ["--acp"], "default_model": "gemini-2.5-pro" },
    "custom": { "command": "/path/to/my-acp-agent", "args": ["--mode", "acp"] }
  },
  "defaultAgent": "gemini",
  "staleTimeoutMs": 3600000,
  "healthCheckIntervalMs": 30000,
  "circuitBreakerMaxFailures": 3,
  "circuitBreakerResetMs": 60000,
  "stallTimeoutMs": 3600000
}

Global

FieldDefaultDescription
agent_servers{ gemini: {...} }Map of agent name → config
defaultAgent"gemini"Agent used when not specified
staleTimeoutMs3600000 (1h)Auto-close: stalled-no-response AND completed-idle
healthCheckIntervalMs30000 (30s)Background health polling interval
circuitBreakerMaxFailures3Consecutive failures before circuit opens
circuitBreakerResetMs60000 (60s)Time before circuit half-opens
stallTimeoutMs3600000 (1h)Per-operation timeout
logsDir~/.pi/acp-agents/logsLog directory
dagStaleTimeoutMs3600000DAG stale threshold
dagOutputTruncateChars8000DAG downstream prompt truncation

Per-agent

FieldRequiredDescription
commandyesExecutable to spawn
argsnoArgs (e.g. ["--acp"])
envnoExtra env vars
cwdnoWorking dir override
default_modelnoDefault model ID

For aliases, model policy, and runtime store paths see docs/USAGE.md.


Architecture

┌─────────────────────────────────────────────────────┐
│                    pi agent                          │
│                                                      │
│  acp_spawn ───┐                                      │
│  acp_status ──┤                                      │
│  acp_msg ─────┤──► AliasResolver ──► Coordinator ──┐ │
│  acp_dag ─────┤                       │            │ │
│  acp_fanout ──┤                       ▼            │ │
│  acp_task ────┤              AcpCircuitBreaker     │ │
│  acp_governance┘                       │            │ │
│                              ┌────────┴────────┐   │ │
│                              │ Adapter Factory │   │ │
│                              └────┬───────┬────┘   │ │
│                            GeminiAdapter│          │ │
│                                  CustomAdapter     │ │
│                                       │            │ │
│                              AcpClient (stdio)     │ │
│                                       │            │ │
│                              HealthMonitor ◄───────┤ │
│                              SessionManager        │ │
│                              DagStore + DagExecutor│ │
│                              WorkerStore           │ │
│                              SessionStoreFactory   │ │
└─────────────────────────────────────────────────────┘

Patterns

PatternImplementation
AdapterAcpAgentAdapterGeminiAcpAdapter / CustomAcpAdapter
FactorycreateAdapter() — string dispatch
Circuit breakerClosed → Open → Half-Open with configurable thresholds
Health monitorBackground polling; distinct no-response and completed-idle auto-close
CoordinatorMulti-agent delegate / broadcast / compare
Alias resolverfailover (sequential) + race (parallel first-wins)
DAG executorTopological wave-execution + persistent resume
Session-store factoryPer-host-session lazy-instantiated stores (4 session-scoped + 3 global)

Resilience

FeatureDefaultDescription
Circuit breaker3 failures → openAuto-recovers after 60s in half-open state
Stall timeout1 hourPer-operation timeout with SIGTERM → SIGKILL escalation
Health polling30sBackground monitor with separate no-response and completed-idle timers
Busy mutexper-sessionPrevents concurrent prompts on the same session
Process safetySIGTERM → SIGKILLGraceful shutdown with escalation
EPIPE handlingstdin / stdoutPrevents crashes on broken pipes
Non-blockingall pathsErrors return as tool error results, never unhandled throws
Alias failoverautomaticSequential fallback on agent failure
Alias race30s timeoutParallel first-wins with cancel of losers
DAG resumeon pi restartresumeAll() discovers running DAGs, skips completed steps

Logs

Central logs at ~/.pi/acp-agents/logs/:

  • main.log — general structured JSON log
  • session-{id}/trace.jsonl — per-session ACP JSON-RPC traces
  • <runtimeDir>/dag/<dagId>.json + dag-index.json — DAG state persistence
  • <runtimeDir>/<sessionId>/{tasks,mailboxes,governance,workers}.json — session-scoped stores
  • <runtimeDir>/{events,session-archive,session-name-registry}.jsonl|json — global stores

Supported agents

AgentStatusConfig
Gemini CLI✅ Built-incommand: "gemini", args: ["--acp"]
Claude Code🔜 PlannedACP mode pending upstream
Codex🔜 PlannedACP mode pending upstream
Custom✅ Via CustomAcpAdapterAny command speaking ACP over stdio

Development

npm install
npm test              # all tests
npm run test:ci       # with coverage
npm run typecheck     # TypeScript validation
npm run publish:dry   # verify package contents before publish

Release process

npm run release:patch    # 0.4.0 → 0.4.1
npm run release:minor    # 0.4.0 → 0.5.0
npm run release:beta     # 0.4.0 → 0.4.1-beta.0
git push --follow-tags   # triggers CI → auto-publish with provenance

Further documentation

TopicLocation
Full usage guide (DAG examples, alias config, worker patterns, slash command reference)docs/USAGE.md
Tool consolidation plan (33 → 7 ACP core multiplexed tools; 9 total with hooks policy)../pi-plugins/flow/intentions/pi-acp-agents/tool-consolidation.md
ACP vs teams gap analysis../pi-plugins/flow/findings/acp-vs-teams-analysis.md
DAG delegation designopenspec/changes/archive/2026-06-20-acp-dag-delegation/
DAG widget designopenspec/changes/archive/2026-06-22-acp-dag-widget/
Session-scoped runtime storesopenspec/changes/archive/2026-06-19-scope-runtime-stores-per-session/
Branch consolidation record (this release)docs/branch-consolidation-2026-06-22.md
OpenSpec changesopenspec/changes/
Specs (canonical)openspec/specs/