Compact, pull-based progress snapshots for background Pi coding agent workers.
Claude, Codex, and other orchestrators can run Pi in the background:
pi -p "Fix the authentication tests"While that process is running, the orchestrator normally has two poor choices: wait without visibility, or stream Pi's entire output/event feed into its context. Streaming tool calls, reasoning, logs, and JSON events is expensive and makes coordination harder. Concurrent Pi workers in one repository also cannot safely share one status file.
There is a second identity problem: if the worker invents its own ID after launch, the orchestrator cannot reliably know which new progress file belongs to its process. Matching by timestamp, task text, or PID is ambiguous.
pi-worker-progress is a global Pi extension that activates only when ctx.mode === "print". Each print worker maintains one compact current-state snapshot:
~/.pi-worker/sessions/<worker-id>/progress.json
The orchestrator creates the identity before launch and passes it through Pi's native --session-id option:
pi --session-id "<fresh-uuid>" -p "Fix the authentication tests"Pi exposes that same ID to the extension through ctx.sessionManager.getSessionId(). The orchestrator therefore knows the exact progress path without an stdout handshake or discovery race.
The snapshot is overwritten atomically. It is not a transcript and never contains chain-of-thought, tool results, command output, or fake percentages.
- Node.js 22.19 or newer
@earendil-works/pi-coding-agent0.82.1 or a compatible later release
The extension was developed and tested against Pi 0.82.1.
After the first npm release:
pi install npm:pi-worker-progressRestart Pi after installation. Pi installs the package globally by default, but the extension remains inert in interactive, RPC, and JSON modes.
To test a checkout without installing it:
pi -e . --session-id local-progress-test -p "Inspect this repository and summarize it"Generate a new UUID for every worker, retain it in the orchestrator, and launch Pi using the background-process facility provided by Claude or Codex:
workerId = generate UUID
start in background: pi --session-id <workerId> -p <task>
read when needed: ~/.pi-worker/sessions/<workerId>/progress.json
wait for Pi and verify its final result
PowerShell can generate the ID with:
$workerId = [guid]::NewGuid().ToString()A POSIX shell can use:
worker_id="$(uuidgen)"Then pass that value to the orchestrator's background shell invocation:
pi --session-id "$worker_id" -p "$task"Do not reuse worker IDs. In particular, do not combine a monitored worker ID with --continue, --session, or a previously used --session-id.
Ordinary usage remains valid:
pi -p "Fix the authentication tests"Pi generates a session ID automatically, so progress is still recorded. Supplying the ID is what gives an orchestrator reliable ownership of a particular snapshot.
PowerShell:
Get-Content "$HOME\.pi-worker\sessions\$workerId\progress.json" | ConvertFrom-JsonPOSIX shell with jq:
jq . "$HOME/.pi-worker/sessions/$worker_id/progress.json"All workers across repositories can be queried consistently with DuckDB after substituting the absolute user-home path:
SELECT
workerId,
status,
task,
current,
updatedAt
FROM read_json_auto('<absolute-user-home>/.pi-worker/sessions/*/progress.json')
ORDER BY updatedAt DESC;DuckDB is not a runtime dependency.
A snapshot looks like this:
{
"schemaVersion": 1,
"workerId": "019c0f72-44c5-7a31-a5de-9c55e2941946",
"status": "working",
"task": "Fix the authentication tests",
"taskTruncated": false,
"cwd": "C:\\src\\my-project",
"processId": 31452,
"startedAt": "2026-07-28T10:30:00.000Z",
"updatedAt": "2026-07-28T10:34:12.000Z",
"current": "Running authentication integration tests",
"completed": ["Identified the failing authentication setup"],
"pending": ["Fix the remaining failure", "Run the full test suite"],
"blocked": null
}Statuses have precise ownership:
starting: created by the extension before the agent starts.working: reported semantically by the worker.blocked: reported with a concrete blocker by the worker.done: written by the extension only after Pi emitsagent_settledsuccessfully.failed: written by the extension after a settled error/abort or shutdown before settlement.
The model-facing update_progress tool can set only working or blocked. Terminal status cannot be self-declared by the model.
The extension uses Pi's public extension API:
session_start: checks for print mode, obtains the native session ID, and dynamically registersupdate_progressonly for that print session.before_agent_start: creates the initial snapshot from the expanded user prompt and appends a short progress instruction to that turn's system prompt.agent_start: marks execution as working.agent_end: captures the final assistant stop reason without prematurely finalizing retries or compaction.agent_settled: writesdoneorfailedafter Pi has no retry, compaction, or continuation remaining.session_shutdown: records failure when a started worker shuts down before settling.
Writes are serialized per worker. Each update is written to a unique temporary file in the worker directory and renamed over progress.json, so concurrent readers never observe partially written JSON. Reusing a worker directory is rejected rather than overwriting an existing worker.
The structured progress tool bounds string lengths, list lengths, and total snapshot growth. The task preview is capped at 2,000 characters and records taskTruncated: true when shortened.
A normal interactive session:
pireceives no progress tool, no progress system instruction, and no session-store writes from this extension. The extension also stays inactive in Pi's RPC and JSON modes.
All runtime state is isolated outside repositories:
Windows: %USERPROFILE%\.pi-worker\sessions\<worker-id>\progress.json
Unix: $HOME/.pi-worker/sessions/<worker-id>/progress.json
No repository .gitignore changes are required. Every snapshot retains its originating cwd, so workers from different repositories can be queried from the central store.
PI_WORKER_SESSIONS_DIR may point to a different absolute directory for isolated testing or managed environments. The default always uses the current user's home directory.
- Semantic intermediate updates depend on the model calling
update_progress; lifecycle-created initial and terminal states do not. - A forceful process kill can bypass Pi's
session_shutdownevent. The last valid snapshot is preserved and is not automatically classified as stale or failed. - The extension deliberately refuses a previously used worker directory. Generate a fresh
--session-idfor every background worker. - The initial release stores current state only. It does not implement event history, cleanup, polling, a daemon, or bidirectional control.
processIdis diagnostic metadata, not worker identity.
Pi extensions execute with the user's permissions. Review extension source before installation. By default this package writes only beneath the current user's ~/.pi-worker/sessions/<worker-id> directory.
The injected instruction expressly prohibits reasoning, logs, command output, and tool results. Snapshot fields are bounded, but task text and model-written milestone summaries may still contain repository information. The central store can contain summaries from multiple repositories; protect and retain it accordingly.
npm ci
npm run check
npm pack --dry-runRun a live local test:
pi -e . --session-id progress-smoke-test -p "Inspect this repository and report its purpose"Use a fresh ID for each repetition. See CONTRIBUTING.md for contribution expectations.
.github/workflows/publish.yml publishes tags matching v* using npm trusted publishing and GitHub Actions OIDC. It does not require a long-lived NPM_TOKEN.
npm requires a package to exist before a trusted publisher can be configured. Publishing also requires account-level 2FA or a granular token that bypasses 2FA. For this one-time interactive bootstrap, enable 2FA on the npm account first, then authenticate the CLI through the browser:
npm logout
npm login --auth-type=web
npm whoami
npm run check
npm publish --access publicDo not continue until npm whoami prints the expected npm username. Complete the browser, security-key, or authenticator challenge when npm requests it during publication. Do not add an npm publishing token to the repository.
After 0.1.0 exists, open its settings on npmjs.com and configure this trusted publisher:
- Provider: GitHub Actions
- Organization or user:
chkrishna2001 - Repository:
pi-worker - Workflow filename:
publish.yml - Allowed action:
npm publish - Environment: leave empty unless the workflow is later assigned one
The failed v0.1.0 workflow does not need to be rerun after the manual publication because that exact version will already exist. For the next release, update package.json and CHANGELOG.md, commit them, and push the matching new tag, for example v0.1.1.
For every later version, .github/workflows/publish.yml verifies that:
- The package already exists, so trusted publishing can be configured.
- The Git tag exactly matches
package.json. - Type checking and tests pass.
- The npm tarball can be created.
It then publishes through GitHub OIDC with automatic npm provenance. The workflow intentionally has no token fallback.
See npm's trusted publishing documentation and GitHub's Node package publishing guide.
MIT