@henryqw/pi-subagent

extensionmaintained

Delegate bounded direct work or durable checked isolated task graphs to Pi roles.

by — · v22.2.0 · published 1d ago

$ pi install npm:@henryqw/pi-subagent
downloads/mo
11.4K
stars
8
last push
1d ago
open issues
4

Signals

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

Download trend

No downloads in the last 12 weeks.

README

@henryqw/pi-subagent

Delegate work to configured Pi Roles with Herdr. Read-only direct tasks run in Herdr tabs in your current workspace; checked implementation runs in isolated Herdr worktrees. Both return a handle so Main can continue while they work.

Pi showing six delegated tasks running in parallel

Install

pi install npm:@henryqw/pi-task-models
pi install npm:@henryqw/pi-subagent

Run /task-models and configure the fast, balanced, frontier, and fav routes you use.

Install pi-mcp-adapter only when a Role declares an MCP server allowlist:

pi install npm:pi-mcp-adapter

Use

Main, its coordinator extension, and a Herdr subagent through checked work, worker failure, and coordinator interruption

The coordinator is extension code running in Main's Pi process, not another agent. It persists the request, launches the Herdr subagent, checks its committed work, and reports saved results; Main chooses what to stage and promote. If a worker fails, the coordinator reports attention after the wave settles, not necessarily immediately while siblings work. If Main's Pi process stops, no follow-up is guaranteed: restart and run /subagent recover in the canonical repository to classify orphaned interruptions and send Main one recovery report. Main then uses subagent_status for exact evidence and deliberate next actions. A stale running state does not prove a worker is still active; interrupted or ambiguous actions are never replayed automatically.

Ask Main to delegate a bounded task, such as “Have a scout trace sign-in without editing files.” Main uses delegate_task; you receive progress and a result or an actionable failure while Main remains available.

Commands are for you; tools and the packaged skill are for Main. You do not need to call agent tools or manage candidate identities yourself.

In the TUI, the compact widget uses one row per visible agent/task: D or I marks direct or isolated work, and [S] or [I3] identifies the Role initial (plus the isolated model class: 1 fast, 2 balanced, 3 frontier, * fav). Direct rows show the launched model, thinking level, elapsed time, and measured session tokens (— tok until usage is available). Isolated rows show the recorded task state and any retained workspace; their status glyphs use the active TUI theme, while the text still names the status without color. Herdr does not currently provide reliable live model, thinking, or token readings for those rows. Attention appears before ordinary work, with a +N more summary when direct rows overflow. A ready isolated candidate is not promoted to Main. Use /subagent or subagent_status for exact identities, failures, and recovery.

SurfaceTypePurpose
/subagentcommandBrowse work, launch isolated status inspection in a separate Herdr tab with the fast model, or send/edit pending instructions. Requires interactive TUI or RPC; inspection also requires a Herdr-managed pane and a read-only scout Role.
/subagent recovercommandClassify orphaned isolated work in the current canonical repository and send Main one follow-up with request IDs, blockers, and reported next actions. No UI or Herdr pane required.
delegate_tasktoolStart read-only direct work or a durable isolated checked graph.
subagent_statustoolRead durable isolated state and the exact allowed continuation without replaying work.
subagent_resumetoolPerform the reported retry, verify, or finalize continuation; does not replace staging or integration.
subagent_stagetoolStage or resolve an exact candidate, or reject/revise it; rejection of a staged candidate freezes that generation.
subagent_integratetoolAdvance staged dependents, refresh after clean Main drift, validate, record one correction, promote, reconcile interrupted promotion, clean up proven promotion, or explicitly release rejected/superseded resources.
subagent_aborttoolAbort an isolated request only when no retained candidates or integration worktrees remain; cannot discard them.
pi-subagentskillGuide Main through delegation, authorization, checks, integration, and recovery.

How Main routes delegation

