pi-background-tasks 0.7.7 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/BACKGROUND-TASKS-INSTRUCTIONS.md +63 -0
  2. package/PUBLISHING.md +43 -29
  3. package/README.md +234 -385
  4. package/TESTING.md +15 -9
  5. package/TEST_PLAN.md +46 -13
  6. package/docs/INDEX.md +157 -0
  7. package/docs/api/eventbus-v1.md +166 -0
  8. package/docs/assets/architecture.svg +78 -0
  9. package/docs/assets/footer-dock.svg +47 -0
  10. package/docs/assets/logo.svg +49 -0
  11. package/docs/attestations.json +189 -0
  12. package/docs/choose-a-workflow.md +98 -0
  13. package/docs/commands/bg-clear.md +70 -0
  14. package/docs/commands/bg-update.md +82 -0
  15. package/docs/commands/bg.md +90 -0
  16. package/docs/commands/fusion-models.md +70 -0
  17. package/docs/commands/fusion.md +69 -0
  18. package/docs/commands/jobs.md +74 -0
  19. package/docs/commands/kill.md +82 -0
  20. package/docs/commands/logs.md +90 -0
  21. package/docs/commands/task-manager.md +109 -0
  22. package/docs/concepts/completion-delivery.md +66 -0
  23. package/docs/concepts/context-projection-and-budgeting.md +79 -0
  24. package/docs/getting-started.md +122 -0
  25. package/docs/manifest.json +1825 -0
  26. package/docs/operations/configuration.md +110 -0
  27. package/docs/operations/releasing.md +67 -0
  28. package/docs/operations/testing.md +101 -0
  29. package/docs/operations/troubleshooting.md +38 -0
  30. package/docs/read-before-edit.md +94 -0
  31. package/docs/reference/runtime-contracts.md +213 -0
  32. package/docs/reference/shortcuts-and-dock.md +70 -0
  33. package/docs/subsystems/attested-pi-runs.md +141 -0
  34. package/docs/subsystems/background-task-runtime.md +85 -0
  35. package/docs/subsystems/child-launch-durability-and-safety.md +57 -0
  36. package/docs/subsystems/delegation.md +190 -0
  37. package/docs/subsystems/docs-freshness-gate.md +26 -0
  38. package/docs/subsystems/fusion.md +121 -0
  39. package/docs/subsystems/host-ui-and-telemetry.md +83 -0
  40. package/docs/tools/bg_delegate.md +193 -0
  41. package/docs/tools/bg_kill.md +114 -0
  42. package/docs/tools/bg_logs.md +133 -0
  43. package/docs/tools/bg_result.md +120 -0
  44. package/docs/tools/bg_run.md +168 -0
  45. package/docs/tools/bg_run_pi_attested.md +170 -0
  46. package/docs/tools/bg_status.md +111 -0
  47. package/docs/tools/fusion_investigate.md +116 -0
  48. package/docs/tools/fusion_reason.md +75 -0
  49. package/docs/tools/fusion_research.md +162 -0
  50. package/docs/tools/fusion_validate.md +206 -0
  51. package/logo.png +0 -0
  52. package/package.json +29 -6
  53. package/src/core/delegate/budget.ts +1 -1
  54. package/src/core/delegate/launch.ts +6 -0
  55. package/src/core/fusion/artifacts.ts +80 -5
  56. package/src/core/fusion/budget.ts +129 -28
  57. package/src/core/fusion/child-protocol.ts +82 -0
  58. package/src/core/fusion/clean-context.ts +91 -0
  59. package/src/core/fusion/config.ts +124 -35
  60. package/src/core/fusion/context.ts +33 -6
  61. package/src/core/fusion/evaluation.ts +392 -15
  62. package/src/core/fusion/orchestrator.ts +274 -25
  63. package/src/core/fusion/pi-child.ts +635 -10
  64. package/src/core/fusion/prompts.ts +167 -6
  65. package/src/core/fusion/source-policy.ts +257 -0
  66. package/src/core/fusion/types.ts +232 -5
  67. package/src/core/fusion/web-fetch.ts +993 -0
  68. package/src/core/fusion/workflows.ts +184 -0
  69. package/src/extension.ts +3 -3
  70. package/src/fusion-child-extension.ts +370 -54
  71. package/src/fusion-extension.ts +625 -125
  72. package/src/testing/normalize.ts +0 -22
package/README.md CHANGED
@@ -1,476 +1,325 @@
1
- # pi-background-tasks
1
+ <div align="center">
2
+ <img src="logo.png" alt="pi-background-tasks logo: a futuristic dispatcher coordinating parallel work nodes into a completed result" width="144" height="144">
2
3
 
