pi-fork-join
In-process shared-prefix concurrent fork/join for the Pi coding agent. Read-only forks by default; write forks are isolated in their own git worktrees. The parent keeps its context window; children return dense structured reports.
Package details
Install pi-fork-join from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-fork-join- Package
pi-fork-join- Version
0.1.0- Published
- Aug 30, 2026
- Downloads
- 176/mo · 7/wk
- Author
- guan810
- License
- MIT
- Types
- extension
- Size
- 37.6 KB
- Dependencies
- 0 dependencies · 5 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-fork-join
In-process shared-prefix concurrent fork/join for the Pi coding agent.
Keep your main agent's context window clean while running several independent
investigations in parallel. Each fork is a child AgentSession in the same
process that inherits the current active branch as its shared prefix, runs a
bounded task, and returns only a dense structured report. The parent's context
window never receives the forks' intermediate tool logs or thinking.
pi-fork-join is the in-process realization of the "shared-prefix concurrent
fork" idea: the execution engine is brand new (no child pi process, no JSONL
snapshot round-trip), while the configuration surface is adapted from the
pi-fork extension (MIT).
Install
pi install git:github.com/InertialG/pi-fork-join
Or run from source:
pi -e ./src/index.ts
Usage
The extension registers one tool, fork_join:
{
"tasks": [
{ "id": "data", "task": "Inspect the data layer; return evidence and risk." },
{ "id": "concurrency", "task": "Audit the concurrency/state model; return risks." },
{ "id": "impl", "task": "Implement step 3 and land the patch.", "write": true }
],
"maxConcurrency": 4
}
Each fork inherits the parent conversation up to, but excluding, the assistant
message that contains the current fork_join call — the call itself, its
sibling task descriptions, and any parallel-status text are not visible inside a
branch. fork_join blocks until all forks finish and returns their reports in
input order. The parent is responsible for synthesis and any final serial edits.
Context semantics
fork_join locates the current call in the active branch and uses the entry
before it as the shared fork base:
C0: existing conversation and tool results
C1: current assistant message
├─ optional text
└─ fork_join toolCall
child_i = C0 + private_task_i
This means:
- Every child sees the same history that existed before the current call.
- No child sees the
fork_joincall, sibling tasks, or parallel-status text. - Prior tool results and earlier fork reports that already entered the parent history are treated exactly like any serial result — nothing is deleted or semantically pruned.
- Locating the call is a hard requirement: if it cannot be found, or is
ambiguous, the whole
fork_joinfails rather than silently falling back to the current leaf (which would leak the running call into children).
A single task failing does not discard the batch; the parent still receives the status of every task.
Isolation model
Isolation is enforced by tool policy, not by filesystem copying — and it is
best-effort, not a sandbox. A read-only fork excludes the edit/write
tools, which is what prevents the classic parallel-edit clobber race. But both
read-only and write forks still have bash, which can create, modify, and
delete files. Treat read-only as "no edit/write tools" — not as "cannot touch
the filesystem". The parent agent retains final authority and should not
delegate destructive or security-sensitive work to any fork without awareness.
| Fork kind | Tools | Working directory | When |
|---|---|---|---|
| Read-only (default) | read, bash, grep, find, ls — no edit/write |
shared parent cwd |
investigation, review, evidence collection |
Write (write: true) |
above plus edit, write |
its own git worktree | a fork that must call edit/write |
Design rationale:
- Read-only is the default and needs no worktree. Because no fork has
edit/write, concurrent forks cannot clobber each other through those tools regardless of scheduling order. Read-only forks also see the parent's current dirty working tree (uncommitted changes), which is exactly what an investigating agent needs. They still havebash, so they can write files if they try — that is an accepted limitation, surfaced honestly here and in the tool description, not silently hidden. - Worktree only for write forks. A write fork is isolated in its own git
worktree so its
edit/writecannot race the parent or other forks. A worktree forks from the last commit: the parent's uncommitted changes are deliberately not visible inside it. Worktree isolation is also best-effort against a cooperative model; a fork that deliberately writes to an absolute parent path can escape it. Write forks require a git repository; if none exists, that fork fails with a clear error. - Non-git directories are fine for read-only forks (they use the shared
cwd). Onlywrite: trueneeds git.
Concurrency
- Forks run on the single event loop of the parent process. LLM calls, tool subprocesses, and file I/O are all async I/O, so several forks genuinely overlap (wall-clock ≈ slowest fork, not the sum). There is no CPU parallelism; that is a deliberate and correct trade for this I/O-bound workload.
- Concurrency is bounded by
maxConcurrency(default4, max8) via a worker pool. This keeps provider calls and worktrees within a sane budget. - Cancellation: when the parent tool call is aborted, every running fork's
session is aborted. Fork results report
cancelled. - Failure isolation: each fork runs in its own
AgentSession; one fork failing does not cancel the others. Failures are collected per-fork.
Context isolation
- Only the final assistant report of each fork is returned to the parent.
- Fork transcripts, tool calls, and thinking never enter the parent context.
- The shared prefix is rebuilt from the current active branch only
(
sessionManager.getBranch()); sibling/abandoned branches are excluded. - Messages are deep-cloned before seeding, so a fork can never mutate the parent's history.
Settings
Configure under the pi-fork-join key in ~/.pi/agent/settings.json
(global) or .pi/settings.json (project). Project overrides global.
{
"pi-fork-join": {
"defaultMaxConcurrency": 4,
"costFooter": true,
"leanChildren": true
}
}
defaultMaxConcurrency— used when afork_joincall omitsmaxConcurrency.costFooter— show fork cost as a dim footer status line.leanChildren— children load only the base system prompt + projectAGENTS.md, skipping extensions/skills/prompts/themes. Defaulttrue(prevents child extension bloat and accidental recursivefork_join).
Development
npm install # peer deps for typechecking
npm test # node --test unit tests (pure modules + real git worktrees)
npm run typecheck # tsc --noEmit against the installed pi packages
The unit tests exercise the pure modules (scheduler, tool policy, report
assembly/extraction, config, and git worktree create/remove round-trips).
src/fork-runner.ts and src/index.ts are type-checked against the installed
pi SDK; integration against a live model/provider requires running inside pi
(pi -e ./src/index.ts and calling fork_join).
Important caveats
- Single-threaded: forked CPU-bound work still serializes on the event loop. This extension is tuned for I/O-bound agent investigation.
- Read-only is not a sandbox: a read-only fork still has
bash, which can create/delete files. It only lacksedit/writetools. The parent agent must not rely on a read-only fork being unable to touch the filesystem. - Context pollution on repeated calls: each fork inherits the shared prefix.
If you call
fork_joinrepeatedly in one session, later forks inherit the earlier fork reports and may drift into meta-commentary. Prefer a singlefork_joincall for all parallel tasks, and run clean experiments in a fresh session. - Worktree sees only committed state: a
write: truefork cannot see your uncommitted parent changes. Use read-only forks (sharedcwd) when a fork must inspect the dirty working tree. - Report-only contract: only the final report returns to the parent; fork tool logs and thinking never enter the parent context.
License
MIT. The configuration surface and early design were adapted from
pi-fork (MIT); the in-process
execution engine and shared-prefix context implementation are original to this
package.