Main chooses the mode from your request; you do not need to select one. Words like “direct” and “isolated” can signal your intent, but the work determines the route. Main declares the chosen mode in every delegate_task call, and the extension never falls back between modes.

ModeRequest that triggers itCheckout behavior
directRead-only research, analysis, or reviewOpens non-focused Herdr tabs in Main's current workspace. No worktree is created.
isolatedImplementation or a checked task graphRuns changesets in owned Herdr worktrees; Main selects, validates, and promotes exact candidates.

Keep trivial mechanically verifiable work in Main. Keep tightly coupled changes under one owner rather than splitting by file count.

Direct delegation

A read-only task:

{
  "mode": "direct",
  "role": "scout",
  "name": "Map sign-in flow",
  "task": "Trace sign-in through session creation. Report files and risks. Do not edit files."
}

Direct mode selects exactly one shape:

Single:   { mode: "direct", role, name, task, ... }
Parallel: { mode: "direct", tasks: [{ role, name, task, ... }] }
Chain:    { mode: "direct", chain: [{ role, name, task, ... }] }

Only Roles with known read-only tools and no extensions or MCP servers may run direct. Write-capable Roles and direct changeset tasks are rejected; use isolated mode for implementation. Parallel tasks run independently; chains replace each literal {previous} with the preceding successful answer and stop on failure.

The tool returns a task ID and first Herdr tab after launch, not the answer. The extension observes each worker and sends one result to Main when the workflow finishes. If Main is busy, Pi queues it after the current turn; if idle, it starts a turn. A blocked, stalled, unknown, or truncated result is not reported as success. Session switch or shutdown stops observation and preserves tab identities for recovery. Open /subagent on the session branch to inspect exact tabs, agents, and Pi session files, including tabs launched after the first. A recorded tab may no longer be running; locally observed work is labelled separately.

Isolated checked graphs

An isolated request has one durable ID, one goal, and 1–8 typed tasks:

{
  "mode": "isolated",
  "id": "refresh-repair",
  "goal": "Repair token refresh with exact checked evidence.",
  "tasks": [
    {
      "id": "inspect",
      "kind": "text",
      "role": "scout",
      "modelClass": "fast",
      "requirements": "Identify the refresh failure and relevant tests.",
      "deliverable": "A concise evidence report.",
      "dependsOn": [],
      "contextFrom": []
    },
    {
      "id": "repair",
      "kind": "changeset",
      "role": "implementer",
      "modelClass": "balanced",
      "requirements": "Implement the smallest correct repair.",
      "deliverable": "A committed candidate in the owned worktree.",
      "dependsOn": ["inspect"],
      "contextFrom": ["inspect"],
      "checks": [
        { "command": "pnpm", "args": ["test", "--", "refresh"] }
      ],
      "judgment": {
        "role": "reviewer",
        "modelClass": "balanced",
        "criterion": "The exact candidate fixes refresh and preserves unrelated behavior."
      }
    }
  ],
  "finalChecks": [
    { "command": "pnpm", "args": ["test", "--", "refresh"] }
  ]
}

dependsOn controls scheduling. contextFrom may name completed text tasks and preserves the declared order. Changesets require focused task checks; a graph with changesets requires final checks. A canonical root package.json test script is required: the runner adds pnpm test to final checks if missing, and Main runs it on the combined integration checkout before promotion. Checks and judgments bind to exact Git identities; candidate drift, Main drift, mutation during validation, ambiguity, or conflicts stop promotion and retain evidence.

delegate_task, subagent_resume, and subagent_integrate advance return a durable request ID after the wave is saved; productive work continues while Main is free. Main can do other work or end its turn instead of sleeping or polling. Pi sends a follow-up in the launching session whenever a checked worker becomes ready, and again when the wave finishes or needs attention. Use subagent_status if delivery is missed or you need exact current state.

