@bermudi/pi-delegate

Delegate tool for the Pi coding agent.

Packages

Package details

extension

Install @bermudi/pi-delegate from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:@bermudi/pi-delegate
Package
@bermudi/pi-delegate
Version
0.1.20
Published
Sep 11, 2026
Downloads
2,170/mo · 754/wk
Author
bermudi
License
unknown
Types
extension
Size
765.6 KB
Dependencies
1 dependency · 0 peers
Pi manifest JSON
{
  "extensions": [
    "./delegate.ts"
  ]
}

Security note

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

README

pi-delegate

Repository: https://github.com/bermudi/pi-delegate

Delegate tool for the Pi coding agent — spawn subagents to run tasks in parallel, with async ticketing, session pooling, retries, and per-model concurrency limits.

Extracted from bermudi/agent-extensions as a standalone repo (full history preserved).

Install

Install a reviewed Git commit or release tag as a Pi package:

# Replace this with a reviewed commit or release tag; do not use a moving branch.
pi install git:github.com/bermudi/pi-delegate@<reviewed-commit-or-tag>

Remove any old delegate.ts symlink from ~/.pi/agent/extensions/ before starting Pi. Pi loads delegate.ts from the isolated Git package checkout; do not point a running Pi at this repository or at .build/delegate.bundle.ts. Start a fresh Pi process after updating the installed ref.

Usage

Use the built-in default profile to run a subagent with the live parent's model, thinking level, delegatable native tools, and base system prompt:

delegate({
  tasks: [{ agent: "default", prompt: "Investigate the auth module" }],
});

Parent extension/MCP tools are not copied, and project instructions are rebuilt for the task's cwd. Omit agent when you want an inline task using delegate's normal defaults instead.

The other built-ins are:

  • scout — read-only investigation with read, grep, find, and ls.
  • coder — implementation and verification with read, write, edit, and bash in the shared workspace.
  • reviewer — review with read and bash in the shared workspace by default. Set workspace: "scratch" when its project changes should be discarded.

Shared-write safety

Before starting work, Delegate resolves each task's real tools and physical Git root. If a task could mutate a shared tree that overlaps another task in the same call or a still-running sync/async dispatch, the new call is rejected before any subagent starts. Unknown tool names are treated as mutating. External processes are outside this in-process gate.

Operators can deliberately bypass this check by setting "allowUnsafeSharedWrites": true in ~/.pi/agent/delegate.json. This setting is intentionally absent from the model-facing tool API. It provides no isolation or rollback, and Delegate marks running and final results with a visible batch-level warning while it is active.

