@pi-kaush/pi-simple-subagents

Delegate background tasks or persistent interactive workers to isolated Pi processes, with execution profiles, bounded delegation, and process-tree cleanup.

Packages

Package details

extension

Install @pi-kaush/pi-simple-subagents from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:@pi-kaush/pi-simple-subagents
Package
@pi-kaush/pi-simple-subagents
Version
0.3.1
Published
Oct 14, 2026
Downloads
410/mo · 133/wk
Author
kaushikgopal
License
MIT
Types
extension
Size
428.4 KB
Dependencies
1 dependency · 4 peers
Pi manifest JSON
{
  "extensions": [
    "./src/index.ts"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

pi-simple-subagents

The default mode runs bounded tasks in the background and returns structured output. Interactive workers keep their Pi session between assignments.

Despite the name, this is the full-featured subagent tool — "simple" refers to the bounded default: tasks in, structured results out.

Quick start

pi install npm:@pi-kaush/pi-simple-subagents

The tool needs two things on disk before it is useful: Running the tool with { "action": "init" } writes both starter files when they are missing; it never overwrites existing files.

  1. At least one agent definition. A subagent call names an agent, and an unknown name fails with the list of available agents. Create ~/.pi/agent/agents/bee.md:

    ---
    emoji: 🐝
    profile: quick
    ---
    
    You are a general worker. Do the delegated task and report the result
    concisely.

    Run /reload after adding or renaming agents. See Agents.

  2. ~/.pi/agent/profiles.yaml. The extension loads it at startup and fails to register without it. A minimal file:

    version: 1
    profiles:
      quick:
        description: Fast, low-cost execution for simple tasks.
        candidates:
          - model: anthropic/claude-haiku-4-5

    Use any provider/model you are authenticated for. The schema lives in @pi-kaush/pi-model-profiles.

Examples

Ask the parent model in plain language; it chooses the subagent call.

Background runs (the default) return a result inside the current turn:

  • Use a quick bee subagent to count the .ts files under src and reply with the number only.
  • In parallel, have two quick bee subagents summarize README.md and CHANGELOG.md in one sentence each.
  • Chain: a bee subagent lists the exported functions in src/index.ts, then a bee subagent writes one line describing {previous}.
  • Press Esc during a run to stop every child; partial results are kept.

Interactive workers keep their session and report back on their own:

  • Start an interactive bee worker: read src/index.ts and tell me in 3 bullets what it does. In Herdr it opens in an unfocused pane below. Outside Herdr, it uses the headless RPC fallback and warns that no interactive pane is available. The result arrives without a wait call.
  • Send that worker a follow-up: list any timers it creates. Same session, new assignment, second report.
  • Steer that worker: focus only on error handling. Folds into the running task.
  • Stop the worker. then Resume it and ask what it remembers. Context is kept; interrupted work is never replayed.
  • /subagent opens a picker to inspect, focus, message, or stop workers.

Interactive workers survive /reload, come back idle after pi --continue, and each result is reported once. Background runs and interactive workers share five slots; when idle workers hold them all, background runs fail immediately and name workers to stop.

What it adds

  • subagent, an LLM-callable tool with three task forms:
    • single — one agent, one task
    • parallel — a tasks[] batch with an optional shared immutable context
    • chain — sequential steps where {previous} placeholders receive the prior step's output
  • Execution profiles (quick, coder, default, thinker, deep-thinker): ordered model-candidate ladders from the shared machine-local ~/.pi/agent/profiles.yaml, shared with @pi-kaush/pi-agent-mode and @pi-kaush/pi-model-profiles. Profiles reload on file change — ladder edits apply to long-running sessions without a restart.
  • Model-facing discovery: user agents are read from ~/.pi/agent/agents/*.md when the extension loads and listed alongside the execution profiles in the tool guidance. Agent names select behavior and tools; profile names select compute. When both appear in a request, their order does not matter: thinker redteam and redteam thinker select the same pair.
  • Bounded delegation: depth 2, five children per call, five active children per Pi session, at most thirty simultaneously active descendants. Completed bounded children release their slots; idle interactive workers do not.
  • Subtree-safe lifecycle: depth-1 children lead POSIX process groups; abort, timeout, and session shutdown terminate the whole subtree (graceful then forced). Windows uses taskkill /T.
  • Child watchdogs: two hours total runtime and fifteen minutes without output by default; either configurable in src/limits.json, 0 disables. The orchestrator is never watchdogged.
  • Resumable child sessions: children persist real Pi sessions, named subagent(<agent>): <task preview>. Expanded tool output shows the full session ID with a pi --session <id> resume hint and the session file path for live tail -f viewing. Disable with persistChildSessions: false.
  • Transcript artifacts: bounded children's complete stdout/stderr streams to a private JSONL artifact under ~/.pi/agent/subagent-runs/; interactive workers retain Pi session transcripts, bounded status previews, and assignment result archives under ~/.pi/agent/managed-subagents/.
  • Structured yield: delegated children finish through a terminating yield tool (completed, blocked, failed), with artifact paths; ordinary final assistant output remains a fallback.
  • Model resolution: before delegation, refresh Pi's authenticated model catalog locally; select profile candidates and bare model overrides from that catalog, not the parent's enabledModels / scopedModels cycling list. An explicit model overrides an invocation profile, which overrides the agent's profile frontmatter, then its legacy model field. Bare ids resolve family-scoped by nearest version; qualified refs pass through to the child for validation.

Interactive mode

The default action: "run" runs background work. It supports single, parallel, and chain tasks. Use action: "interactive" for persistent workers. An interactive worker keeps its session for later assignments. In Herdr, it opens a pane. Interactive mode accepts one agent/task or a tasks[] batch with optional shared context. It does not support chains.

{ "action": "interactive", "agent": "coder", "task": "Implement the parser", "profile": "coder" }
{ "action": "send", "handle": "mw-…", "message": "Also cover empty input" }
{ "action": "wait", "handle": "mw-…", "assignmentId": "a-…" }

Use list and status to inspect workers, send to steer active work, and wait to collect a specific assignment. delivery: "followUp" queues a separate assignment. Interrupted tasks are never replayed automatically; use the returned assignment ID to avoid collecting an older result.

Finished assignments report themselves. Each terminal result (completed, blocked, failed, timed out, aborted, interrupted, or cancelled) arrives in the parent session as one bounded managed-subagent-result message with the worker's identity, model, result preview, artifacts, usage, and transcript path. An idle parent starts a turn to present the answer; a busy parent receives it as a follow-up after the current turn. Steers merged into a running assignment are reported with it. Reports are recorded in the parent session, so /reload and restarts neither repeat nor lose them, and results already collected with wait are not reported again. Receipts follow the active branch: after /tree navigation, a result reported only on the abandoned branch is reported again. Each report is sent at most three times. wait remains available for deterministic collection inside a turn. The interactive-worker status line hides exited or stopped workers once their results are reported or collected; live idle workers stay visible.

Interactive worker status is published as the extension status below-footer:managed-subagents, one worker per line, at most five rows plus a +N more row. With pi-footer-minimal installed, each line renders as its own dimmed row below the footer. Without it, Pi's native footer joins every extension status into one line, so the worker rows appear inline with other statuses, separated by spaces and clipped to the terminal width.

Interactive launch confirmations and controls stay in the expanded tool output. IDs are explicitly labeled worker-handle [mw-…] and assignment-id [a-…]; expand the completion message to inspect status, task, usage, and transcript details. The collapsed completion message adds no card or metadata above the parent's answer. The parent is instructed to present brief answers directly without launch or completion announcements, task recaps, IDs, or closing commentary. This is model guidance, not filtering or rewriting its messages.

A worker handle identifies the reusable child session; use it with status, send, stop, and resume. Each task or follow-up has an assignment ID; pass it to wait alongside the handle to collect that specific result, not whichever task finished most recently. Routine use needs neither copied ID: automatic reports and /subagent provide the normal workflow.

Interactive workers share the session's concurrency gate with background runs. An idle worker keeps a slot until it exits, is stopped, or is suspended. stop retains the worker's Pi session and transcripts; resume restarts it. Parent shutdown suspends interactive workers and releases their slots. They resume idle with their saved context when that parent session starts again; interrupted instructions and uncertain message deliveries are never replayed automatically. /reload leaves interactive workers running. If interactive workers hold every slot, background runs fail immediately and list workers to stop. Interactive sessions persist even when persistChildSessions is disabled for background runs.

In Herdr, each interactive worker runs in an unfocused, extension-owned pane split down from the caller; later workers stack below earlier ones. The /subagent picker can inspect it or focus it for native TUI use. Outside Herdr, interactive workers run through Pi RPC and remain headless. Interactive mode warns when no pane is available; the expanded tool output and picker give a fish-safe command for the saved session and tell you to stop a running headless worker before opening it elsewhere. That command opens a standalone session, not a live attachment: close it before resuming the worker from the parent. Open never resumes an unsupported headless worker implicitly. /reload refreshes the default host adapter without moving existing worker tabs or restarting processes; subsequent launches use the current split layout. Host-specific launch, focus, and cleanup remain behind the existing host port for future multiplexer adapters.

Future direction: Pi Durable's multiplayer model could let multiple clients observe and steer the same running worker without starting a second writer. One process owns storage; clients attach to that owner. This could solve the headless-to-interactive attachment problem: opening pi --session today starts another process, not a client of the existing worker. Durable is an experimental, separate framework, not a drop-in attachment feature for these Pi CLI workers. Adopting it would require a worker backend and UI adapter; it does not make independent concurrent storage writers safe.

Herdr workers use the Herdr server's provider environment and the shared Pi agent directory. Parent-only exported credentials are not copied into launch arguments. Blocked trust or permission prompts are never automatically approved.

Profiles and model overrides are resolved when an interactive worker starts and reused for later assignments. Explicit models override invocation profiles, which override agent profiles and legacy agent models. The parent's cycling model scope is not inherited. Isolation flags are opt-in; noMcp is currently supported only for background runs (requires Pi 1.0.4 or newer), and noExtensions explicitly loads this package's child bridge and yield tool.

The offline RPC smoke runs in the ordinary test suite. Native Herdr smoke is opt-in because it creates and closes a real unfocused split pane:

env PI_MANAGED_HERDR_SMOKE=1 npx vitest run extensions/pi-simple-subagents/test/subagent-managed-cli.test.ts

Both smoke tests use a deterministic local provider, not network model calls.

Agents

Agent definitions are Markdown files with optional frontmatter (emoji, profile, model, tools, confirmProjectAgents). Declare either profile or model, not both. Discovery:

  1. user: ~/.pi/agent/agents/*.md
  2. project: nearest .pi/agents/*.md, with agentScope: "project" | "both"

User agents are advertised to the parent model at extension load. Additions or renames need /reload to refresh that catalog. Project agents remain dynamically discoverable by exact name without being embedded in the global catalog. Project-controlled agents prompt for confirmation before first use.

Example agents

Two worked agent definitions live in examples/agents: a read-only reviewer and a plan-only architect. Both show the handback contract this extension delegates well: findings or decisions with severities, the checks already run, and the checks that remain. Adapt them; they are not loaded automatically. Their shape is adapted from the bundled agents in championswimmer/pi-subagent-manager (MIT).

Configuration

src/limits.json bounds delegation (validated against hard ceilings):

{
  "maxDepth": 2,
  "maxChildrenPerCall": 5,
  "maxConcurrency": 5,
  "maxRuntimeMs": 7200000,
  "maxInactivityMs": 900000,
  "persistChildSessions": true
}

The model-profiles schema lives in @pi-kaush/pi-model-profiles.