The worker produces a committed candidate without tracked or untracked changes, then runs focused preliminary checks and optional judgment. Ignored generated files do not block candidacy, but prevent automatic worktree cleanup; inspect them before deleting retained work. The runner records each ready candidate immediately but does not integrate it automatically. Main can inspect subagent_status and use subagent_stage to select the exact candidate in an owned integration worktree while siblings are still working; conflicts are resolved there. Stage and resolve are available during an active wave, but rejection and revision wait for the wave to settle. subagent_integrate can advance dependent tasks from a staged snapshot, validate the combined tip with full root checks and optional final judgment after the wave settles, and promote it to Main only after exact validation. A definitive failed combined check/review allows one committed correction in the integration checkout followed by another validation. Main must explicitly promote; promotion and cleanup never silently discard retained work.

Open /subagent without arguments to pick work by label. The menu shows direct records on this session branch and isolated requests in the current canonical Git checkout (including requests launched in other sessions). Pick an isolated request and Inspect status and recovery to launch a non-focused Herdr tab with a read-only scout Role on the configured fast route. Its prompt contains bounded saved status (including retained paths, but not raw launch environment or session transcripts); the analysis stays in that tab and does not queue a Main turn. The tab and session file are recorded for recovery even if launch becomes uncertain. The read-only subagent_status tool remains Main's authority for exact identities before any stage or recovery action. If Herdr or the read-only fast route is unavailable, inspection fails without changing the request; Main can use subagent_status. Refresh or choose completed/history for older work. Invalid state IDs are warned about and preserved. Outside Git or when isolated inventory is unavailable, direct recovery still works. Browsing does not resume work or change resources.

For a locally active unsealed isolated changeset, choose Send follow-up instructions to open a native multiline editor; submission queues the instruction, not a completed revision. Choose Edit queued instructions to withdraw all still-pending instructions for that selected task before the editor opens. Their full text appears in FIFO order separated by blank lines. Explicit submission queues one replacement at the tail. Cancel leaves the withdrawn instructions removed; already-claimed instructions, initial prompts, and automatic corrections cannot be recalled. A worker may seal during editing: failed submission reopens the full submitted text for deliberate editing or copying, never automatically requeues it. Combined text above the single-instruction limit is preserved in the editor but rejected until shortened. Cancelling before withdrawal makes no change. Neither action touches Main's editor or Pi's global Option+Up queue shortcut. No-UI sessions can run /subagent recover or use agent tools for individual inspection and recovery.

A queued follow-up runs after the current turn settles and must produce a new clean commit that passes preliminary checks again. When no queued revision remains, the checked candidate seals immediately; there is no guaranteed post-completion editing window. Late follow-ups fail visibly.

Isolated recovery

After a coordinator interruption, run /subagent recover once. It acquires the repository productive lease before changing any orphaned pending or running state, preserves unreadable state files and retained candidates, and reports blockers and existing continuations to Main without replaying prompts, merges, promotion, or cleanup. If another productive run holds the lease, it only reads status and reports the live owner; wait rather than taking over. Repeating the command is safe. Main uses subagent_status for exact identities and the recovery tools in the interface table above for deliberate decisions; the command never stages or promotes. Direct Herdr tab recovery remains in /subagent.

The following details describe exact identity and retained-work requirements.