A same-named Markdown file can override any built-in (first definition wins across .pi/agents/, ~/.pi/agent/agents/, ~/.agents/, .claude/agents/, ~/.claude/agents/). A prompt-only override keeps the built-in's tools and workspace — scout stays read-only and reviewer stays shared unless the file explicitly sets tools or workspace. Fresh built-ins inherit the parent's exact model object and thinking level; an explicit model/thinking in the Markdown file replaces that inheritance. Task fields always win. For each unset model, thinking, or tools field on scout/coder/reviewer, an exact-parent-model override in agentOverridesByParentModel wins over the unconditional agentOverrides, which wins over explicit Markdown frontmatter. default is the exception: it ignores both delegate.json override maps, but task fields still win and explicit default.md fields still override inherited parent values. ~/.pi/agent/delegate.json is the only delegate config file (user scope, global), and edits apply from the next delegate call. Project-local configuration belongs in .pi/agents/*.md profiles or explicit task fields; Delegate ignores legacy delegate fields in project .pi/settings.json.

Disposable scratch workspace

For review, tests, or other commands whose project changes should be thrown away, run a one-shot task in a CoW copy:

delegate({
  tasks: [
    { prompt: "Review this change and run its tests", workspace: "scratch" },
  ],
});

Delegate reflink-copies the containing Git repository beside the original, runs the subagent in the corresponding copied directory, then deletes the copy. It requires Linux with /proc/self/fd, GNU cp, and a reflink-capable filesystem such as Btrfs; it never falls back to an expensive full copy. Scratch mode cannot use sessionId, resumeFrom, session actions, linked Git worktrees, or project symlinks that point outside the copied tree.

This protects the real project from ordinary relative writes. It is not a security sandbox: unrestricted commands and absolute paths can still reach the host filesystem.

Git-native isolated writers

Use workspace: "isolated" for sync or async one-shot coding tasks that should run in parallel without sharing a working tree:

delegate({
  tasks: [
    { prompt: "Implement the parser change", workspace: "isolated" },
    { prompt: "Update the parser tests", workspace: "isolated" },
  ],
});

Delegate snapshots tracked changes, deletions, and non-ignored untracked files into a private baseline commit without touching the user's index or branch. Each task gets a detached worktree. Successful proposals are captured as private refs and full binary patches, then reconciled in task-array order. A proposal applies all-or-nothing; conflicts retain the proposal ref, full patch, and conflict worktree. Before updating the source, Delegate checks that it still matches the baseline.

A clean reconciliation reports applied_unverified: textual merging does not prove the code builds or tests pass, so the result includes a suggested verification call. Async isolated tasks prepare after returning their ticket and settle only after reconciliation. Cancellation never applies unfinished work; a completed proposal cancelled before source application is retained as a private ref and full patch. Isolated mode requires Git, rejects session reuse, disables whole-task retries, and rejects repositories with submodules. It is separation, not confinement: absolute-path writes can still reach the host.

Token accounting

Sync delegate calls report aggregate subagent Usage on the tool result, so Pi (0.81+) folds those tokens and cost into the parent footer and session total automatically — no manual addition needed.

Async tickets can't be auto-counted: their results arrive as a follow-up message, which has no usage slot. The per-call aggregate (Nk tokens) is still shown in the delegate header for both modes. Use sync delegation when totals must roll into the session.

Background-work visibility

Async tickets keep running after the parent's turn settles, and pi renders an idle session — so delegate adds three signals:

  • Footer status — while any ticket is active, the footer shows ⏳ 2 subagents · t5042v19 · /subagents, updated live as subagents start and finish.
  • Settle warning — the first time a turn settles with a ticket still active, a warning notification names the ticket and reminds you that quitting aborts it. Once per ticket; the footer carries it from there.
  • Switch/fork guard/new, /resume, and forking ask for confirmation before killing live subagents (pi lets extensions cancel those paths).

Quitting (Ctrl+C×2 / Ctrl+D / /quit) and /reload cannot be intercepted by an extension — pi's session_shutdown is advisory. The footer status is the mitigation there; on quit, delegate also prints a trace line to the terminal naming the aborted tickets and agents, and on /reload it shows a warning notification.

Live subagent browser

Press Ctrl+Shift+B from the editor, or run /subagents, to open a live browser without sending a model request. It shows both sync tasks and async tickets, including retained completed work. Your draft is left untouched.

Key Action
↑ / ↓ Select an agent
Tab / ← / → Switch between tool activity and assistant responses
PgUp / PgDn Scroll the selected view
Home / End Jump to oldest retained text / follow live output
p Pause or resume the selected agent's whole async ticket
Esc Close and return to your draft

The view shows running commands, tool results, edited file paths, queue dependencies, errors, token counts and activity age. Pause controls use the same cooperative boundary described below; they do not freeze subprocesses. Down in the editor keeps its normal behavior rather than intercepting menus or replacing a custom editor.

This is a bounded live preview, not a transcript archive: assistant text keeps the current attempt's last 32K characters (no thinking blocks); tool activity shows up to 100 recent calls with output tails. The browser retains the last 20 completed sync calls. Async results remain available until the existing ticket cleanup removes them (eligible after 30 minutes). Nothing is recovered from transcripts after a restart. A refresh timer runs only while the browser is open and is cleared on close or shutdown. This feature requires Pi's TUI, not print/JSON/RPC mode.

Pause and resume

Pause an async ticket with delegate({ ticketAction: "pause", ticket: "<id>" }); continue it with ticketAction: "resume". Both return immediately.

Each current model response and its tool calls finish, then the agent waits before its next model request. Queued tasks do not start. Pausing means work is reaching that boundary; paused means it has reached it. A task that finishes naturally may complete instead. Resume keeps the same live conversations, including scratch workspaces.

Pause is not cancellation, rollback, or a process freeze. Background commands can keep running, and files may be unfinished. In-progress isolated preparation or application finishes before pausing. Sessions, concurrency slots and workspace reservations remain held. Inactivity checks stop while parked, but explicit wall-clock deadlines keep counting. Wait does not resume work; cancel still works. Pauses do not survive Pi exit or reload.

Stall detection and cancellation

stallTimeoutMs is an inactivity watchdog, not a hard execution deadline. When an active subagent emits no model or tool activity for the configured interval, delegate reports that a stall was detected and asks Pi's AgentSession.abort() to cancel it, along with any active compaction or branch summary. Cancellation is cooperative: delegate waits for the session to become idle and non-compacting before returning a failed task, because returning while a provider, tool, or extension can still run would let a supposedly finished agent keep mutating state. An operation that ignores cancellation can therefore delay the final task result. Set stallTimeoutMs to 0 to disable the watchdog.

Develop

bun install
bun run typecheck
bun test
bun run build       # optional disposable bundle smoke test

The package entry point is delegate.ts; extension.ts holds the tool implementation. .build/delegate.bundle.ts is generated by bun run build and is only a verification artifact. Never symlink it into a running Pi or build over an installed extension.

Glossary

  • Delegate task — One item in delegate({ tasks: [...] }). This is the core unit of work: a prompt plus optional overrides such as agent, tools, systemPrompt, thinking, cwd, context, workspace, sessionId, or resumeFrom. model is also accepted but should be rare — subagents inherit the parent model by default.
  • Default subagent — The reserved built-in agent: "default" profile. It mirrors the live parent's model, thinking level, delegatable native tools, and base system prompt while preserving delegate's extension/context isolation; a default.md Markdown file can override its prompt/tools/model/thinking (first definition wins — a prompt-only file keeps the parent-mirrored tools and thinking/model inheritance).
  • Custom agent — A subagent profile defined by the parent, either inline in a delegate task (systemPrompt, tools, and thinking) or persisted as a Markdown file. The subagent inherits the parent model by default; model is a rare override. Markdown agents are examples of custom agents.
  • Named agent / Markdown agent — A reusable custom agent persisted as a Markdown file in .pi/agents/*.md, ~/.pi/agent/agents/*.md, ~/.agents/*.md, .claude/agents/*.md, or ~/.claude/agents/*.md (first definition wins). The frontmatter defines its name, description, model, tools, and thinking level; the Markdown body is its system prompt. A same-named file for a built-in (default/scout/coder/reviewer) overrides that built-in; a prompt-only override keeps the built-in's tools and workspace, and an explicit model/thinking replaces parent inheritance. For default, the delegate.json override maps are ignored (task fields still win); for the other built-ins, those maps win field-by-field over frontmatter.
  • Inline subagent — A subagent created from inline task fields instead of a named Markdown agent profile. In current output this is labeled inline.
  • Inline task — The task object itself when its configuration is supplied directly in the delegate call. Prefer this term over “inline agent” when talking about the API shape.
  • Subagent run — The actual execution of a resolved delegate task. Multiple runs using the same named agent are independent unless they share a sessionId or resume from the same session file.
  • Pooled subagent / persistent session — A live subagent kept in memory under a sessionId. The first call creates it; later calls with the same sessionId continue the same conversation until explicitly closed or the parent Pi session ends.
  • Resumed subagent — A subagent rehydrated from a previous session .jsonl via resumeFrom. It can also be pooled by providing a sessionId.
  • Async ticket — A background execution handle returned when top-level async: true is used. Poll, wait, pause, resume, or cancel tickets with top-level ticketAction: "poll", ticketAction: "wait" (blocks until the ticket settles; optional timeoutMs), or ticketAction: "cancel".
  • Skill — A SKILL.md instruction bundle injected into the subagent system prompt. Skills are text instructions only; they do not unlock additional tools.
  • AGENTS.md context — Project and ancestor guidance files are automatically appended to subagent system prompts. User-global AGENTS.md files are excluded; they describe the parent harness, not the delegated task.