3
- Claude-Code-like explicit background shell task manager for [Pi](https://pi.dev/).
4
+ # pi-background-tasks
4
5
 
5
- This package adds named, tracked background shell jobs with durable output files, bounded log reads, kill/timeout safety, task-owned context-window/token/tool-use/model telemetry, explicit Pi-agent telemetry wrapping for tasks marked as agents, a focused footer-dock task manager, `/tasks` fallback UI, and completion notifications that can wake the agent when LLM-launched work finishes. It also ships Fusion: a direct child-Pi five-call synthesis workflow exposed as `/fusion`, `/fusion-models`, and the always-active `fusion_brainstorm` tool. A terminal task status is published only after trailing wrapped-agent telemetry is consumed and final output plus terminal metadata have completed their durability writes.
6
+ **Keep Pi moving while long jobs, delegated investigations, and fixed-purpose multi-model Fusion work run in the background.**
6
7
 
7
- ## Install
8
+ [![npm](https://img.shields.io/npm/v/pi-background-tasks?label=npm)](https://www.npmjs.com/package/pi-background-tasks)
9
+ [![Pi extension](https://img.shields.io/badge/Pi-extension-19c7d4)](https://github.com/earendil-works/pi-coding-agent)
10
+ [![Node](https://img.shields.io/badge/node-%3E%3D22.19-1f8f4d)](package.json)
11
+ [![License](https://img.shields.io/badge/license-ISC-f5a623)](LICENSE)
8
12
 
9
- From npm after publish:
13
+ </div>
10
14
 
11
- ```bash
12
- pi install npm:pi-background-tasks@0.7.7
13
- ```
15
+ `pi-background-tasks` adds background jobs, delegated agents, and multi-model Fusion for Pi:
14
16
 
15
- From git after pushing this package to its standalone repository and tagging:
17
+ - **Run long work without blocking**: start named shell jobs, keep talking to Pi, and get durable completion notifications when they finish.
18
+ - **Delegate context-aware investigation**: launch one route-pinned, inspect-only child Pi agent seeded with a frozen projection of the current conversation, then retrieve a hash-verified result.
19
+ - **Combine model perspectives through fixed-purpose Fusion**: run three candidate children, blind evaluation, optional bounded evaluator repair, and merger for reasoning, investigation, targeted URL research, or validation review.
16
20
 
17
- ```bash
18
- pi install git:github.com/ismailsaleekh/pi-background-tasks@v0.7.7
19
- ```
21
+ <p align="center">
22
+ <img src="docs/assets/architecture.svg" alt="Architecture diagram showing Pi session, background task registry, delegated child agent, and Fusion candidate/evaluator/merger flow" width="760">
23
+ </p>
20
24
 
21
- For project-local install:
25
+ <!-- pi-docs:begin name="readme-package-facts" generator="scripts/docs/generate.mjs" -->
26
+ | Fact | Value |
27
+ | --- | --- |
28
+ | Package | `pi-background-tasks` |
29
+ | Version | `1.0.3` |
30
+ | Node engine | `>=22.19.0` |
31
+ | Pi entrypoint | `./extensions/background-tasks.ts` |
32
+ | Package image | [logo.png](https://raw.githubusercontent.com/ismailsaleekh/pi-background-tasks/main/logo.png) |
33
+ <!-- pi-docs:end name="readme-package-facts" -->
22
34
 
23
- ```bash
24
- pi install -l npm:pi-background-tasks@0.7.7
25
- ```
35
+ <!-- pi-docs:begin name="readme-public-surfaces" generator="scripts/docs/generate.mjs" -->
36
+ | Surface kind | Count |
37
+ | --- | --- |
38
+ | command | 10 |
39
+ | tool | 11 |
40
+ | shortcut | 2 |
41
+ | renderer | 2 |
42
+ | eventbus | 1 |
43
+ | workflow | 4 |
26
44
 
27
- ## Commands
45
+ Public commands: `/bg`, `/bg-clear`, `/bg-tasks`, `/bg-update`, `/fusion`, `/fusion-models`, `/jobs`, `/kill`, `/logs`, `/tasks`.
28
46
 
29
- - `/bg [--agent] [--name "Task name"] <command>` start a named tracked background shell command. Use `--agent` only when the command launches an LLM/agent.
30
- - `/jobs` — list running and recent completed/failed/killed tasks.
31
- - `/logs <id> [maxBytes]` — show bounded tail output and full output path.
32
- - `/kill <id>` — stop a running task.
33
- - `/tasks` or `/bg-tasks` — fallback command to open the task manager UI.
34
- - `/bg-clear` — clear finished background-task footer notices.
35
- - `/bg-update` — print update instructions when a newer published version exists (instruct-only; never self-installs).
36
- - `/fusion <prompt>` — run three candidate child Pi final-text calls, one blind evaluator, and one merger, then append the merged answer directly as a visible `fusion-result` custom message without asking the parent model to rewrite it. Running `/fusion` without arguments opens a multiline editor in UI-capable modes; cancelling the editor does not spawn children.
37
- - `/fusion-models` — TUI-only five-slot global model selector (`Candidate 1`, `Candidate 2`, `Candidate 3`, `Evaluator`, `Merger`). It supports duplicate selections, `$current` defaults, slash-containing model ids, atomic saves to `fusion-models.json`, and rejects non-TUI modes immediately.
47
+ Public tools: `bg_delegate`, `bg_kill`, `bg_logs`, `bg_result`, `bg_run`, `bg_run_pi_attested`, `bg_status`, `fusion_investigate`, `fusion_reason`, `fusion_research`, `fusion_validate`.
38
48
 
39
- ## Footer dock UX
49
+ Full owner map and generated contracts live in [docs/INDEX.md](docs/INDEX.md).
50
+ <!-- pi-docs:end name="readme-public-surfaces" -->
40
51
 
41
- When tasks are active or unseen completions/failures exist, Pi shows a compact footer status:
42
52
 
43
- ```text
44
- bg 2 running · Shift↓
45
- bg 1 running · 1 failed · Shift↓ · /bg-clear
46
- bg 2 done · Shift↓ · /bg-clear
47
- ```
48
53
 
49
- Press `Shift+Down` to open the focused bottom dock. Arrow keys are captured only while the dock is focused. Each task row shows the latest context-window usage reported by that specific background task, for example `ctx 21.0%/200k`; tasks that do not report their own context show `ctx —` rather than the parent Pi session's usage. Background Pi-agent tasks also surface the LLM model they ran (`model gpt-5.5` in the compact row, fully-qualified such as `openai-codex/gpt-5.5` in the detail view), cumulative token usage (`tok 1.6k`), and tool-use counts (`tools 2/1 failed`) in the dock and detail view; missing model/token/tool telemetry is omitted in rows and shown as “not reported by this background task” in details. When a background command is explicitly marked as an agent and invokes a print/json child agent such as `pi -p ...` through the normal shell command name, the extension wraps that child Pi process with `--mode json`, parses real assistant usage/tool execution events, and emits task-owned telemetry automatically — including the model reported by the child assistant turns.
50
54
 
51
- ### Agent activity transcript
55
+ ## Why use it?
52
56
 
53
- For those wrapped background Pi agents, the task output file and the dock's detail **Output tail** show a live, human-readable transcript of what the agent is actually doing — assistant messages, `→ tool args` calls, `… reasoning`, and `✗ tool failed` errors — as the agent loop runs. The machine telemetry (context/token/tool/model) is parsed out of the child stream and surfaced only as the metrics above and in `bg_status`/metadata, so the focused window reflects the agent's real activity rather than its raw instrumentation JSON. Child stderr is passed through to the transcript verbatim. Non-agent tasks and agent commands that cannot be wrapped (for example a path-qualified `pi`) keep streaming their raw stdout/stderr unchanged.
54
-
55
- Finished-task badges intentionally remain visible until acknowledged. The reliable clear path is `/bg-clear`, which works in every terminal and clears finished background-task footer notices without opening the dock. `Ctrl+Alt+C` is still registered as an optional terminal-dependent fallback shortcut, but the footer advertises `/bg-clear` because some macOS terminals do not transmit `Ctrl+Alt+C` distinctly.
56
-
57
- Dock controls:
58
-
59
- | Key | Action |
57
+ | You want to... | Use this package because... |
60
58
  |---|---|
61
- | `Shift+Down` | Open focused background-task dock |
62
- | `/bg-clear` | Clear finished-task footer notices from the main UI |
63
- | `Ctrl+Alt+C` | Optional terminal-dependent shortcut for `/bg-clear` |
64
- | `↑` / `↓` | Select task (list) · scroll the output tail (detail) |
65
- | `PageUp` / `PageDown` | Page the task list (list) · page the output tail (detail) |
66
- | `Enter` / `→` | Inspect logs/details |
67
- | `←` | Return from details to list |
68
- | `h` | Toggle recent history |
69
- | `k` | Stop selected running task |
70
- | `a` / `K` | Stop all running tasks, with confirmation |
71
- | `r` | Refresh detail tail |
72
- | `R` | Rerun selected command |
73
- | `c` | Show copyable output path |
74
- | `x` / `Esc` / `q` | Close dock |
75
- | `/bg-update` | Print update instructions when a newer version is published |
76
-
77
- In the detail view the **Output tail** is scrollable: `↑`/`↓` and `PageUp`/`PageDown` move through the loaded output (a generous bounded window, not just the last lines). Scrolling up pauses the live tail and freezes the view so it stays stable as new output arrives; the status line shows your position (e.g. `lines 46–57 of 60`). Scrolling back to the bottom (or pressing `r`) resumes live tailing.
78
-
79
- ### Update-available notice
59
+ | Start a dev server, watch build, migration dry run, or long check | `bg_run` and `/bg` return immediately, write durable output files, show a footer dock, and notify on terminal state. |
60
+ | Let Pi keep working instead of sleeping or polling | Default `bg_run` completion delivery sends a durable terminal notification and can wake a follow-up turn. |
61
+ | Ask a second agent to inspect the repo with the current conversation as context | `bg_delegate` starts one isolated child with read/search/list tools only; `bg_result` verifies the committed result before returning it. |
62
+ | Compare model perspectives without exposing arbitrary parent context | Fusion children receive only the workflow input and fixed tool policy; no silent route substitution or fallback is used on delegate/Fusion paths. |
63
+ | Produce local evidence for a direct Pi run | `bg_run_pi_attested` records local same-user-writable artifacts and hashes after a successful structured child Pi task. |
80
64
 
81
- When a newer version of `pi-background-tasks` has been published to npm, the footer appends a compact, instruct-only segment after the entry hint:
65
+ ## Install
82
66
 
83
- ```text
84
- bg 1 running · 1 failed · Shift↓ · /bg-clear · ⬆ v0.7.0 /bg-update
85
- ```
67
+ Version information comes from [`package.json`](package.json). Use npm `@latest` for normal installs; use git `main` only when you intentionally want the current repository state.
86
68
 
87
- When no tasks are active, the notice still appears on its own (`bg ⬆ v0.7.0 /bg-update`) so the update hint is visible. The segment is rendered only when a strictly newer published version exists; `/bg-update` prints the npm and git update commands and never installs or self-updates.
69
+ ```bash
70
+ # Global install from npm
71
+ pi install npm:pi-background-tasks@latest
88
72
 
89
- The lookup runs at most once per session on `session_start`, is time-boxed, and is fully offline-safe: it never blocks or errors the footer/session, and on an offline or failed lookup it simply renders no segment (it never pins a misleading version). The check is skipped entirely when `PI_OFFLINE=1`, and can be disabled explicitly with `PI_BG_DISABLE_UPDATE_CHECK=1`. Set `PI_BG_REGISTRY_URL` to point the check at a registry mirror instead of `https://registry.npmjs.org`.
73
+ # Project-local install from npm
74
+ pi install npm:pi-background-tasks@latest -l
90
75
 
91
- ## LLM tools
76
+ # Git main branch; not a release tag
77
+ pi install git:github.com/ismailsaleekh/pi-background-tasks@main
92
78
 
93
- - `bg_run` — start named long-running commands without blocking the conversation.
94
- - `bg_delegate` — launch one background Pi agent seeded with a frozen projection of the current conversation, then return a launch receipt immediately. See [Delegated background agents](#delegated-background-agents).
95
- - `bg_result` — retrieve a `bg_delegate` answer, hash-verified before it is returned.
96
- - `bg_run_pi_attested` — opt-in structured direct-spawn Pi agent task that emits a strict local attestation sidecar after successful completion.
97
- - `bg_status` — inspect one task or all recent tasks.
98
- - `bg_logs` — read bounded task output.
99
- - `bg_kill` — stop a running task.
100
- - `fusion_brainstorm({prompt})` — always-active tool that runs the Fusion workflow and returns the exact merged text as the tool result for the parent agent to consume, with the exact Pi `Usage` shape attached when the host supports tool-result usage: token fields plus complete `cost.input`, `cost.output`, `cost.cacheRead`, `cost.cacheWrite`, and `cost.total`. Its closed public schema has exactly one required parameter, `prompt`; extra keys are rejected. It has no eligibility, quota, routine, or justification gate. Tool context capture excludes the current assistant tool-call leaf when Pi is executing that `fusion_brainstorm` call, so the nested children do not see the in-progress tool call or sibling calls. Children receive the documented conversation projection described under [Conversation context policy](#conversation-context-policy): visible user/assistant text verbatim, with thinking and tool payloads replaced by explicit hash-accounted omission receipts. Because the prompt is composed by the parent agent, it is treated as authoritative and self-contained.
79
+ # Project-local git main install
80
+ pi install git:github.com/ismailsaleekh/pi-background-tasks@main -l
101
81
 
102
- `bg_run` requires a concise `name` for the footer dock, the shell `command`, and required `isAgent: boolean`. Set `isAgent: true` only when the background task launches an LLM/agent process (for example `pi -p ...` or `pi --mode json ...`); set `isAgent: false` for scripts, tests, dev servers, sleeps, and ordinary shell commands. It defaults both `notifyOnCompletion` and `triggerOnCompletion` to `true`. With those defaults, `bg_run` returns immediately, the agent continues only independent useful work or ends its current turn instead of sleeping or polling, and a durable `background-task-notification` for completed, failed, or killed state automatically starts a follow-up turn. The launch receipt states the effective notification/wake behavior explicitly. `bg_status` and `bg_logs` remain available for user-requested inspection, deliberately disabled completion delivery, concrete hang diagnosis, or reading output after the terminal event; they are not waiting primitives, and the terminal notification does not need status reconfirmation. Setting `triggerOnCompletion: false` keeps the notification but prevents it from starting an agent turn. Setting `notifyOnCompletion: false` suppresses both notification and wake-up even if `triggerOnCompletion` is true.
82
+ # Local checkout/package path, run from this package directory
83
+ pi install .
84
+ pi install . -l
85
+ ```
103
86
 
104
- Tasks marked with `isAgent: true` that launch print/json child Pi agents through the normal shell command name are telemetry-wrapped; set `PI_BG_DISABLE_PI_TELEMETRY=1` only when raw Pi stdout is required. The task snapshot and metadata expose `isAgent`, `contextUsage` (latest reported child assistant turn), cumulative `tokenUsage` (`input`, `output`, `cacheRead`, `cacheWrite`, `totalTokens`), cumulative `toolUsage` (`total`, `failed`, `byName`), and `model` (the LLM identifier reported by the child assistant turns, preferring the fully-qualified `provider/model` form) when reported by the child task. User-launched `/bg` jobs are display-only by default unless `--agent` is provided; UI reruns preserve the original task's `isAgent` value.
87
+ Local paths are loaded from disk without copying; use the path to this package from your current directory.
105
88
 
106
- `bg_run_pi_attested` is separate from `bg_run` and never accepts a shell command. It takes structured `provider`, `model`, `prompt`, optional literal extra Pi argv, and a relative `reportPath`; launches exactly one direct `pi --mode json` child; records raw Pi JSON events, separate stderr, exact argv/cwd, prompt/report hashes, observed Pi session/provider/model, and `ModelRegistry.isUsingOAuth` credential class. It forbids direct API-key/auth-file launch arguments and emits no partial attestation: failures remain ordinary failed tasks with no sidecar.
89
+ ## Quick start: useful in under five minutes
107
90
 
91
+ 1. Install and start Pi in a project.
92
+ 2. Launch a background command:
108
93
 
109
- ## Delegated background agents
94
+ ```text
95
+ /bg --name "Typecheck watch" npm run typecheck -- --watch
96
+ ```
110
97
 
111
- `bg_delegate` fills the gap between `bg_run` (a background agent with a **fresh,
112
- empty** context) and `fusion_brainstorm` (your current context, but synchronous
113
- and five-model). It is one agent, one prompt, seeded with the current session's
114
- context, non-blocking. `bg_result` retrieves its answer safely. They ship
115
- together: a delegate without a safe retrieval path could not return its work.
98
+ `/bg` starts a tracked shell task and returns the task id plus output path. User-launched `/bg` tasks notify in the UI but do not automatically wake a follow-up model turn.
116
99
 
117
- ```text
118
- bg_delegate({ name, prompt }) → launch receipt, immediately
119
- … the parent keeps working; the terminal notification wakes it …
120
- bg_result({ taskId }) → hash-verified answer
121
- ```
100
+ 3. Open the footer dock with **Shift↓** or list tasks:
122
101
 
123
- ### Context seeding
102
+ ```text
103
+ /jobs
104
+ ```
124
105
 
125
- The child does **not** share the parent's live session. The parent conversation
126
- is projected with the same frozen `visible-conversation-ledger-v2` transform
127
- Fusion uses (see [Conversation context policy](#conversation-context-policy)),
128
- frozen as an immutable seed, and the child is given its **own** `--session-id`
129
- and a task-owned `--session-dir`. Conceptually it has your context; physically it
130
- can never open or mutate the parent session.
106
+ 4. Read bounded output only when you need it:
131
107
 
132
- | Content | Disposition |
133
- |---|---|
134
- | User text | included verbatim, never clipped |
135
- | Assistant text | included verbatim, never clipped |
136
- | User image blocks | marker only, never raw bytes |
137
- | Assistant thinking | excluded; recorded as a hash-accounted omission receipt |
138
- | Tool-call arguments | excluded; recorded as a hash-accounted omission receipt |
139
- | Tool-result payloads | excluded; recorded as a hash-accounted omission receipt |
140
- | The in-flight `bg_delegate` call and its sibling calls | scope-excluded from the branch |
141
-
142
- The assistant message carrying the in-flight call is excluded as a whole, so when
143
- several `bg_delegate` calls share one assistant message **every** sibling call is
144
- excluded for **every** child: two delegates launched together receive identical
145
- projected history and neither can observe the other's arguments.
146
-
147
- The seed is canonical-JSON, SHA-256'd, and persisted. The persisted bytes are the
148
- exact bytes the child reads, and the child re-verifies that hash **before its
149
- first model call**. Repeated construction from the same session is
150
- byte-identical.
151
-
152
- **Documented limitation:** facts that exist only inside omitted parent tool
153
- output are **not** available to the child. The child is told this explicitly and
154
- instructed to say so plainly rather than guess. Restate any such finding in the
155
- `prompt`.
156
-
157
- ### Route pinning
158
-
159
- The route is pinned at launch — by default the parent's current effective
160
- provider/model, or an explicit `route {provider, model}`. It is **never**
161
- substituted, never falls back, and is never retried on a different route. An
162
- unavailable route or one with no declared context window is a typed refusal
163
- before anything is created. The child additionally asserts that every assistant
164
- message it produced came from the pinned route; a mismatch prevents the run from
165
- committing an answer at all.
166
-
167
- ### Inspect-only capability boundary
168
-
169
- v1 supports exactly one capability, `inspect`. The child is launched with
170
- `--tools read,grep,find,ls,delegate_read_artifact`, `--no-builtin-tools`, an
171
- explicit `--exclude-tools` denylist, and no ambient extensions, skills, prompt
172
- templates, themes, or context files. **The boundary is enforced by argv and the
173
- child's tool registry, not by prompt text.** There is no shell, no network, no
174
- edit/write, no recursive delegation, and no Fusion from the child. Writable
175
- profiles are deliberately out of scope.
176
-
177
- ### Budgets, spilling, and limits
178
-
179
- Admission is checked **before** the child process, the child session, or the
180
- artifact directory exists, so a refusal leaves **zero** children and **zero**
181
- artifacts. Inside the child, every model call is measured before dispatch; a call
182
- that would exceed the pinned route's allowance is refused and the run terminates
183
- with a typed `provider_context_budget_exhausted`.
184
-
185
- A tool result larger than the per-result transcript cap is written **in full** to
186
- a hashed artifact and replaced in the transcript by an explicit receipt naming
187
- the artifact, its exact byte count, its SHA-256, and how to read a bounded range.
188
- The raw payload never enters the transcript and **nothing is truncated**. The
189
- bounded `delegate_read_artifact` tool returns exactly the requested range or
190
- fails; a request past end-of-file is refused rather than silently shortened.
191
- Turn, tool-call, aggregate-output, and wall-clock limits are enforced and
192
- reported.
193
-
194
- ### Retrieving the answer
195
-
196
- The child commits exactly one self-contained result package by temp-write,
197
- `fsync`, rename, directory `fsync`. **The rename is the commit point**: a package
198
- present under its final name is complete, and its absence means no answer was
199
- accepted — whatever the process exit code was. A child that exits `0` without
200
- committing is a typed `child_exited_without_commit`, never a silent empty
201
- success. A run that degraded anything latches terminal state and **cannot**
202
- commit a success package, so a hash-valid answer can never be built on silently
203
- mutilated context.
204
-
205
- `bg_result` verifies the package identity, seed hash, route, every per-block
206
- SHA-256, and the aggregate SHA-256 before returning a single byte, and returns
207
- bytes from the buffer it verified. A running task returns a typed *not ready*
208
- result and **never blocks or polls**. An answer over the inline cap degrades to
209
- an artifact reference **explicitly**; requesting `delivery:"inline"` for it is a
210
- typed `result_too_large_for_inline` failure naming the artifact. It is **never**
211
- truncated to fit. `autoDeliver` (`never` | `when_small` | `always`) defaults to
212
- `never`: completion notifications carry metadata, and the answer is fetched
213
- deliberately with `bg_result`.
214
-
215
- ### Failure taxonomy
216
-
217
- Every delegate failure is typed and states what happened, what was preserved, and
218
- what the operator can do. Admission codes
219
- (`delegate_hook_contract_unsupported`, `delegate_isolation_unsupported`,
220
- `route_unresolved`, `route_capacity_unknown`, `seed_projection_failed`,
221
- `seed_budget_exceeded`, `seed_persist_failed`, `invalid_arguments`) always report
222
- `childCreated: false`. Execution and integrity codes include `child_spawn_failed`,
223
- `child_timeout`, `child_cancelled`, `child_turn_limit`, `child_tool_call_limit`,
224
- `child_exited_without_commit`, `provider_context_budget_exhausted`,
225
- `aggregate_tool_output_cap`, `child_result_invalid`,
226
- `child_result_encoding_invalid`, `route_attestation_missing`, `route_mismatch`,
227
- `seed_hash_mismatch`, `answer_hash_mismatch`, `artifact_spill_failed`, and
228
- `artifact_read_failed`. Retrieval states are `result_not_ready`,
229
- `result_unavailable`, `result_too_large_for_inline`, and `task_unknown`.
230
-
231
- Usage that the provider did not report is recorded as explicitly `unavailable`,
232
- never as zero.
233
-
234
- ### Proven Pi hook contract
235
-
236
- The child-side guard depends on runtime Pi behaviour, which is **proven by
237
- execution** rather than read from type declarations. The
238
- `npm run test:hook-contract` gate drives a real Pi agent loop and records what it
239
- observed. On Pi 0.83 it establishes that `context` fires once before every model
240
- call in extension load order and that returned messages reach the provider; that
241
- **throwing** from a `context` handler does **not** block the call (Pi catches it
242
- and dispatches anyway); that `ctx.abort()` does not skip the provider call site
243
- but hands it an already-aborted signal and terminates the run; and that
244
- `tool_result` fires before the result enters the transcript, chains in load order,
245
- and preserves tool-call id, role, and error flag across replacement.
246
-
247
- Because neither a throw nor an abort is a hard admission gate on its own, the
248
- guard uses abort as the barrier **and** removes the oversized content from the
249
- outgoing message set, so the request cannot carry it even if a provider ignored
250
- the aborted signal. If a Pi build cannot provide the required guarantees,
251
- `bg_delegate` refuses to spawn with a typed
252
- `delegate_hook_contract_unsupported`; the guard is never weakened to fit.
253
-
254
- Delegate artifacts are written under:
255
-
256
- ```text
257
- .pi/delegate/<session-id>-<pid>/<task-id>/
258
- ```
108
+ ```text
109
+ /logs b12ab34c 20000
110
+ ```
259
111
 
260
- Each run contains `seed.json`, `child-prompt.txt` (the exact bytes handed to the
261
- child over stdin, never a shell or positional argument),
262
- `context-omission-ledger.json`, `budget-plan.json`, `manifest.json`, the
263
- task-owned `child-session/`, any `spill/` artifacts, `result.json` once the child
264
- commits, and `outcome.json` once the parent adjudicates the run.
112
+ 5. Let a child agent inspect while you continue:
265
113
 
266
- `result.json` is written by the **child** and `outcome.json` by the **parent**, so
267
- neither writer can claim a state it did not observe. `manifest.state` records only
268
- what the parent knew at launch and is never used to decide success.
114
+ ```json
115
+ {"name":"Config reader","prompt":"Inspect the repository configuration files and report where background task settings are documented. Include file paths and quote only the relevant lines.","capability":"inspect"}
116
+ ```
269
117
 
270
- ## Fusion workflow
118
+ Call this with `bg_delegate`. When its terminal notification arrives, retrieve the answer deliberately:
271
119
 
272
- Fusion runs direct child `pi --mode text` processes only; it never calls `pi-ai` completion APIs. Each child is launched with `--no-session`, `--no-tools`, `--no-extensions`, `--no-skills`, `--no-prompt-templates`, `--no-themes`, and `--no-context-files`, plus the resolved provider/model/thinking level and the package-owned private `extensions/fusion-child.ts` metadata extension. The prompt travels over stdin, not a shell or positional argument.
120
+ ```json
121
+ {"taskId":"<task id from bg_delegate>","delivery":"inline"}
122
+ ```
273
123
 
274
- Pi text mode writes the final full answer exactly once instead of serializing cumulative reasoning/partial-message events on every token delta. The private child extension emits one compact, reasoning-free metadata record per finalized assistant message for provider/model, stop reason, the complete Pi token/cost `Usage` object, and response byte/hash validation. Fusion persists those compact records in `*.events.jsonl`; the complete answer remains in the stage response artifact. The 32 MiB child stdout cap therefore applies to one final response, not amplified JSON telemetry. Failed attempts keep the authoritative response artifact empty and, when any stdout was captured, persist it separately as an explicitly incomplete `*.response.partial.*` artifact.
124
+ Call this with `bg_result`. Retrieval is hash-verified and never silently truncated.
275
125
 
276
- Model configuration is global under the Pi agent directory:
126
+ 6. For a self-contained synthesis, ask Fusion:
277
127
 
278
- ```text
279
- fusion-models.json
280
- ```
128
+ ```json
129
+ {"prompt":"Compare the tradeoffs between a watcher, a one-shot build, and a delegated repo inspection for a large refactor."}
130
+ ```
281
131
 
282
- Missing config means all five slots are `$current`. Malformed config, stale explicit models, unavailable current models, and concurrent selector write conflicts fail loudly before child inference. Selector saves use an inter-process lock plus revision re-read before rename so simultaneous dialogs cannot silently overwrite each other. Candidate identities are anonymized before evaluation; provider/model metadata stays in local artifacts, not in evaluator prompts.
132
+ Call this with `fusion_reason`, or use `/fusion <prompt>` interactively.
283
133
 
284
- Progress is surfaced through `fusion` status updates, TUI cancellable loader UI for `/fusion`, and partial `fusion_brainstorm` tool updates. Session shutdown or reload tracks the whole invocation from entry, aborts live or initializing Fusion runs, and waits for cleanup.
134
+ More walkthrough detail: [Getting started](docs/getting-started.md).
285
135
 
286
- ### Conversation context policy
136
+ ## Pick the right workflow
287
137
 
288
- Fusion children receive a **versioned conversation projection**, not a raw execution transcript. The canonical input schema is `pi-background-tasks.fusion-input.v4` and every run states exactly what was included and what was omitted.
138
+ | Workflow | Blocking? | Context | Tools/network/write boundary | Best for | Expected behavior |
139
+ |---|---:|---|---|---|---|
140
+ | Ordinary foreground Pi work | Yes | Full current session | Whatever tools the active session has | Short reads/edits/commands where you want live back-and-forth | Pi waits for the work before responding. |
141
+ | `/bg` | No | No model child unless your command starts one | Runs your shell command; **not sandboxed** | User-started local commands, servers, watches | UI notification and footer tracking; `/bg` uses notification-only by default. |
142
+ | `bg_run` | No | No model child unless command starts one | Runs your shell command; **not sandboxed** | Agent-started long commands | Returns task id/output path; defaults to notification plus automatic follow-up wake. |
143
+ | `bg_delegate` + `bg_result` | No launch; retrieval is point-in-time | Frozen visible conversation projection | Inspect-only child: read, grep, find, ls, artifact read; no shell, writes, network, recursion | Context-aware read-only investigation while parent continues | Launch returns immediately; result is committed by child and hash-verified by retrieval. |
144
+ | `bg_run_pi_attested` | No | Prompt passed to one direct child Pi run | Direct `pi --mode json`; no shell command; writes requested report path | Evidence-oriented direct Pi task | Emits local attestation sidecar only after successful completion. |
145
+ | `/fusion` / `fusion_reason` | Synchronous | Versioned conversation projection plus prompt | Candidates/evaluator/repair/merger run with no tools | Self-contained reasoning and synthesis | Three candidates → blind evaluator → optional bounded repair if evaluator JSON is invalid → merger. |
146
+ | `fusion_investigate` | Synchronous | Clean task input only | Candidate read-only repo tools; evaluator/repair/merger no tools | Independent repo investigation | Restate all needed facts in fields; parent hidden/tool context is not visible. |
147
+ | `fusion_research` | Synchronous | Clean task input only | Candidate read-only repo tools plus targeted fetch of caller-supplied public URLs only | URL-backed synthesis | Targeted URL retrieval, **not web search**. |
148
+ | `fusion_validate` | Synchronous | Clean task input only | Advisory read-only validation review | Second-opinion review of completed work | Not a replacement for tests, builds, linters, security scans, or human review. |
289
149
 
290
- The projection transform (`visible-conversation-ledger-v2`) is shared by both entry points:
150
+ See [Choose a workflow](docs/choose-a-workflow.md) for a decision tree and tradeoffs.
291
151
 
292
- | Content | Disposition |
293
- |---|---|
294
- | User text | included verbatim, never clipped |
295
- | Assistant text | included verbatim, never clipped |
296
- | User image blocks | `[Image omitted from fusion text transcript: <mime-type>]` marker |
297
- | Assistant thinking | excluded; recorded as an omission receipt |
298
- | Tool-call arguments | excluded; recorded as an omission receipt |
299
- | Tool-result payloads | excluded; recorded as an omission receipt |
300
- | Tool-result images | excluded; recorded as an omission receipt (never raw bytes) |
301
- | Active `fusion_brainstorm` call and its sibling calls | scope-excluded from the branch |
152
+ ## Copy-paste examples
302
153
 
303
- Omissions are **explicit, deterministic, and auditable** — never silent. Each omitted event produces a ledger row with its kind, exact byte count, and SHA-256 of the omitted bytes. Fusion v4 encodes child-facing projection entries as positional tuples to remove repeated object keys while preserving every role, ordinal, span, byte total, count, and text byte:
154
+ ### `bg_run`: start long shell work
304
155
 
305
156
  ```json
306
- ["t","u",0,0,"hello"]
307
- ["o",[1,9],29019,[0,5,5]]
157
+ {
158
+ "name": "Docs preview",
159
+ "command": "npm run docs:dev",
160
+ "isAgent": false,
161
+ "timeoutSeconds": 3600
162
+ }
308
163
  ```
309
164
 
310
- Text tuples are `["t", role, sourceOrdinal, blockOrdinal, text]`, where `role` is `"u"` for user or `"a"` for assistant. Omission tuples are `["o", [firstSourceOrdinal, lastSourceOrdinal], bytes, [assistantThinking, toolCalls, toolResultTexts]]`. The span is inclusive, `bytes` is the total omitted non-image payload for that run, and the count tuple order is fixed. Per-event hashes, ledger indices, and per-event byte details live in `context-omission-ledger.json`, not in the prompt: a child cannot verify a hash of payload it does not hold, so forwarding one only consumed context. That ledger also carries a `projection_map` proving every ledger row is represented by exactly one receipt or ledger-only image marker, and `accounting.omission_receipt_utf8_bytes` records the exact compact tuple receipt cost. The complete ledger is persisted as `context-omission-ledger.json`, and its row shape and root hash are unchanged by the compact encoding. **No head, tail, or preview of an omitted payload is ever forwarded** (`tool_payload_preview_bytes` is `0`), because an arbitrary prefix is usually irrelevant and can leak secrets or carry tool-output prompt injection. Repeated construction is byte-identical via canonical JSON, so prompt bytes and hashes are stable.
311
-
312
- Two entry points share the transform but differ in request authority:
313
-
314
- | Entry point | Policy id | `request.authority` |
315
- |---|---|---|
316
- | `fusion_brainstorm({prompt})` | `fusion-tool-explicit-v2` | `explicit_text` — the prompt is authoritative and self-contained |
317
- | `/fusion [prompt]` | `fusion-command-conversation-v2` | `directive_over_projected_conversation` |
318
-
319
- **Documented limitation:** facts that exist only inside omitted tool output are not available to Fusion children. Restate any required finding as visible conversation text, or include it in the `fusion_brainstorm` prompt. Children are instructed to say so plainly rather than guess. No model-generated summarization is used as hidden preprocessing.
320
-
321
- ### Stage budgets
322
-
323
- Every prompt-expansion stage — candidate, evaluator, evaluation repair, and merger — is size-checked **before any child process is created**, and each stage is checked against **its own configured route**, so a large-context slot cannot hide a small-context sibling and a small slot cannot veto stages it never serves.
324
-
325
- Input forecasting uses the shared affine estimator `estimateInputTokens({family, segments})`: additive integer byte-class accounting plus a 512-token affine intercept. Calibrated normal-ASCII rates are used only for backed exact model IDs, measured prompts at or above 50 KiB, and prompts that pass the low-whitespace dense-ASCII gate. That gate records the measured whitespace fraction in `budget-plan.json` and falls back conservatively for out-of-distribution near-zero-whitespace payloads; it is a heuristic token-density proxy, not a bound. Multibyte UTF-8 uses the conservative 2.0 B/tok fatal rate while persisting the provable 1.00 B/tok ceiling as advisory. The calibration basis is 882 real large Fusion prompts: Anthropic observed floor 2.047 B/tok (shipped `r=1.73`) and Codex observed floor 3.400 B/tok (shipped `r=2.89`); unknown providers and unbacked model IDs use the unbacked 1.00 B/tok floor and are surfaced in artifacts and result details.
326
-
327
- Each stage is forecast with its **real prompt builder**, rendered with empty embedded-output slots, plus the enforced output contracts for whatever that stage will embed:
328
-
329
- | Stage | Embeds | Mandatory |
330
- |---|---|---|
331
- | `candidate` | canonical input only | yes, once per slot |
332
- | `evaluation` | + 3 candidate answers | yes |
333
- | `evaluation_repair` | + 3 candidates, invalid evaluator output, diagnostics | conditional, still budgeted |
334
- | `merge` | + 3 candidates, validated evaluation | yes |
335
-
336
- `evaluation_repair` only runs when the evaluator returns schema-invalid JSON, but it is budgeted unconditionally: "uncommon" is not a reliability contract.
337
-
338
- Output sizes are guaranteed by **enforced contracts**, not assumptions. Each stage has a maximum response size measured in *JSON-rendered transfer bytes* — the bytes the response actually costs once a later stage embeds it, escaping included (candidate 48 KiB, evaluator 64 KiB, merger 64 KiB, repair diagnostics 8 KiB). Measuring the rendered form removes any escaping guess: quotes, backslashes, and newlines expand 2x and control characters up to 6x. A response over its contract is a loud `child_output_cap` failure; it is preserved in the run artifacts and is never sliced, truncated, or forwarded.
165
+ Expected: returns immediately with a task id, PID when available, and `.pi/tasks/...output`. The command runs as an ordinary local shell command with your user permissions; it can invoke networked tools or paid services if the command itself does so.
339
166
 
340
- When a workflow cannot fit, the error names the **first failing mandatory stage** in deterministic order (candidate slots, then evaluation, then merge), lists every other blocking stage, and labels conditional ones. It never blames a stage that would have succeeded.
167
+ ### `bg_delegate`: context-seeded read-only investigation
341
168
 
342
- Remediation is derived, not guessed: Fusion re-plans the entire workflow **with the request removed**. If it still fails, the error says plainly that shortening the request cannot help and points at starting a fresh conversation or raising the route's context window. If it then fits, the request is what determines feasibility, and the error states the exact minimum byte reduction and the maximum safe request size.
343
-
344
- Preflight is two-tiered: input-only forecasts are fatal (`prompt_budget_exceeded_forecast`), while worst-case downstream output reservations are warning-only and recorded in `budget-plan.json`. Exact rendered per-stage checks remain fatal (`prompt_budget_exceeded_measured`). Runs that fit still emit advisory warnings for tight utilization or reservation overage. The warning never alters behaviour.
345
-
346
- Every configured route must also satisfy a documented minimum capacity; smaller routes are rejected at configuration time with an actionable error naming the requirement, rather than being accepted and failing later at the provider. All route capacities, per-stage forecasts, headroom, utilization, the byte composition of the blocking stage, and the blockers list are persisted as `budget-plan.json`.
347
-
348
- If an input still exceeds the safe budget, Fusion fails with `prompt_budget_exceeded_forecast` for input-only preflight or `prompt_budget_exceeded_measured` for exact rendered prompts. The error names the stage, measured bytes, measured token upper bound, allowed tokens, the limiting configured model and its context window, estimator source, and concrete remediation. **Zero children are launched** when preflight rejects. Provider context-window failures remain loud child failures; there is no hidden local truncation and no silent fallback anywhere in this path.
349
-
350
- ## Extension EventBus API
351
-
352
- Version `0.7.0` exposes a real extension-to-extension service over Pi's documented `pi.events` bus. This is not a `ctx` method and it does not call a second task manager: requests route to the same `BackgroundTaskRegistry` used by `bg_run`, `bg_status`, `bg_logs`, and `bg_kill`.
169
+ ```json
170
+ {
171
+ "name": "Route audit",
172
+ "prompt": "Inspect the package source and identify where delegate route pinning is enforced. Return file paths, function names, and a short explanation. If a fact exists only in omitted parent tool output, say it is unavailable rather than guessing.",
173
+ "capability": "inspect",
174
+ "autoDeliver": "never"
175
+ }
176
+ ```
353
177
 
354
- Public constants are exported from `src/core/extension-api.ts`:
178
+ Then retrieve:
355
179
 
356
- | Purpose | Value |
357
- |---|---|
358
- | Request channel | `pi-background-tasks:request:v1` |
359
- | Response channel | `pi-background-tasks:response:v1` |
360
- | Terminal channel | `pi-background-tasks:terminal:v1` |
361
- | Request schema | `pi-background-tasks.extension-request.v1` |
362
- | Response schema | `pi-background-tasks.extension-response.v1` |
363
- | Terminal schema | `pi-background-tasks.extension-terminal.v1` |
180
+ ```json
181
+ {
182
+ "taskId": "<delegate task id>",
183
+ "delivery": "inline"
184
+ }
185
+ ```
364
186
 
365
- Requests are closed frames: `{ schema_version, request_id, operation, payload }`, where `operation` is one of `capabilities`, `run`, `status`, `logs`, or `kill`. Responses echo the same `request_id` and `operation`, set `ok`, and contain exactly one of `result` or bounded `error`. Malformed frames, unknown keys, duplicate request IDs, requests before `session_start`, and requests during shutdown receive `ok: false`; they are not silently dropped or rerouted to shell fallback behavior.
187
+ Expected: `bg_delegate` returns a launch receipt immediately. `bg_result` returns a typed not-ready result while running; after commit it verifies package identity, seed hash, route, block hashes, and aggregate hash before returning bytes. Oversized answers become explicit artifact references, not truncated inline text.
366
188
 
367
- `capabilities` returns exactly:
189
+ ### `bg_run_pi_attested`: local evidence for one Pi child
368
190
 
369
191
  ```json
370
- {"api_version":1,"run":true,"run_is_agent":true,"run_completion_trigger":true,"status":true,"logs":true,"logs_bounded":true,"kill":true}
192
+ {
193
+ "name": "Migration report",
194
+ "provider": "openai-codex",
195
+ "model": "gpt-5.5",
196
+ "prompt": "Inspect the repository and write a concise migration report to reports/migration.md.",
197
+ "reportPath": "reports/migration.md",
198
+ "timeoutSeconds": 1800
199
+ }
371
200
  ```
372
201
 
373
- `run.payload` is the strict `bg_run` launch object: `name`, `command`, `isAgent`, `notifyOnCompletion`, `triggerOnCompletion`, plus optional positive-integer `timeoutSeconds`. The result is a `BgTaskSnapshot`. `status` returns `{ tasks }`; `logs` returns the existing bounded log detail fields plus bounded `text`; `kill` returns `{ task, message }`. Task status vocabulary is exactly `running`, `completed`, `failed`, or `killed`.
202
+ Expected: launches exactly one direct `pi --mode json` child using structured provider/model fields. It rejects direct API-key/auth-file launch arguments and only writes the attestation sidecar after successful completion and durable hashes. The attestation is local evidence, not cryptographic proof against a compromised machine or provider.
374
203
 
375
- Terminal events keep the strict paired-consumer frame `{ schema_version: "pi-background-tasks.extension-terminal.v1", task }`. They are correlated by `task.id`, published exactly once per task after final output and terminal metadata durability, and for EventBus `run`/`kill` requests they are held behind a response barrier so even an immediately exiting or immediately killed child cannot emit its terminal event until the correlated response has been delivered and one microtask turn has allowed consumers to bind the returned task id. Terminal EventBus delivery exceptions are logged loudly and retried; a task is marked terminal-published only after the EventBus emit returns successfully.
204
+ ### Fusion tools
376
205
 
377
- ## Runtime files
378
-
379
- Task output and metadata are written under the current project:
380
-
381
- ```text
382
- .pi/tasks/<session-id>-<pid>/<task-id>.output
383
- .pi/tasks/<session-id>-<pid>/<task-id>.json
206
+ ```json
207
+ {"prompt":"Design a rollback strategy for a risky database migration. Include assumptions and failure modes."}
384
208
  ```
385
209
 
386
- Fusion writes private debugging artifacts under:
210
+ Use with `fusion_reason` for self-contained synthesis.
387
211
 
388
- ```text
389
- .pi/fusion/<session-id>-<pid>/<run-id>/
212
+ ```json
213
+ {
214
+ "objective": "Find how background task output is capped and surfaced.",
215
+ "background": ["We are evaluating pi-background-tasks behavior for long-running commands."],
216
+ "deliverable": "File paths, constants, defaults, and user-visible behavior.",
217
+ "scope": ["src"],
218
+ "constraints": ["Read-only inspection only."]
219
+ }
390
220
  ```
391
221
 
392
- Each run contains `manifest.json`, `canonical-input.json`, `context-omission-ledger.json`, `budget-plan.json`, candidate/evaluation/merge prompts, raw child JSONL events, stderr, responses, `blind-candidates.json`, `evaluation.json`, `merged.md`, and `error.json` for failed/cancelled runs. Persisted stage prompts are byte-identical to the exact bytes written to that child's stdin. `context-omission-ledger.json` carries the complete source-ordered omission ledger, and `budget-plan.json` records every configured route's capacity plus the pre-candidate feasibility decision, so a rejected run is as auditable as a successful one. Artifact files are written by private temp-file/fsync/rename, and v2 manifests persist cumulative child usage plus per-attempt observed usage/model data for successful, failed, and cancelled child attempts. Every usage record preserves the complete Pi cost breakdown; the same exact shape is cloned into `fusion_brainstorm` tool results so newer Pi hosts can calculate and replay footer/session statistics safely. These artifacts are local evidence only; they are not shown in `/jobs` or the background-task dock.
393
-
394
- For attested Pi tasks only, the task id is `b` plus 32 random hex characters (128 bits) and additional flat siblings are written in the same directory:
222
+ Use with `fusion_investigate`.
395
223
 
396
- ```text
397
- .pi/tasks/<session-id>-<pid>/<task-id>.pi-events.jsonl
398
- .pi/tasks/<session-id>-<pid>/<task-id>.stderr
399
- .pi/tasks/<session-id>-<pid>/<task-id>.pi-telemetry-wrapper.cjs
400
- .pi/tasks/<session-id>-<pid>/<task-id>.attestation.json
224
+ ```json
225
+ {
226
+ "objective": "Summarize the installation syntax Pi documents for packages.",
227
+ "background": ["We need package README install examples to match Pi package docs."],
228
+ "deliverable": "A short summary with caveats.",
229
+ "sources": [
230
+ {"url":"https://github.com/earendil-works/pi-coding-agent","purpose":"Pi package documentation repository"}
231
+ ]
232
+ }
401
233
  ```
402
234
 
403
- The attestation sidecar uses `schema_version: "phase2.pi_task_attestation.v1"` and is written last, after metadata/output/events/stderr/wrapper/report bytes are closed and hashed. An attested task does not become externally visible as `completed` until final metadata and the sidecar are durable. These are local runtime artifacts and should remain gitignored.
404
-
405
- ## Durability model
406
-
407
- All task metadata, task output/events/stderr, attestation sidecars, Fusion artifacts, and the global `fusion-models.json` are written through one shared primitive in `src/core/durable-fs.ts`.
408
-
409
- The invariant is: **open the file once with write intent, write and `fsync` through that same writable handle, then close.** A pathname is never reopened merely to flush it. This is required for Windows correctness — `FlushFileBuffers` needs a handle with write access, so flushing through a reopened read-only handle fails with `EPERM: operation not permitted, fsync` and previously broke every background task on Windows.
235
+ Use with `fusion_research`. Only declared public `http(s)` URLs may be fetched; this is not a search tool.
410
236
 
411
- Two write modes are provided:
412
-
413
- | Mode | Flags | Used for |
414
- |---|---|---|
415
- | Direct durable write | `w`, inherited mode | task output, events, stderr |
416
- | Atomic replacement | private `0o600` temp with `wx`, then rename | task metadata, attestations, Fusion artifacts/manifests, model config |
417
-
418
- Failure handling is loud and typed. Every open, write, `fsync`, rename, close, and temp-cleanup failure raises `DurableFileError` carrying the failing operation, path, native error code, and original cause. **A failed `fsync` is never tolerated or downgraded**, so the package cannot report durable state it did not actually achieve. A close or cleanup failure is attached as a secondary `cleanupFailures` entry and never replaces the primary error, and a temporary file is only removed when that same call created it.
419
-
420
- **Platform limitation.** After the rename, the parent directory is itself `fsync`ed on POSIX so the new directory entry is crash-durable. Node exposes no portable equivalent on Windows, so that step is skipped there: file contents are still explicitly flushed before the rename and rename failures remain fatal, but the package cannot offer the same crash-durability guarantee for the directory entry on Windows. When a failure occurs after a successful rename, `DurableFileError.renameCompleted` is `true`, meaning the replacement may already be visible even though the operation reported an error.
237
+ ```json
238
+ {
239
+ "objective": "Review whether a documentation-only change is ready to ship.",
240
+ "background": ["The change edits README and package-local docs only."],
241
+ "changeSummary": "Replaced monolithic README with landing page and moved operational details into docs.",
242
+ "scope": ["README.md", "docs/getting-started.md", "docs/choose-a-workflow.md"],
243
+ "acceptanceCriteria": ["Install commands are accurate", "Safety limitations are explicit", "Examples match public schemas"],
244
+ "verification": {
245
+ "status": "provided",
246
+ "evidence": [{"check":"Focused link check", "outcome":"All local README links resolve"}]
247
+ }
248
+ }
249
+ ```
421
250
 
422
- ## Safety model
251
+ Use with `fusion_validate` for advisory read-only review.
423
252
 
424
- - Commands are spawned and tracked with `child_process.spawn`; the package does not rely on shell `&`.
425
- - Fusion inference is isolated to direct child `pi --mode text` invocations with tools/skills/session/context files disabled and only the package-owned compact metadata extension explicitly loaded; no direct completion API, API-key argument, or model fallback is used.
426
- - Attested Pi tasks are a local, unsigned, same-user-writable attestation path for downstream gates. They bind source bytes and observed Pi/ModelRegistry facts; they are not cryptographic proof against a malicious local user, compromised Pi binary, or compromised provider.
427
- - stdout/stderr are captured to task output files.
428
- - Model-visible logs are bounded and point to full output files.
429
- - POSIX process groups are used for process-tree kill where possible, with child-process fallback.
430
- - On Windows, process-tree termination uses `%SystemRoot%\\System32\\taskkill.exe` directly (never `PATH`) with a two-stage `/T` then `/T /F` flow. Stage one is a logical Windows termination request, not SIGTERM and not a guaranteed graceful shutdown: console processes may ignore it, and re-parented descendants can escape the tree. Strong containment would require Windows Job Objects via a native component, which is out of scope.
431
- - Running tasks are cleaned up on Pi session shutdown/reload.
432
- - Child Pi processes are never launched through a shell. On Windows the npm `pi.cmd` shim is deliberately not executed, because a batch shim cannot preserve argument bytes safely; the package resolves the Pi package's own CLI entry and launches it with `process.execPath` instead. Batch, PowerShell, and extensionless launch targets are rejected loudly rather than executed, and a launch target resolving outside the Pi package root is rejected.
433
- - Cross-Pi-restart process reattachment and Ctrl+B backgrounding of already-running foreground tools are intentionally out of scope.
253
+ ## Footer dock
434
254
 
435
- ### Windows shell and telemetry
255
+ <p align="center">
256
+ <img src="docs/assets/footer-dock.svg" alt="Illustration of the pi-background-tasks footer dock with running and completed tasks" width="760">
257
+ </p>
436
258
 
437
- `cmd.exe` remains the default Windows shell, so existing commands written for `cmd` keep working unchanged. The generic `SHELL` variable is deliberately ignored on Windows, because honouring it would silently change the command language for `%VAR%`, `set`, `dir`, backslash paths, and quoting.
259
+ When tasks are running or unseen completions exist, the footer shows a compact `bg ...` segment. Press **Shift↓** to open the focused bottom dock. Use `/bg-clear` to acknowledge finished-task footer notices in any terminal.
438
260
 
439
- A POSIX shell is available as an explicit opt-in via `PI_BG_SHELL=bash`, with an optional absolute `PI_BG_SHELL_PATH`. Invalid values fail loudly rather than falling back. Bash is invoked with `-c` and never `-lc`, because a login shell runs profile scripts whose banner output would be captured into task output.
261
+ | Control | Action |
262
+ |---|---|
263
+ | `Shift↓` | Open the dock |
264
+ | `/bg-clear` | Clear finished-task notices |
265
+ | `↑` / `↓`, `PageUp` / `PageDown` | Move through list or scroll output tail |
266
+ | `Enter` / `→` | Inspect details |
267
+ | `←` | Return to list |
268
+ | `k` | Stop selected running task |
269
+ | `R` | Rerun selected command |
270
+ | `c` | Show copyable output path |
271
+ | `x` / `Esc` / `q` | Close dock |
440
272
 
441
- **Documented limitation:** `isAgent: true` Pi-agent telemetry wrapping works by installing a POSIX shell function that intercepts `pi`. That mechanism has no safe `cmd.exe` equivalent, so under the Windows `cmd` dialect the command is left byte-for-byte unchanged and telemetry is reported as unavailable with the reason `win32-cmd-cannot-safely-intercept-pi-argv`. Zero usage is never synthesized, and no warning text is injected into captured command output. Use `PI_BG_SHELL=bash` on Windows when agent telemetry is required.
273
+ Agent tasks launched through `pi -p ...` or `pi --mode json ...` and marked `isAgent:true` can show task-owned model/context/token/tool telemetry. Missing child telemetry is shown as unavailable, not synthesized as zero.
442
274
 
443
- On Windows, argument lists are checked against the 32,767-code-unit command-line limit before a child is created, so an oversized invocation fails loudly with the measured length rather than producing a confusing native spawn error. Argument contents are never included in that error.
275
+ <a id="commands"></a>
276
+ <a id="footer-dock-ux"></a>
277
+ <a id="update-available-notice"></a>
278
+ <a id="llm-tools"></a>
279
+ <a id="delegated-background-agents"></a>
280
+ <a id="fusion-workflow"></a>
281
+ <a id="conversation-context-policy"></a>
282
+ <a id="extension-eventbus-api"></a>
283
+ <a id="runtime-files"></a>
284
+ <a id="durability-model"></a>
285
+ <a id="safety-model"></a>
286
+ <a id="windows-shell-and-telemetry"></a>
444
287
 
445
- ## Development and QA
288
+ ## Architecture, trust, and safety
446
289
 
447
- Default QA gate:
290
+ - Runtime task files live under `.pi/tasks/<session-id>-<pid>/`; Fusion artifacts under `.pi/fusion/...`; delegate artifacts under `.pi/delegate/...`.
291
+ - Shell jobs are tracked by the package, but they are not sandboxed. Treat commands as local processes with your permissions and credentials.
292
+ - Delegate and Fusion child Pi processes are route-pinned where applicable; delegate/Fusion paths do not silently substitute routes.
293
+ - Fusion uses direct child `pi --mode text` processes, not direct completion APIs. Frontier Fusion routes are admitted only through Pi Anthropic or Codex subscription OAuth; metered frontier API credentials are rejected before child creation.
294
+ - Fusion research fetches only caller-supplied public `http(s)` URLs with bounded retrieval. It is not web search and not a secret-exfiltration boundary.
295
+ - Attestation sidecars are local, unsigned, same-user-writable evidence. They are useful for downstream local gates, but not cryptographic proof against local compromise, a compromised Pi binary, or a compromised provider.
296
+ - Metadata, attestations, delegate/Fusion artifacts, and configuration replacements use write/fsync/rename durability patterns. Ordinary task output is closed and drained before terminal publication but is not explicitly fsynced. POSIX directory entries are fsynced after atomic replacement; Windows lacks the same portable directory-entry crash-durability guarantee.
448
297
 
449
- ```bash
450
- npm run test
451
- ```
298
+ Detailed operations: [Configuration](docs/operations/configuration.md).
452
299
 
453
- Smoke and release checks:
300
+ ## EventBus and Autopilot integration
454
301
 
455
- ```bash
456
- npm run smoke
457
- npm run pack:dry-run
458
- npm run test:compat
459
- ```
302
+ Other Pi extensions can control the same `BackgroundTaskRegistry` through Pi's `events` bus instead of shelling out or maintaining a second task manager. The public channels are:
460
303
 
461
- Full interactive QA gate:
304
+ | Purpose | Channel |
305
+ |---|---|
306
+ | Request | `pi-background-tasks:request:v1` |
307
+ | Response | `pi-background-tasks:response:v1` |
308
+ | Terminal task event | `pi-background-tasks:terminal:v1` |
462
309
 
463
- ```bash
464
- npm run test:full
465
- ```
310
+ Operations are `capabilities`, `run`, `status`, `logs`, and `kill`. This is the integration point for orchestrators such as Autopilot that need non-blocking package-managed work with bounded logs and correlated terminal events. Consumers must deduplicate terminal frames by `task.id`: an EventBus listener failure can cause a retried publication.
466
311
 
467
- The suite includes typecheck, unit, SDK, RPC, component, package, PTY/TUI, and scripted-provider coverage for the focused dock, lifecycle safety, and completion follow-up behavior.
312
+ ## Documentation map
468
313
 
469
- Note: the repo QA standard requires exhaustive coverage of every public behavior and plausible edge case. `TEST_PLAN.md` tracks the current coverage matrix and any future edge-case additions.
314
+ | Need | Read |
315
+ |---|---|
316
+ | First install and first task | [Getting started](docs/getting-started.md) |
317
+ | Which workflow/tool to choose | [Choose a workflow](docs/choose-a-workflow.md) |
318
+ | Environment variables, shells, output caps, model config, offline behavior | [Configuration](docs/operations/configuration.md) |
319
+ | Package QA expectations | [TESTING.md](TESTING.md) and [TEST_PLAN.md](TEST_PLAN.md) |
320
+ | Publishing notes | [PUBLISHING.md](PUBLISHING.md) |
321
+ | License | [LICENSE](LICENSE) |
470
322
 
471
- This package follows the repo-wide Pi extension QA standard documented in:
323
+ ## Contributing
472
324
 
473
- - [`../EXTENSION_QA_STANDARD.md`](../EXTENSION_QA_STANDARD.md)
474
- - [`../EXTENSION_TESTING_PLAYBOOK.md`](../EXTENSION_TESTING_PLAYBOOK.md)
475
- - [`TEST_PLAN.md`](TEST_PLAN.md)
476
- - [`TESTING.md`](TESTING.md)
325
+ Keep user-facing claims tied to source. If you change public schemas, command behavior, durability, model routing, or environment variables, update these package-local docs in the same change and run focused checks appropriate to the edit.