Pass the exact generation, candidate (taskId, attempt, candidate) and expectedTip identities reported by status to stage; pass the generation and combined expectedTip to integrate. If Main advances cleanly before promotion, call subagent_integrate refresh with the recorded expectedMain, old expectedTip, and new clean newMain. Main must restage selected immutable candidates into the new generation and rerun combined checks. Dirty or divergent Main blocks refresh. Stale identities fail. If integration allocation was uncertain, subagent_stage resolve first proves the owned checkout is still at its base before starting the selected merge; otherwise it retains the intent. At most two integration worktrees may remain unreleased per request; a superseded generation is read-only. To release a superseded clean integration checkout, call subagent_integrate with { id, action: "release", generation, expectedTip }, using that generation's last staged tip (or integration base when no stage completed). To release a rejected candidate after its worker has terminated and all generations using it are released, pass { id, action: "release", generation: <latest generation number, or 1>, taskId, attempt, expectedTip: <candidate tip> }. Release persists each host/checkout/branch step, verifies ownership and cleanliness (including ignored files), and deletes the branch only if its ref still names the exact tip. Dirty, conflicted, changed, or uncertain resources remain for manual inspection; retry the same release after resolving the obstacle. Released checkouts free a retained slot; abort is available after every retained resource is released. Rejection can invalidate dependent work; if fresh dependent execution is impossible, start a new request instead of forcing promotion. Productive requests have no whole-run wall-clock deadline. Resume does not reset the recorded policy or correction count. Child limits, abort signals, process I/O, status inspection, exact termination, and cleanup retain finite safety bounds.

The extension never pushes, opens a pull request, publishes, deploys, force-cleans recoverable work, or silently falls back to Main.

Config

pi-subagent owns ~/.pi/agent/config/pi-subagent/config.json. A missing file silently uses defaults.

NameDescriptionValuesDefault
maxSubagentsConcurrent direct Herdr workers and ephemeral child-process limitSafe integer ≥ 15
maxTurnsProvider-turn limit per childSafe integer ≥ 150
maxTokensOptional token limit per childSafe integer ≥ 1Unlimited
maxCorrectionsSame-worker automatic corrections per isolated requestSafe integer ≥ 01
timeout.idleMinutesDirect worker and ephemeral child idle timeoutPositive and within Node's timer range10
timeout.maxMinutesEphemeral child hard runtimeGreater than idle and within Node's timer range30

Limits come only from this global file. Request fields cannot override or replenish them. Existing durable requests keep their recorded policy, while a lower current correction limit can tighten recovery. There is intentionally no whole-run timeout setting.

Malformed or unreadable JSON, unknown keys, and invalid values block delegation with one actionable warning. The file is preserved and never rewritten automatically.

Roles

Role Markdown lives in ~/.pi/agent/config/pi-subagent/ and requires frontmatter plus a Markdown system prompt.

FieldRequirement
name, descriptionRequired non-empty text without terminal control characters
modelClassOptional fast, balanced, frontier, or fav default
toolsRequired array of base tool names; [] selects none
extensionsRequired array of trusted absolute paths or supported package sources
skillsRequired array of effective Pi Skill names
mcpsOptional exact MCP server names; omitted or [] denies MCP access
bodyRequired system instructions

Roles describe responsibility and capabilities. They do not choose isolation; each request does. A same-named user Role overrides a built-in Role. The package ships implementer, reviewer, and scout.

Children disable ambient extension and Skill discovery. Only declared resources and required internal policy adapters load. Missing Skills, tools, MCP servers, Roles, or routes fail before productive work starts. Main-only delegation and recovery tools plus ask_question are excluded from children.

API

The package root exports the Role loader and launch APIs, the FIFO ephemeral executor, child-worktree helpers, exact review and working-change evidence helpers, checked isolated schemas, runtimes, and runner types.

createEphemeralSubagentExecutor accepts global concurrency, turn/token, idle, and hard-runtime policy. Queued time consumes no child timeout. A run resolves resources only after receiving its permit and accepts abort, output, token, and activity callbacks. Output and diagnostics are bounded.

See Orchestration and package-author API for the detailed contracts and recovery model.

State and storage

Durable state is private under config/pi-subagent/state/. State v5 rejects v4 and older files rather than migrating them; retain the old state and recover its work manually.

Limits and recovery

Role extensions and MCP servers are trusted executable code, not a sandbox. Select the smallest resource set. Read-only direct Roles cannot write through their declared tools. This is a capability check, not an OS sandbox; external processes and changes to Main's checkout can still make a concurrent read stale. Retained-work reports identify exact resources for deliberate recovery.