@buihongduc132/pi-acp-agents
Pi extension: ACP agent client — spawn and control ACP-compatible agents (Gemini CLI, etc.) from within pi
Package details
Install @buihongduc132/pi-acp-agents from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@buihongduc132/pi-acp-agents- Package
@buihongduc132/pi-acp-agents- Version
0.5.0- Published
- Jul 9, 2026
- Downloads
- 928/mo · 98/wk
- Author
- buihongduc132
- License
- MIT
- Types
- extension, skill
- Size
- 507.1 KB
- Dependencies
- 2 dependencies · 2 peers
Pi manifest JSON
{
"extensions": [
"./index.ts"
],
"skills": [
"./skills"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
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.
Table of Contents
- What works vs what does not ← read this first
- Install
- Quick start
- Tool surface
- DAG delegation
- Alias resolver + fallback chains
- Persistent workers
- Configuration
- Architecture
- Resilience
- Logs
- Supported agents
- Development
- Release process
- Further documentation
What works vs what does not
Status as of 0.4.0. Verified by full test suite (npx vitest run → 1627 passed / 0 failed / 83 skipped / 1 todo). Counts: 16 tools registered (verified via rg -c 'pi.registerTool' index.ts), 42 entries in ACP_TOOL_NAMES legacy schema.
✅ Working
| Capability | Notes |
|---|---|
acp_prompt (single agent) |
Session create / reuse / archive-reload |
acp_status (diagnostic) |
Agent list, sessions, circuit breaker |
acp_cancel (in-flight prompt abort) |
Calls adapter.cancel(), archives handle |
acp_broadcast (one prompt → N agents) |
Scoped to caller session's agents |
acp_task_create / acp_task_update |
Multiplexed: status / assignee / deps / bulk * filter |
acp_message (send + list) |
DM / steer / broadcast via kind param |
acp_dag_submit / acp_dag_status / acp_dag_cancel |
Wave-based topological DAG execution, persistent resume |
acp_worker_spawn / _list / _steer / _shutdown / _kill / _prune |
Persistent named workers in WorkerStore |
| Alias resolver (failover + race) | AliasResolver class — sequential fallback OR parallel first-wins with cancel of losers |
| Circuit breaker | 3 failures → open, 60s → half-open, auto-recover |
| Health monitor | 30s background polling; distinct no-response vs completed-idle timers |
| Session-scoped stores | tasks/mailboxes/governance/workers partitioned per host session ID; session-archive/session-name-registry/event-log global |
| Legacy migration | Non-destructive flat → legacy/ on first run after partitioning |
| DAG widget | dagIndexEntryToWidgetDag helper renders DAG state in TUI |
| TUI widget | Real-time session + DAG status panel |
| Gemini CLI adapter | Auto-auth, default |
| Custom adapter | Any ACP-speaking stdio agent |
| Stall timeout | Per-op with SIGTERM → SIGKILL escalation |
| EPIPE safety | stdin/stdout broken-pipe handled |
| Tag-triggered CI publish | git push --follow-tags → npm publish with provenance |
❌ Not working / not implemented
| Gap | Status |
|---|---|
| One-call parallel batch delegate | No acp_delegate_parallel — must acp_worker_spawn × N + acp_prompt × N |
| Plan approval flow as tool | acp_plan_request / acp_plan_resolve are command-only stubs, not tools |
| Hooks policy | No hooks_policy_* — retry governance absent |
| Model policy as tool | acp_model_policy_get / _check are command-only |
| Predefined teams | No predefined_teams_* |
| Workspace isolation (worktree mode) | Workers use cwd only — no git worktree isolation |
| Context inheritance | No contextMode: "branch" (clone leader session) |
task_dep_ls |
Only add/rm via task_update — cannot list blockers |
task_get (single) |
Not exposed — only create + update |
| Diagnostics as tools | acp_doctor, acp_runtime_info, acp_event_log, acp_env, acp_cleanup are slash commands only |
| Session lifecycle tools | acp_session_list / _shutdown / _kill / _prune / _set_model / _set_mode are not exposed (automation-only) |
| Streaming responses | Planned — currently returns after full prompt completes |
| Tool use forwarding | Planned — ACP agent tool calls not relayed back to pi |
| OAuth / token auth | Planned — env vars only |
| Config hot-reload | Manual restart required |
| Retry with backoff | Circuit breaker handles fail-closed; no exponential backoff |
| Session sharing across pi instances | Single-host only |
| Metrics export (Prometheus) | Planned |
| Agent routing (auto-select) | Manual via alias or explicit |
| Ensemble / chain-of-agents | Manual via DAG composition |
| Cost tracking | Planned |
| Claude Code / Codex ACP adapters | Pending 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
Install an ACP agent (Gemini CLI default):
gemini --version gemini # first run to authenticate(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" } EOFUse in pi:
Use the acp_prompt tool to ask gemini "What is the capital of France?"
For full usage patterns, DAG examples, alias configuration, and worker orchestration see docs/USAGE.md.
Tool surface
16 tools registered (gated by /acp settings per-tool toggles):
| Tool | Purpose |
|---|---|
acp_prompt |
Send a prompt to an ACP agent, get the text response |
acp_status |
Show configured agents, active sessions, circuit breaker state |
acp_cancel |
Cancel an ongoing prompt by ID or friendly name |
acp_broadcast |
Send same prompt to multiple agents in parallel |
acp_task_create |
Create a persistent task in the runtime task store |
acp_task_update |
Multiplexed mutations: status, assignee, deps, result, bulk * |
acp_message |
Send or list messages (dm / steer / broadcast) |
acp_dag_submit |
Submit a DAG of tasks (validates, persists, starts background exec) |
acp_dag_status |
Get DAG state by dagId, or list all DAGs when called without it |
acp_dag_cancel |
Cancel a running DAG |
acp_worker_spawn |
Spawn a persistent named worker |
acp_worker_list |
List workers + status |
acp_worker_steer |
In-flight redirect — inject context mid-prompt |
acp_worker_shutdown |
Graceful shutdown |
acp_worker_kill |
Force kill |
acp_worker_prune |
Prune stale workers |
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
| Variable | Resolves 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
| Gate | Behavior |
|---|---|
needs (default) |
Success-gate — all deps must complete. Failure cascades. |
after |
Completion-gate — deps just need a terminal state. Use for cleanup/verify steps. |
Config
| Option | Default | Description |
|---|---|---|
dagStaleTimeoutMs |
3_600_000 (1h) |
No step transitions for this long → stale |
dagOutputTruncateChars |
8000 |
Max 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_prompt / acp_delegate when called with an alias name.
| Strategy | Behavior |
|---|---|
| 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_worker_spawn. Workers persist across task completions and are managed via:
| Tool | Purpose |
|---|---|
acp_worker_spawn |
Spawn worker + bind to ACP session + optional init prompt |
acp_worker_list |
List workers + status (online / offline / busy / disposed) |
acp_worker_steer |
In-flight redirect — inject context mid-prompt |
acp_worker_shutdown |
Graceful shutdown |
acp_worker_kill |
Force kill |
acp_worker_prune |
Prune stale workers |
Workers share the caller's filesystem (no git worktree isolation). For parallel worktree-isolated delegation use the teams pi-plugin instead.
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
| Field | Default | Description |
|---|---|---|
agent_servers |
{ gemini: {...} } |
Map of agent name → config |
defaultAgent |
"gemini" |
Agent used when not specified |
staleTimeoutMs |
3600000 (1h) |
Auto-close: stalled-no-response AND completed-idle |
healthCheckIntervalMs |
30000 (30s) |
Background health polling interval |
circuitBreakerMaxFailures |
3 |
Consecutive failures before circuit opens |
circuitBreakerResetMs |
60000 (60s) |
Time before circuit half-opens |
stallTimeoutMs |
3600000 (1h) |
Per-operation timeout |
logsDir |
~/.pi/acp-agents/logs |
Log directory |
dagStaleTimeoutMs |
3600000 |
DAG stale threshold |
dagOutputTruncateChars |
8000 |
DAG downstream prompt truncation |
Per-agent
| Field | Required | Description |
|---|---|---|
command |
yes | Executable to spawn |
args |
no | Args (e.g. ["--acp"]) |
env |
no | Extra env vars |
cwd |
no | Working dir override |
default_model |
no | Default model ID |
For aliases, model policy, and runtime store paths see docs/USAGE.md.
Architecture
┌─────────────────────────────────────────────────────┐
│ pi agent │
│ │
│ acp_prompt ──┐ │
│ acp_status ──┤ │
│ acp_cancel ──┤──► AliasResolver ──► Coordinator ──┐ │
│ acp_dag_* ───┤ │ │ │
│ acp_worker_* ┤ ▼ │ │
│ acp_message ─┤ AcpCircuitBreaker │ │
│ acp_task_* ──┘ │ │ │
│ ┌────────┴────────┐ │ │
│ │ Adapter Factory │ │ │
│ └────┬───────┬────┘ │ │
│ GeminiAdapter│ │ │
│ CustomAdapter │ │
│ │ │ │
│ AcpClient (stdio) │ │
│ │ │ │
│ HealthMonitor ◄───────┤ │
│ SessionManager │ │
│ DagStore + DagExecutor│ │
│ WorkerStore │ │
│ SessionStoreFactory │ │
└─────────────────────────────────────────────────────┘
Patterns
| Pattern | Implementation |
|---|---|
| Adapter | AcpAgentAdapter → GeminiAcpAdapter / CustomAcpAdapter |
| Factory | createAdapter() — string dispatch |
| Circuit breaker | Closed → Open → Half-Open with configurable thresholds |
| Health monitor | Background polling; distinct no-response and completed-idle auto-close |
| Coordinator | Multi-agent delegate / broadcast / compare |
| Alias resolver | failover (sequential) + race (parallel first-wins) |
| DAG executor | Topological wave-execution + persistent resume |
| Session-store factory | Per-host-session lazy-instantiated stores (4 session-scoped + 3 global) |
Resilience
| Feature | Default | Description |
|---|---|---|
| Circuit breaker | 3 failures → open | Auto-recovers after 60s in half-open state |
| Stall timeout | 1 hour | Per-operation timeout with SIGTERM → SIGKILL escalation |
| Health polling | 30s | Background monitor with separate no-response and completed-idle timers |
| Busy mutex | per-session | Prevents concurrent prompts on the same session |
| Process safety | SIGTERM → SIGKILL | Graceful shutdown with escalation |
| EPIPE handling | stdin / stdout | Prevents crashes on broken pipes |
| Non-blocking | all paths | Errors return as tool error results, never unhandled throws |
| Alias failover | automatic | Sequential fallback on agent failure |
| Alias race | 30s timeout | Parallel first-wins with cancel of losers |
| DAG resume | on pi restart | resumeAll() discovers running DAGs, skips completed steps |
Logs
Central logs at ~/.pi/acp-agents/logs/:
main.log— general structured JSON logsession-{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
| Agent | Status | Config |
|---|---|---|
| Gemini CLI | ✅ Built-in | command: "gemini", args: ["--acp"] |
| Claude Code | 🔜 Planned | ACP mode pending upstream |
| Codex | 🔜 Planned | ACP mode pending upstream |
| Custom | ✅ Via CustomAcpAdapter |
Any 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
| Topic | Location |
|---|---|
| Full usage guide (DAG examples, alias config, worker patterns, slash command reference) | docs/USAGE.md |
| Tool consolidation plan (39 → 7 multiplexed tools) | ../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 design | openspec/changes/archive/2026-06-20-acp-dag-delegation/ |
| DAG widget design | openspec/changes/archive/2026-06-22-acp-dag-widget/ |
| Session-scoped runtime stores | openspec/changes/archive/2026-06-19-scope-runtime-stores-per-session/ |
| Branch consolidation record (this release) | docs/branch-consolidation-2026-06-22.md |
| OpenSpec changes | openspec/changes/ |
| Specs (canonical) | openspec/specs/ |