@henryqw/pi-subagent
Delegate bounded single, parallel, or chained tasks to isolated Pi roles.
Package details
Install @henryqw/pi-subagent from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@henryqw/pi-subagent- Package
@henryqw/pi-subagent- Version
15.1.4- Published
- Sep 8, 2026
- Downloads
- 13.2K/mo · 5,305/wk
- Author
- henrywang
- License
- MIT
- Types
- extension, skill
- Size
- 279.7 KB
- Dependencies
- 3 dependencies · 4 peers
Pi manifest JSON
{
"extensions": [
"./extensions/subagent.ts"
],
"skills": [
"./skills"
],
"image": "https://raw.githubusercontent.com/HenryQW/pi-harness/main/extensions/pi-subagent/example.png"
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
@henryqw/pi-subagent
Delegate bounded work from Main to isolated Pi Roles, or run independent implementation units through a fixed Git Flow. One Role system covers focused research, review, implementation, parallel work, and ordered chains without loading every detail into Main.
Install
pi install npm:@henryqw/pi-task-models
pi install npm:@henryqw/pi-subagent
Run /task-models and configure the fast profile before delegating. Open /task-models again and verify that fast no longer says not configured.
Works with
| Package | Relationship | Purpose |
|---|---|---|
@henryqw/pi-task-models |
Required | Supplies fast, balanced, frontier, and fav model routes. |
Routes come from ~/.pi/agent/config/pi-task-models/config.json. It stores explicit task overrides. Missing shared model config warns once because delegation needs a route.
Use
Start with one read-only delegation:
{
"role": "scout",
"name": "Map sign-in flow",
"task": "Trace the sign-in request from entry point to session creation. Report the relevant files and unresolved risks. Do not edit files."
}
A separate child returns a bounded report to Main. It creates no saved Pi session and makes model requests through the selected route.
| Surface | Type | Purpose |
|---|---|---|
delegate_task |
tool | Run one bounded task, independent tasks in parallel, or dependent tasks in a chain. |
delegate_flow |
tool | Implement and integrate 1–8 independent Git units. |
delegate_flow_continue |
tool | Repair the one blocked Flow unit once. |
pi-subagent-delegated-development |
skill | Guide Main's planning and orchestration. |
Pi's built-in tool block shows each call and result.
Select exactly one delegate_task shape:
// Single
{ role, name, task, model?, modelClass?, background? }
// Parallel: 1–8 independent tasks
{ tasks: [{ role, name, task, model?, modelClass? }], background? }
// Chain: 1–8 dependent tasks
{ chain: [{ role, name, task, model?, modelClass? }], background? }
Main supplies each name. It must be a short description, about five words and fewer than 30 characters. Names cannot contain C0/C1 control characters such as newlines or terminal escapes. role and an explicit model also reject those controls.
modelClass selects fast, balanced, frontier, or fav. An explicit call class wins over a Role class. Without either, the configured task assignment or declared default applies. The route sets the model and exact thinking level.
An explicit model (provider/modelId) replaces only the route model and must support that thinking level. background applies to the whole selected mode, never one entry.
Parallel tasks start together, settle together, and report in input order. Chains are sequential and fail at the first failure. {previous} passes only the immediately preceding successful assistant output.
Foreground failures throw after keeping bounded sibling and recovery evidence. One call has one aggregate 50 KiB cap for Main-visible text. Final results show summaries first and full evidence below.
The status widget shows each task group name above at most three indented child rows. Each row shows a one-letter Role badge, status, activity, usage, and duration. Flow stages for one unit share that unit's heading.
Background work belongs to its launching session. Shutdown or reload aborts it and may leave only recoverable-work evidence or no follow-up message.
Each entry resolves its own Role, resources, route, and optional isolation. A Role with isolation: worktree gets a separate deterministic worktree when available. Non-Git and unborn-HEAD contexts can use Main's directory. Other setup failures, including unsafe submodule layouts, reject instead of falling back. Siblings and chain steps never share a created worktree.
See the orchestration guide for full delegation, transport, isolation, and UI behavior.
Skills
The bundled pi-subagent-delegated-development Skill guides Main's planning and orchestration. It adds no runtime code, config, or Role installation.
Implementers remove only task-created temporary, generated, or ignored artifacts. Required deliverables and unrelated files stay intact. They never use git clean or blanket deletion, and unclear paths block.
For known regressions with a runner that supports test-name filtering, use a test-name filter. Keep broad package or workspace checks to one caller-owned final validation after relevant units integrate. Flow itself does not run that check.
Before delegating:
- Keep trivial, single-owner, mechanically verifiable edits in Main.
- For literal UI or copy defects, search the exact text first. Read only its producer and nearby assertions unless ownership remains unclear.
- Find concrete outcomes that can ship on their own.
- Split only those outcomes. Give each one owner and a focused check.
- Run independent work in parallel.
- Prefer parallel delegation when at least two outcomes are independent.
- Use only as many units as independent outcomes require. Never create units to reach a count.
Its ordinary review loop is optional. Use it only when the caller or repository policy explicitly requires judgment review.
- Call
delegate_taskwithrole: "reviewer"to select the effectivereviewerRole. - Its task packet must state the read-only scope and exact
PASSor findings contract. Include exact acceptance criteria and validation evidence. - The Reviewer must see exact candidate evidence from its working directory. Use
delegate_flowfor an isolated candidate, not an ordinary review from Main's unchanged checkout. - Fix initial findings together. Validate repaired inputs once before focused re-review. Include the original findings and acceptance criteria, exact repaired-candidate evidence, and validation evidence.
- Only
PASScompletes the loop. Surface and block on re-review findings or empty output. Retry empty output only when explicit caller policy requires one. A second empty result blocks. Do not add another round.
Flow is separate. It owns exact review evidence, exact PASS approval, validation replay, one repair continuation, and no automatic retry.
Flow
Flow requires a clean Main worktree on an attached branch with a committed HEAD. Use it only for independent Git changes that can merge in any order.
Keep work together or run it in order when a split divides an invariant or adds coordination. Do not split units with overlapping mutable ownership. Do not split units that overlap files, APIs, schemas, generated output, package metadata, lockfiles, or invariants.
One Implementer launch must plausibly finish before the configured maximum runtime. Cohesion is not enough when work has several preservable, separately verifiable milestones. Split oversized dependent work into serial one-unit Flows after each milestone integrates. Units in one Flow stay independent and commuting.
delegate_flow({ units: [{ id, name, task, modelClass?, validation: [{ command, args }], review? }] })
delegate_flow_continue({ guidance, modelClass? })
A Flow has 1–8 units with unique non-empty IDs and allows one active Flow. It freezes the effective Implementer at start. It freezes the Reviewer only when a unit requests review.
- Each unit gets one worktree. Implementers run in parallel. Flow handles units in declared order.
- Flow runs each declared command with its arguments. That validation is authoritative for objective checks.
- Without
review, Flow fast-forwards the exact validated tip. - With
review, the Reviewer receives the exact{base, tip, patchPath}packet and must return exactlyPASSbefore the same integration path. Usereviewonly for stated judgment that validation cannot decide. - Trust a successful Flow result. Do not re-read integrated files or repeat its validation merely to confirm it.
A Role selects base tools, extensions, named Skills, instructions, and optional worktree isolation. Named Skills resolve from Main's effective Pi registry. Unavailable names warn and skip.
Children disable ambient extension and Skill discovery. tools: [] adds no base tools, but selected extension tools and caller tools still activate. extensions: [] adds no Role extension bundle. skills: [] adds no separately named Role Skills, but selected extension Skills still load.
Parent-only delegation tools and ask_question are always excluded. Requested Role or caller tool names are checked after provider loading. Unavailable tools fail before the first model turn.
An explicit unit modelClass overrides both frozen Roles. Without one, each Role uses its own modelClass, configured pi-subagent/delegateTask assignment, or declared default.
One delegate_flow_continue can repair an Implementer, validation, or review block in the same worktree. Omitting its class keeps the unit's explicit class and frozen Role defaults. Supplying one replaces both Role defaults for that repair and its later Reviewer launch.
A second block is terminal. Rebase and infrastructure failures are terminal.
Flow has no dependency graph, saved recovery, automatic retry, aggregate review, or post-merge validation.
Config
pi-subagent owns ~/.pi/agent/config/pi-subagent/config.json. It is optional. A missing file uses these defaults without a warning.
| Name | Description | Values | Default |
|---|---|---|---|
maxSubagents |
Sets the maximum number of active child processes. | Safe integer of at least 1. | 5 |
maxTurns |
Sets the hard provider-turn limit for each child. | Safe integer of at least 1. | 50 |
maxTokens |
Sets the token limit for each child. | Safe integer of at least 1. | Unlimited |
timeout.idleMinutes |
Sets the idle timeout for a child. | Positive minutes; minutes × 60,000 ≤ 2,147,483,647 ms | 10 |
timeout.maxMinutes |
Sets the maximum runtime for a child. | Positive minutes greater than idleMinutes; minutes × 60,000 ≤ 2,147,483,647 ms |
30 |
maxTokens applies separately to every child. This includes delegate_task and each Flow Implementer, Reviewer, and repair launch. It is not a shared pool or per-call option. Set it only in this file.
Pi adds each completed assistant response's Usage.totalTokens once. This matches the executor's aggregate Usage. At 80%, a Role receives one convergence warning.
A terminal response that crosses maxTokens succeeds. A continuing crossing turn completes its tools. Pi then disables tools and allows one response-only handoff. That handoff can overshoot the limit, so maxTokens is not an exact cap. Further continuation rejects with token_limit, aggregate Usage, and bounded last output.
Excess children wait FIFO without using a child timeout. A terminal response on turn 50 succeeds; an attempted continuation rejects with turn_limit.
Final response handoff
Role launches reserve a response-only handoff at a continuing maxTokens crossing or the penultimate maxTurns turn. This includes delegate_task and every Implementer or Reviewer launch within delegate_flow.
With maxTurns set to 1, Pi disables tools at startup. The sole provider turn is the response-only handoff.
Pi waits for the current turn's tools. It then disables all tools and requests a final report. A terminal boundary response gets no handoff.
The fixed decision packet asks for Status (completed, blocked, or incomplete), one-sentence Outcome, up to three concrete Evidence facts, Blocker, one material Risk, and one Suggested next action. It is the default. Exact output required by the assigned task or Role takes precedence. The child returns only that output, such as a Flow Reviewer's exact PASS or caller-required structured output. The handoff stays within maxTurns, but it is the one allowed turn after a token crossing. Commits, validation, and retained-worktree facts from executor/Flow structured evidence remain authoritative; the model handoff supplies semantic context and a suggested next action.
A raw createEphemeralSubagentExecutor launch can enforce the extra-turn window. It cannot guarantee disabled tools or the final handoff. A timeout, provider failure, or child-process failure can also end a Role launch before handoff.
Malformed or unreadable JSON, a non-object root, unknown keys, and invalid values produce one warning. Invalid settings use defaults while valid settings still apply. If the effective maximum is not greater than the idle timeout, both timeout settings use defaults. The file is never rewritten.
PI_SUBAGENT_MAX_SUBAGENTS overrides maxSubagents for the session. It must be a positive integer. An invalid value prevents the extension from loading, so delegate_task is unavailable.
Roles
Role Markdown files live beside the config file. They require frontmatter and a Markdown system prompt body.
| Field | Requirement |
|---|---|
name |
Required unique, non-empty text without C0/C1 controls. |
description |
Required non-empty text without C0/C1 controls. |
modelClass |
Optional; fast, balanced, frontier, or fav. |
tools |
Required YAML array of non-empty tool names. [] selects no base built-ins. |
isolation |
Optional; only worktree. |
extensions |
Required YAML array. Entries are absolute paths, ~/…, file://, or npm:, git:, github:, https?:, or ssh: sources. |
skills |
Required YAML array of non-empty Skill names. |
| body | Required Markdown system prompt after the frontmatter. |
A Role's modelClass is a default. A call-level or Flow-unit class wins.
An unreadable or invalid Role fails loading fast. Duplicate Role names are rejected. A same-named user file overrides a built-in Role.
The package always provides these built-in Roles. Their files leave modelClass unset, so they use the configured pi-subagent/delegateTask assignment or declared default unless a call or Flow unit overrides it:
| Role | Purpose | Isolation/use |
|---|---|---|
implementer |
Make and validate one focused change. | Requests a worktree; commits scoped work locally. Never pushes or opens a PR without permission. |
reviewer |
Review supplied plans or files for correctness. | Read-only. For Flow review, reads the exact packet in the Unit Worktree; never edits or commits. |
scout |
Map code and evidence for one bounded task. | Read-only and generic delegate_task only; never changes files. |
Flow uses the effective Implementer and, only when requested, Reviewer. The Scout is not part of Flow.
API
The package root includes these main exports:
| Surface | Type | Purpose |
|---|---|---|
loadRoles |
function | Loads built-in and user Role definitions. |
resolveRoleSkills |
function | Resolves a Role's named Skills from Pi's effective registry. |
resolveRoleLaunch |
function | Resolves a Role, route, and launch resources. |
createRoleLaunch |
function | Builds launch arguments from a resolved route. |
createEphemeralSubagentExecutor |
function | Creates the bounded child-process executor. |
| Worktree helpers | functions | Create, inspect, finalize, and report child worktrees. |
The executor works only inside the active Pi process. It does not discover or start a standalone Node.js Pi installation.
finalizeChildWorktree returns the breaking WorktreePayload lifecycle union. pruned proves zero commits, a clean tree, and removed worktree and branch. retained contains measured commits and dirty values. recovery has an actionable note and only completed measurements. An omitted recovery measurement is unknown.
See the public Role and executor API for contracts and a prepare example. Pass modelClass to resolveRoleLaunch to override a Role default.
Limits and recovery
An explicitly selected extension is trusted, not sandboxed. Its tools, Skills, and executable behavior load together. Select fewer trusted extensions to reduce scope. pi-subagent does not guess or remove undocumented dependencies.
Flow never force-deletes recoverable work. Failed or uncertain units, and cleanup refusals after integration, retain their worktree path or branch for recovery.
See Flow mechanics and recovery for retained-work recovery.
