pi-background-tasks 2.5.0 → 2.6.0

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 (203) hide show
  1. package/BACKGROUND-TASKS-INSTRUCTIONS.md +1 -1
  2. package/README.md +70 -25
  3. package/TESTING.md +42 -2
  4. package/TEST_PLAN.md +7 -4
  5. package/dist/extensions/anthropic-attribution-child.js +4 -0
  6. package/dist/extensions/anthropic-attribution-child.js.map +1 -0
  7. package/dist/extensions/anthropic-attribution.js +107 -0
  8. package/dist/extensions/anthropic-attribution.js.map +1 -0
  9. package/dist/extensions/background-tasks.js +2 -0
  10. package/dist/extensions/background-tasks.js.map +1 -0
  11. package/dist/extensions/delegate-child.js +2 -0
  12. package/dist/extensions/delegate-child.js.map +1 -0
  13. package/dist/extensions/fusion-child.js +2 -0
  14. package/dist/extensions/fusion-child.js.map +1 -0
  15. package/dist/package.json +5 -0
  16. package/dist/src/core/anthropic-attribution-path.js +24 -0
  17. package/dist/src/core/anthropic-attribution-path.js.map +1 -0
  18. package/dist/src/core/anthropic-attribution.js +2247 -0
  19. package/dist/src/core/anthropic-attribution.js.map +1 -0
  20. package/dist/src/core/attested-pi-contract.js +5 -0
  21. package/dist/src/core/attested-pi-contract.js.map +1 -0
  22. package/dist/src/core/attested-pi-run.js +749 -0
  23. package/dist/src/core/attested-pi-run.js.map +1 -0
  24. package/dist/src/core/canonical-json.js +19 -0
  25. package/dist/src/core/canonical-json.js.map +1 -0
  26. package/dist/src/core/common.js +787 -0
  27. package/dist/src/core/common.js.map +1 -0
  28. package/dist/src/core/config.js +78 -0
  29. package/dist/src/core/config.js.map +1 -0
  30. package/dist/src/core/context/parent-snapshot.js +75 -0
  31. package/dist/src/core/context/parent-snapshot.js.map +1 -0
  32. package/dist/src/core/context/token-budget.js +631 -0
  33. package/dist/src/core/context/token-budget.js.map +1 -0
  34. package/dist/src/core/context/visible-conversation-v2.js +390 -0
  35. package/dist/src/core/context/visible-conversation-v2.js.map +1 -0
  36. package/dist/src/core/delegate/artifacts.js +357 -0
  37. package/dist/src/core/delegate/artifacts.js.map +1 -0
  38. package/dist/src/core/delegate/budget.js +266 -0
  39. package/dist/src/core/delegate/budget.js.map +1 -0
  40. package/dist/src/core/delegate/facade-contract.js +7 -0
  41. package/dist/src/core/delegate/facade-contract.js.map +1 -0
  42. package/dist/src/core/delegate/hook-contract-evidence.json +18 -0
  43. package/dist/src/core/delegate/hook-contract.js +124 -0
  44. package/dist/src/core/delegate/hook-contract.js.map +1 -0
  45. package/dist/src/core/delegate/launch.js +366 -0
  46. package/dist/src/core/delegate/launch.js.map +1 -0
  47. package/dist/src/core/delegate/result-package.js +293 -0
  48. package/dist/src/core/delegate/result-package.js.map +1 -0
  49. package/dist/src/core/delegate/runner.js +398 -0
  50. package/dist/src/core/delegate/runner.js.map +1 -0
  51. package/dist/src/core/delegate/seed.js +334 -0
  52. package/dist/src/core/delegate/seed.js.map +1 -0
  53. package/dist/src/core/delegate/types.js +103 -0
  54. package/dist/src/core/delegate/types.js.map +1 -0
  55. package/dist/src/core/durable-fs.js +372 -0
  56. package/dist/src/core/durable-fs.js.map +1 -0
  57. package/dist/src/core/extension-api.js +382 -0
  58. package/dist/src/core/extension-api.js.map +1 -0
  59. package/dist/src/core/fusion/artifacts.js +654 -0
  60. package/dist/src/core/fusion/artifacts.js.map +1 -0
  61. package/dist/src/core/fusion/budget.js +949 -0
  62. package/dist/src/core/fusion/budget.js.map +1 -0
  63. package/dist/src/core/fusion/child-protocol.js +176 -0
  64. package/dist/src/core/fusion/child-protocol.js.map +1 -0
  65. package/dist/src/core/fusion/claude-cache.js +156 -0
  66. package/dist/src/core/fusion/claude-cache.js.map +1 -0
  67. package/dist/src/core/fusion/clean-context.js +64 -0
  68. package/dist/src/core/fusion/clean-context.js.map +1 -0
  69. package/dist/src/core/fusion/config.js +349 -0
  70. package/dist/src/core/fusion/config.js.map +1 -0
  71. package/dist/src/core/fusion/context.js +184 -0
  72. package/dist/src/core/fusion/context.js.map +1 -0
  73. package/dist/src/core/fusion/evaluation.js +696 -0
  74. package/dist/src/core/fusion/evaluation.js.map +1 -0
  75. package/dist/src/core/fusion/facade-contract.js +3 -0
  76. package/dist/src/core/fusion/facade-contract.js.map +1 -0
  77. package/dist/src/core/fusion/orchestrator.js +867 -0
  78. package/dist/src/core/fusion/orchestrator.js.map +1 -0
  79. package/dist/src/core/fusion/output-contract.js +27 -0
  80. package/dist/src/core/fusion/output-contract.js.map +1 -0
  81. package/dist/src/core/fusion/pi-child.js +1741 -0
  82. package/dist/src/core/fusion/pi-child.js.map +1 -0
  83. package/dist/src/core/fusion/prompts.js +260 -0
  84. package/dist/src/core/fusion/prompts.js.map +1 -0
  85. package/dist/src/core/fusion/result-package.js +857 -0
  86. package/dist/src/core/fusion/result-package.js.map +1 -0
  87. package/dist/src/core/fusion/source-policy.js +259 -0
  88. package/dist/src/core/fusion/source-policy.js.map +1 -0
  89. package/dist/src/core/fusion/types.js +207 -0
  90. package/dist/src/core/fusion/types.js.map +1 -0
  91. package/dist/src/core/fusion/web-fetch.js +777 -0
  92. package/dist/src/core/fusion/web-fetch.js.map +1 -0
  93. package/dist/src/core/fusion/workflows.js +126 -0
  94. package/dist/src/core/fusion/workflows.js.map +1 -0
  95. package/dist/src/core/lazy-module.js +181 -0
  96. package/dist/src/core/lazy-module.js.map +1 -0
  97. package/dist/src/core/pi-launch.js +431 -0
  98. package/dist/src/core/pi-launch.js.map +1 -0
  99. package/dist/src/core/registry.js +3331 -0
  100. package/dist/src/core/registry.js.map +1 -0
  101. package/dist/src/core/reload-shell-owner.js +1396 -0
  102. package/dist/src/core/reload-shell-owner.js.map +1 -0
  103. package/dist/src/core/shell-policy.js +80 -0
  104. package/dist/src/core/shell-policy.js.map +1 -0
  105. package/dist/src/core/task-durable.js +51 -0
  106. package/dist/src/core/task-durable.js.map +1 -0
  107. package/dist/src/core/update-check.js +92 -0
  108. package/dist/src/core/update-check.js.map +1 -0
  109. package/dist/src/core/windows-taskkill.js +185 -0
  110. package/dist/src/core/windows-taskkill.js.map +1 -0
  111. package/dist/src/delegate-child-extension.js +776 -0
  112. package/dist/src/delegate-child-extension.js.map +1 -0
  113. package/dist/src/delegate-extension.js +746 -0
  114. package/dist/src/delegate-extension.js.map +1 -0
  115. package/dist/src/extension.js +953 -0
  116. package/dist/src/extension.js.map +1 -0
  117. package/dist/src/fusion-child-extension.js +760 -0
  118. package/dist/src/fusion-child-extension.js.map +1 -0
  119. package/dist/src/fusion-extension.js +1030 -0
  120. package/dist/src/fusion-extension.js.map +1 -0
  121. package/dist/src/ui/background-tasks-manager.js +689 -0
  122. package/dist/src/ui/background-tasks-manager.js.map +1 -0
  123. package/dist/src/ui/fusion-model-selector.js +277 -0
  124. package/dist/src/ui/fusion-model-selector.js.map +1 -0
  125. package/docs/INDEX.md +35 -33
  126. package/docs/api/eventbus-v1.md +21 -5
  127. package/docs/choose-a-workflow.md +2 -0
  128. package/docs/commands/bg-clear.md +3 -3
  129. package/docs/commands/bg-update.md +3 -3
  130. package/docs/commands/bg.md +18 -6
  131. package/docs/commands/claude-cache.md +4 -4
  132. package/docs/commands/fusion-models.md +49 -4
  133. package/docs/commands/fusion.md +3 -3
  134. package/docs/commands/jobs.md +3 -3
  135. package/docs/commands/kill.md +3 -3
  136. package/docs/commands/logs.md +3 -3
  137. package/docs/commands/task-manager.md +5 -5
  138. package/docs/concepts/completion-delivery.md +6 -2
  139. package/docs/getting-started.md +7 -1
  140. package/docs/manifest.json +228 -42
  141. package/docs/operations/configuration.md +58 -5
  142. package/docs/operations/releasing.md +4 -2
  143. package/docs/operations/testing.md +22 -0
  144. package/docs/operations/troubleshooting.md +6 -2
  145. package/docs/read-before-edit.md +44 -31
  146. package/docs/reference/runtime-contracts.md +87 -51
  147. package/docs/reference/shortcuts-and-dock.md +28 -14
  148. package/docs/subsystems/anthropic-attribution.md +23 -11
  149. package/docs/subsystems/attested-pi-runs.md +12 -7
  150. package/docs/subsystems/background-task-runtime.md +53 -11
  151. package/docs/subsystems/child-launch-durability-and-safety.md +28 -9
  152. package/docs/subsystems/delegation.md +16 -3
  153. package/docs/subsystems/docs-freshness-gate.md +14 -7
  154. package/docs/subsystems/fusion.md +36 -11
  155. package/docs/subsystems/host-ui-and-telemetry.md +22 -9
  156. package/docs/tools/bg_delegate.md +3 -1
  157. package/docs/tools/bg_kill.md +3 -1
  158. package/docs/tools/bg_logs.md +3 -1
  159. package/docs/tools/bg_result.md +5 -3
  160. package/docs/tools/bg_run.md +31 -5
  161. package/docs/tools/bg_run_pi_attested.md +3 -1
  162. package/docs/tools/bg_status.md +3 -1
  163. package/docs/tools/fusion_investigate.md +3 -1
  164. package/docs/tools/fusion_reason.md +3 -1
  165. package/docs/tools/fusion_research.md +3 -1
  166. package/docs/tools/fusion_validate.md +3 -1
  167. package/extensions/anthropic-attribution-child.ts +3 -0
  168. package/extensions/anthropic-attribution.ts +166 -1
  169. package/package.json +12 -10
  170. package/src/core/anthropic-attribution-path.ts +5 -4
  171. package/src/core/anthropic-attribution.ts +293 -113
  172. package/src/core/attested-pi-contract.ts +4 -0
  173. package/src/core/attested-pi-run.ts +468 -81
  174. package/src/core/canonical-json.ts +21 -0
  175. package/src/core/common.ts +450 -40
  176. package/src/core/config.ts +121 -0
  177. package/src/core/context/visible-conversation-v2.ts +3 -6
  178. package/src/core/delegate/artifacts.ts +13 -9
  179. package/src/core/delegate/budget.ts +13 -6
  180. package/src/core/delegate/facade-contract.ts +6 -0
  181. package/src/core/delegate/result-package.ts +32 -14
  182. package/src/core/delegate/runner.ts +129 -58
  183. package/src/core/delegate/seed.ts +5 -7
  184. package/src/core/durable-fs.ts +178 -30
  185. package/src/core/extension-api.ts +36 -8
  186. package/src/core/fusion/artifacts.ts +17 -16
  187. package/src/core/fusion/clean-context.ts +8 -5
  188. package/src/core/fusion/config.ts +2 -1
  189. package/src/core/fusion/context.ts +2 -6
  190. package/src/core/fusion/facade-contract.ts +2 -0
  191. package/src/core/fusion/orchestrator.ts +3 -3
  192. package/src/core/fusion/prompts.ts +1 -1
  193. package/src/core/fusion/result-package.ts +259 -72
  194. package/src/core/fusion/source-policy.ts +43 -17
  195. package/src/core/lazy-module.ts +215 -0
  196. package/src/core/pi-launch.ts +390 -65
  197. package/src/core/registry.ts +2010 -381
  198. package/src/core/reload-shell-owner.ts +1662 -0
  199. package/src/core/shell-policy.ts +134 -0
  200. package/src/core/task-durable.ts +67 -0
  201. package/src/delegate-extension.ts +404 -144
  202. package/src/extension.ts +387 -114
  203. package/src/fusion-extension.ts +202 -96
@@ -10,6 +10,8 @@ covers_sources: [src/core/extension-api.ts]
10
10
  # EventBus API v1
11
11
 
12
12
  <!-- pi-docs:begin name="eventbus-contract" generator="scripts/docs/generate.mjs" -->
13
+ Availability: `always`; available by default: **yes**.
14
+
13
15
  | Channel purpose | Channel | Schema |
14
16
  | --- | --- | --- |
15
17
  | Request | `pi-background-tasks:request:v1` | `pi-background-tasks.extension-request.v1` |
@@ -35,6 +37,12 @@ Operations: `capabilities`, `kill`, `logs`, `run`, `status`.
35
37
 
36
38
  Primary source: `src/core/extension-api.ts`. Code is authoritative.
37
39
 
40
+ ## Initialized-host SDK requirement
41
+
42
+ The service accepts requests only after `session_start` supplies its session context. Normal Pi TUI, RPC, print, and JSON modes provide counted lifecycle bindings. An SDK embedder must call `bindExtensions()` with at least one counted UI/command/shutdown/error binding so reload emits `session_start`; an empty or mode-only host must explicitly bind again after every reload.
43
+
44
+ Bare `createAgentSession()` does not initialize this context, and an empty or mode-only binding does not preserve post-bind initialization across `reload()`. Calls before initialization fail as unavailable; consumers must not fabricate context. This public-host limitation remains a `BLOCKED_SCOPE` SDK compatibility blocker. The generated API availability describes the initialized-host contract and is not a pre-bind availability guarantee.
45
+
38
46
  ## Channels and schema ids
39
47
 
40
48
  | Purpose | Channel | `schema_version` |
@@ -59,7 +67,7 @@ Closed object; unknown keys fail.
59
67
  Payloads:
60
68
 
61
69
  - `capabilities`: `{}` only.
62
- - `run`: `{ name, command, isAgent, notifyOnCompletion, triggerOnCompletion, timeoutSeconds? }`; strings are non-empty, booleans are booleans, `timeoutSeconds` is a positive integer when present.
70
+ - `run`: `{ name, command, isAgent, notifyOnCompletion, triggerOnCompletion, timeoutSeconds? }`; strings are non-empty, booleans are booleans, `timeoutSeconds` is a positive integer when present. V1 cannot request reload survival: `surviveReload` remains an unknown-key error and every v1 run uses the compatible default `false`.
63
71
  - `status`: `{ taskId? }`; `taskId` is non-empty when present.
64
72
  - `logs`: `{ taskId, maxBytes?, tail? }`; `maxBytes` is positive when present and is still bounded by runtime log caps.
65
73
  - `kill`: `{ taskId }`.
@@ -90,7 +98,7 @@ Closed by construction through the exported union:
90
98
  }
91
99
  ```
92
100
 
93
- Duplicate `request_id` values are rejected. The service rejects requests before `session_start` and while shutting down. Calling `close()` unsubscribes the request listener, so later requests are not handled and receive no service response.
101
+ Duplicate `request_id` values are rejected. The service rejects requests before `session_start` and while shutting down. The installed service exposes typed state `open | closed`. Calling `close()` is idempotent, transitions it permanently to `closed`, unsubscribes the request listener, and disposes registry publication, so requests first emitted after close are not handled and receive no service response. A request already accepted before close may receive one error response, but never a post-close success. Direct publication after close throws `BackgroundTaskExtensionServiceClosedError` with code `pi_background_tasks_eventbus_closed`; callers must not infer closure from message text.
94
102
 
95
103
  ## Capabilities
96
104
 
@@ -120,17 +128,25 @@ Terminal events are emitted on `pi-background-tasks:terminal:v1`:
120
128
  }
121
129
  ```
122
130
 
123
- The terminal event carries no request id; consumers correlate by `task.id` returned from `run`/`kill`/`status`.
131
+ The terminal event carries no request id; consumers correlate by `task.id` returned from `run`/`kill`/`status`. The additive task snapshot can include `surviveReload` and `reloadSurvival` when a task was launched through `bg_run` or `/bg`; the request side remains unchanged. A fresh activation may emit that survivor's pending terminal on this same v1 channel.
124
132
 
125
133
  ## Ordering and durability barrier
126
134
 
127
135
  For `run` and `kill` requests, the service installs a terminal-publication gate. After the response is emitted, the gate waits one microtask before releasing terminal publication, so immediate-exit tasks cannot publish terminal before the caller has observed the task id.
128
136
 
129
- The registry publishes terminal snapshots only after the output stream has finished/closed and durable terminal metadata has been written. Successful publication is latched and not emitted again. If `EventBus.emit` throws, the failure is loud and the registry retries; because one listener may have received a frame before another listener threw, delivery is **at least once under emission failure**. Consumers must deduplicate by `task.id`.
137
+ The registry publishes terminal snapshots only after the output stream has finished/closed and durable terminal metadata has been written. Publication state is tracked separately as `pending`, `delivered`, or `abandoned`; successful publication is latched and not emitted again, while abandonment is never represented as delivery.
138
+
139
+ If `EventBus.emit` throws, the registry retries after 100 ms for at most three total emit attempts. Persistent failure then becomes `abandoned` with bounded diagnostics. Because one listener may have received a frame before a later listener threw, delivery is **at least once under emission failure** and the same task can be observed up to the attempt bound. Consumers must deduplicate by `task.id`.
140
+
141
+ Shutdown, service disposal, a rejected publication gate, retry exhaustion, retention-limit eviction, or reload-handoff expiry can abandon the terminal frame without changing durable task metadata, waiter completion, or notification truth. Shutdown/service disposal clears pending retry timers and races any gate wait against one-way activation closure. Retention eviction abandons and releases an oldest pending publication before deleting that old task, so a newer notified result remains retrievable and finished-task retention stays bounded. After either gate resolution or rejection, lifecycle is checked again, so a late gate cannot emit or re-arm an old activation. Tasks made terminal by ordinary session shutdown intentionally do not publish onto the disposed activation's EventBus.
142
+
143
+ A valid opted reload handoff removes the survivor before old publication closure, clears only old physical gate/retry handles, and retains its logical state plus cumulative attempt count. Completion in the gap queues for the fresh service. The three-attempt cap and typed closed-service handling do not reset across reload; physical delivery remains at-least-once and consumers still deduplicate by task id.
144
+
145
+ If a synchronous terminal listener calls `close()` while emission is on the stack, queued publication work is disposed but that emission settles only when the emitter returns or throws. A normal return is delivered without an abandonment diagnostic; a non-handoff throw is abandoned once with truthful diagnostics. If the listener synchronously transferred reload ownership before throwing, the old publisher first observes that its exact task/lease binding is gone, leaves publication pending with the attempt consumed, and schedules no old retry; the fresh activation resumes at the next cumulative attempt. Closure never records both outcomes.
130
146
 
131
147
  ## Operations
132
148
 
133
- - `run` starts a background task through the registry and returns a `BgTaskSnapshot`.
149
+ - `run` starts a background task through the registry and returns a `BgTaskSnapshot`. Task admission is one-way closed at shutdown; each accepted admission has cancellation plus an overall bounded preflight deadline, and shutdown drains its owned cleanup before taking the running-task snapshot. A request crossing closure cannot insert/spawn or return success.
134
150
  - `status` returns `{ tasks }`; with `taskId`, the array has one resolved task or errors loudly.
135
151
  - `logs` returns bounded log details plus `text`; full bytes stay in `.pi/tasks/...output`.
136
152
  - `kill` stops a running task and returns `{ task, message }` after the stop path.
@@ -9,6 +9,8 @@ covers_sources: []
9
9
  ---
10
10
  # Choose a workflow
11
11
 
12
+ The complete table is available by default. Operators can remove advanced registrations with strict `PI_BG_FEATURES`; a workflow whose capability is disabled is absent rather than executable under a hidden/stale name. See [Configuration](operations/configuration.md). `bg_result` appears only when delegate or Fusion is enabled.
13
+
12
14
  ## Quick decision tree
13
15
 
14
16
  1. **Is the work short and interactive?** Use ordinary foreground Pi work.
@@ -10,9 +10,9 @@ covers_sources: []
10
10
  # `/bg-clear`
11
11
 
12
12
  <!-- pi-docs:begin name="command-contract-bg-clear" generator="scripts/docs/generate.mjs" -->
13
- | Command | Description | Provenance |
14
- | --- | --- | --- |
15
- | `/bg-clear` | Clear finished background task footer notices | `src/extension.ts:559` |
13
+ | Command | Availability | Default | Description | Provenance |
14
+ | --- | --- | --- | --- | --- |
15
+ | `/bg-clear` | `always` | yes | Clear finished background task footer notices | `src/extension.ts:793` |
16
16
  <!-- pi-docs:end name="command-contract-bg-clear" -->
17
17
 
18
18
  Clear finished background task footer notices.
@@ -10,9 +10,9 @@ covers_sources: []
10
10
  # `/bg-update`
11
11
 
12
12
  <!-- pi-docs:begin name="command-contract-bg-update" generator="scripts/docs/generate.mjs" -->
13
- | Command | Description | Provenance |
14
- | --- | --- | --- |
15
- | `/bg-update` | Show how to update pi-background-tasks to the latest published version | `src/extension.ts:567` |
13
+ | Command | Availability | Default | Description | Provenance |
14
+ | --- | --- | --- | --- | --- |
15
+ | `/bg-update` | `always` | yes | Show how to update pi-background-tasks to the latest published version | `src/extension.ts:801` |
16
16
  <!-- pi-docs:end name="command-contract-bg-update" -->
17
17
 
18
18
  Show update instructions for `pi-background-tasks`.
@@ -10,9 +10,9 @@ covers_sources: []
10
10
  # `/bg`
11
11
 
12
12
  <!-- pi-docs:begin name="command-contract-bg" generator="scripts/docs/generate.mjs" -->
13
- | Command | Description | Provenance |
14
- | --- | --- | --- |
15
- | `/bg` | Start a shell command as a tracked background task: /bg [--agent] [--name "Task name"] <command> | `src/extension.ts:517` |
13
+ | Command | Availability | Default | Description | Provenance |
14
+ | --- | --- | --- | --- | --- |
15
+ | `/bg` | `always` | yes | Start a tracked shell command: /bg [--survive-reload] [--agent] [--name "Task name"] <command> | `src/extension.ts:750` |
16
16
  <!-- pi-docs:end name="command-contract-bg" -->
17
17
 
18
18
  Start a shell command as a tracked background task from the command line.
@@ -20,7 +20,7 @@ Start a shell command as a tracked background task from the command line.
20
20
  ## Synopsis
21
21
 
22
22
 
23
- `/bg [--agent|--llm-agent] [--script|--no-agent] [--name <name>|-n <name>] [--] <command>`
23
+ `/bg [--survive-reload] [--agent|--llm-agent] [--script|--no-agent] [--name <name>|-n <name>] [--] <command>`
24
24
 
25
25
  `--name=<name>` and `-n=<name>` are also accepted. Quoted names are parsed by the extension before the remaining bytes become the shell command.
26
26
 
@@ -34,11 +34,18 @@ Use `/bg` for user-driven long-running commands where you want a footer entry, o
34
34
  - `--script`/`--no-agent`: forces `isAgent:false` after earlier flags.
35
35
  - `notifyOnCompletion`: `true`.
36
36
  - `triggerOnCompletion`: `false` for `/bg`, so completion is display-only by default.
37
+ - `surviveReload`: `false`; one bare leading `--survive-reload` opts an ordinary `isAgent:false` shell execution into supported same-process real reload handoff.
37
38
  - Task name: explicit `--name` if present, otherwise derived from the command.
38
39
 
40
+ ## Shell selection
41
+
42
+ `/bg` uses the same activation-stable policy and agent-visible guidance as `bg_run`. The compatible non-Windows default remains non-empty `SHELL`, otherwise `/bin/sh`, with `-c`; it never silently switches existing users to Bash or loads login-shell startup. Set `PI_BG_POSIX_SHELL=bash` or `sh` before startup or `/reload` for deliberate automation syntax, with optional validated absolute `PI_BG_POSIX_SHELL_PATH`. See [Configuration](../operations/configuration.md) for search and validation details.
43
+
44
+ Inherited Nu, fish, csh, and unknown names are reported as `user-non-posix`, not POSIX/Bash. Task metadata records the exact executable, argument prefix, and dialect used. Windows remains controlled only by its existing `PI_BG_SHELL`/`PI_BG_SHELL_PATH`/`ComSpec` policy.
45
+
39
46
  ## Lifecycle
40
47
 
41
- The command returns after the child process is spawned and reports task id, output path, and command. Terminal statuses are exactly `running`, `completed`, `failed`, or `killed`. A finished footer badge remains visible until that task's detail view is opened or [`/bg-clear`](bg-clear.md) marks unseen finished tasks seen.
48
+ The command returns after the child process is spawned and reports task id, output path, and command. Terminal statuses are exactly `running`, `completed`, `failed`, or `killed`. By default reload kills it. With `--survive-reload`, a real same-process reload claims the same live child/PID/id/nonce/path and keeps output, status, kill, absolute timeout, cumulative output cap, and one completion delivery working. New/resume/fork/clone/quit, extension removal without a claimant, crash, and process restart do not survive. A finished footer badge remains visible until that task's detail view is opened or [`/bg-clear`](bg-clear.md) marks unseen finished tasks seen.
42
49
 
43
50
  ## Examples
44
51
 
@@ -46,6 +53,7 @@ The command returns after the child process is spawned and reports task id, outp
46
53
  /bg --name "Docs build" npm run docs
47
54
  /bg --agent --name "Child Pi" pi -p "summarize this repo"
48
55
  /bg --name="Server" -- npm run dev -- --host 127.0.0.1
56
+ /bg --survive-reload --name "Reload-safe watcher" npm run watch
49
57
  ```
50
58
 
51
59
  ## Output/result
@@ -64,12 +72,16 @@ Completion is delivered as a durable `background-task-notification` custom messa
64
72
 
65
73
  - Empty command: `Background command is empty`.
66
74
  - Missing or unterminated `--name`: `requires a task name`.
75
+ - Duplicate or assignment-form survival flags: `pi_bg_survive_reload_invalid`; `--` makes a later `--survive-reload` literal command text.
76
+ - `--agent`/`--llm-agent` with survival: `pi_bg_survive_reload_requires_non_agent` before files or spawn.
77
+ - A lifecycle without a bound owner (including the empty-binding SDK reload limitation): `pi_bg_reload_owner_unavailable`.
67
78
  - Shell/spawn errors fail the task loudly and write failure metadata.
79
+ - Invalid or unavailable explicit POSIX Bash/sh selection rejects the launch without fallback.
68
80
  - Unknown shell policy on Windows can reject the launch before a task is created.
69
81
 
70
82
  ## Runtime artifacts
71
83
 
72
- Outputs and metadata are written under `.pi/tasks/<session-id>-<pid>/` as `<task-id>.output` and `<task-id>.json`. Model-visible log reads are bounded; the full output path is preserved in notices.
84
+ Outputs and metadata are written under `.pi/tasks/<session-id>-<pid>/` as `<task-id>.output` and `<task-id>.json`. Ordinary-task metadata includes the non-secret resolved shell policy. Model-visible log reads are bounded; the full output path is preserved in notices.
73
85
 
74
86
  ## Safety boundaries
75
87
 
@@ -10,12 +10,12 @@ covers_sources: []
10
10
  # `/claude-cache`
11
11
 
12
12
  <!-- pi-docs:begin name="command-contract-claude-cache" generator="scripts/docs/generate.mjs" -->
13
- | Command | Description | Provenance |
14
- | --- | --- | --- |
15
- | `/claude-cache` | Show or set Claude cache retention for this session (short, long, default) | `src/core/anthropic-attribution.ts:3026` |
13
+ | Command | Availability | Default | Description | Provenance |
14
+ | --- | --- | --- | --- | --- |
15
+ | `/claude-cache` | `feature:attribution` | yes | Show or set Claude cache retention for this session (short, long, default) | `src/core/anthropic-attribution.ts:3206` |
16
16
  <!-- pi-docs:end name="command-contract-claude-cache" -->
17
17
 
18
- Show or change the Anthropic cache-retention preference for the current session.
18
+ Show or change the Anthropic cache-retention preference for the current session. Under the initialized-host contract, the command is registered only when `PI_BG_FEATURES` includes the ambient `attribution` capability (enabled by default); disabling that capability removes the command together with the parent provider/hooks. Its generated default availability is not a pre-bind availability guarantee: bare SDK sessions and empty/mode-only reloads require the explicit binding described in [Anthropic attribution](../subsystems/anthropic-attribution.md#initialized-host-sdk-contract-and-blocker).
19
19
 
20
20
  ## Synopsis
21
21
 
@@ -10,9 +10,9 @@ covers_sources: []
10
10
  # `/fusion-models`
11
11
 
12
12
  <!-- pi-docs:begin name="command-contract-fusion-models" generator="scripts/docs/generate.mjs" -->
13
- | Command | Description | Provenance |
14
- | --- | --- | --- |
15
- | `/fusion-models` | Open the five-slot global fusion model selector. | `src/fusion-extension.ts:1029` |
13
+ | Command | Availability | Default | Description | Provenance |
14
+ | --- | --- | --- | --- | --- |
15
+ | `/fusion-models` | `feature:fusion` | yes | Open the five-slot global fusion model selector. | `src/fusion-extension.ts:1123` |
16
16
  <!-- pi-docs:end name="command-contract-fusion-models" -->
17
17
 
18
18
  Open the global Fusion model selector.
@@ -33,6 +33,45 @@ The selector edits exactly five global slots:
33
33
 
34
34
  Duplicate model selections are allowed. `$current` is the default for every slot and resolves at run time to Pi's current model; it is available only when a current model exists and is available to child Pi. Slash-containing model ids are stored as `provider/model-id` strings.
35
35
 
36
+ ## What each role does
37
+
38
+ Fusion follows one fixed pipeline: preflight, three candidates in parallel, an evaluator, an optional evaluator repair, then a merger.
39
+
40
+ ### Candidate 1, Candidate 2, and Candidate 3
41
+
42
+ The candidates are three independent attempts over the same canonical input, with the same workflow instruction and fixed workflow tools. They are not assigned specialties: Candidate 1 is not inherently a researcher, Candidate 2 a critic, or Candidate 3 a writer. A tool-enabled workflow lets every candidate use that workflow's same allowlist; `reason` gives all three no tools.
43
+
44
+ The candidate wave must finish before evaluation, so the slowest candidate controls successful wave latency. Choosing different capable routes can improve answer diversity, but route diversity does not guarantee different reasoning or coverage. Repeating one route is valid and can still produce useful independent attempts, but the answers may be similar—especially when that route behaves deterministically.
45
+
46
+ ### Evaluator
47
+
48
+ The evaluator starts after all candidates complete. It receives the original canonical input and the three anonymous answers labeled A, B, and C, with no provider, model, slot, or completion-order identity. It has no tools. It compares strengths, limitations, agreements, conflicts, risks, and useful contributions, then emits closed JSON containing a constrained `synthesis_plan`; it does not select a winner or write the user-facing answer.
49
+
50
+ If that JSON is invalid or schema-invalid, Fusion makes exactly one repair attempt using the same evaluator slot and resolved model. The repair receives the original blind input, the invalid output, and bounded validation errors. A second invalid result fails the run; Fusion does not switch models or add another repair.
51
+
52
+ ### Merger
53
+
54
+ The merger starts only after a valid evaluation. It receives the original canonical input, all three candidate outputs, and the validated evaluation. It has no tools and the merge stage produces the sole final user-facing answer; candidates and evaluator output are never returned as the answer. For `fusion_validate`, the host renders the final report from validated finding accounting after the no-tool merger rather than forwarding unverified merger prose.
55
+
56
+ This is the final sequential stage and normally has the greatest synthesis burden: it must reconcile all upstream material rather than retrieve new facts.
57
+
58
+ ## Choosing models
59
+
60
+ There is no universally best assignment. Use only routes currently listed as available by the selector, and account for both capability and context capacity.
61
+
62
+ | Goal | Candidates | Evaluator | Merger |
63
+ | --- | --- | --- | --- |
64
+ | **Quality-first** | Choose three strong task-capable routes; use different routes when their genuine differences are useful, while remembering that diversity is not guaranteed. | Prefer strong comparison and schema reliability. It must absorb the input plus all three answers, and one failed schema attempt adds repair latency. | Prefer the strongest available synthesis route with enough context for every upstream answer and the evaluation. |
65
+ | **Speed-first** | Choose fast routes for all three; one slow outlier delays the whole parallel wave. Repeating one fast route is allowed. | Choose a fast, schema-reliable route so the sequential stage is short and the optional repair is unlikely. | Choose a capable fast synthesizer; its latency is added after the complete candidate and evaluator stages. |
66
+
67
+ Context capacity can outweigh nominal model strength. Each candidate sees the canonical input; the evaluator fans in that input plus three bounded candidate outputs; the merger additionally receives the validated evaluation. Conditional repair has the widest evaluator input because it also includes the invalid evaluation and validation errors. Fusion measures each rendered stage against that slot's route before launch, so a stronger small-context route can still be unusable. See [Fusion runtime limits](../operations/configuration.md#fusion-runtime-limits) and [budget behavior](../subsystems/fusion.md#budgets-and-output-contracts).
68
+
69
+ For frontier models, every role must remain on Pi's Anthropic or Codex subscription OAuth channel. Here “expensive” can mean latency, subscription quota, and token/context use; it is not permission to move a role to a metered API route.
70
+
71
+ ## Slots are not calls
72
+
73
+ Five role slots do not promise five distinct models or five provider calls. `$current` and explicit selections may be duplicated across any slots. An ordinary success without repair uses five child invocations: three candidates, one evaluator, and one merger. A successful evaluator repair makes six child invocations. A preflight rejection creates zero children. Failed or cancelled runs can stop with fewer completed children, transient pre-creation spawn failures can change launch attempts, and each child Pi process can make multiple provider requests within its runtime limits.
74
+
36
75
  ## UI behavior
37
76
 
38
77
  The selector starts from the loaded config or the default all-`$current` config. It lists:
@@ -45,7 +84,11 @@ In the five-slot view, arrow keys move, Enter opens the model list, `r` resets t
45
84
 
46
85
  ## Persistence and conflicts
47
86
 
48
- The config file is `fusion-models.json` in Pi's agent directory (`fusionModelConfigPath()`). Its schema is closed:
87
+ The config file is `fusion-models.json` in Pi's agent directory (`fusionModelConfigPath()`). Its schema is closed.
88
+
89
+ ### Valid repeated-route example
90
+
91
+ This is both a valid configuration and the missing-file default. It deliberately demonstrates `$current` and duplicates; at run time the current route must still be available and pass admission for child Pi.
49
92
 
50
93
  ```json
51
94
  {
@@ -56,6 +99,8 @@ The config file is `fusion-models.json` in Pi's agent directory (`fusionModelCon
56
99
  }
57
100
  ```
58
101
 
102
+ For explicit selections, save only exact `provider/model-id` keys shown as available by `/fusion-models`; no model aliases are implied by the quality-first or speed-first recipes above. Reusing the same exact key in multiple slots is valid.
103
+
59
104
  Loads reject invalid JSON, unknown keys, wrong schema version, blank selections, surrounding whitespace, unqualified configured selections, and candidate arrays that do not contain exactly three entries.
60
105
 
61
106
  Saves are durable and revision-safe: the parent captures the file revision hash on load, takes a lock next to the config, verifies the on-disk revision still matches, then atomically replaces the file. If another process changes the file first, the save fails with a config-conflict error shown inside the selector; it does not overwrite concurrent work. Lock acquisition times out loudly after 10 seconds.
@@ -10,9 +10,9 @@ covers_sources: []
10
10
  # `/fusion`
11
11
 
12
12
  <!-- pi-docs:begin name="command-contract-fusion" generator="scripts/docs/generate.mjs" -->
13
- | Command | Description | Provenance |
14
- | --- | --- | --- |
15
- | `/fusion` | Start fixed-purpose Fusion reason in the background and return immediately. | `src/fusion-extension.ts:996` |
13
+ | Command | Availability | Default | Description | Provenance |
14
+ | --- | --- | --- | --- | --- |
15
+ | `/fusion` | `feature:fusion` | yes | Start fixed-purpose Fusion reason in the background and return immediately. | `src/fusion-extension.ts:1083` |
16
16
  <!-- pi-docs:end name="command-contract-fusion" -->
17
17
 
18
18
  Run the fixed-purpose Fusion **reason** workflow from the command line.
@@ -10,9 +10,9 @@ covers_sources: []
10
10
  # `/jobs`
11
11
 
12
12
  <!-- pi-docs:begin name="command-contract-jobs" generator="scripts/docs/generate.mjs" -->
13
- | Command | Description | Provenance |
14
- | --- | --- | --- |
15
- | `/jobs` | List running and recent background tasks | `src/extension.ts:605` |
13
+ | Command | Availability | Default | Description | Provenance |
14
+ | --- | --- | --- | --- | --- |
15
+ | `/jobs` | `always` | yes | List running and recent background tasks | `src/extension.ts:850` |
16
16
  <!-- pi-docs:end name="command-contract-jobs" -->
17
17
 
18
18
  List running and recent background tasks.
@@ -10,9 +10,9 @@ covers_sources: []
10
10
  # `/kill`
11
11
 
12
12
  <!-- pi-docs:begin name="command-contract-kill" generator="scripts/docs/generate.mjs" -->
13
- | Command | Description | Provenance |
14
- | --- | --- | --- |
15
- | `/kill` | Stop a running background task: /kill <id> | `src/extension.ts:649` |
13
+ | Command | Availability | Default | Description | Provenance |
14
+ | --- | --- | --- | --- | --- |
15
+ | `/kill` | `always` | yes | Stop a running background task: /kill <id> | `src/extension.ts:894` |
16
16
  <!-- pi-docs:end name="command-contract-kill" -->
17
17
 
18
18
  Stop a running background task.
@@ -10,9 +10,9 @@ covers_sources: []
10
10
  # `/logs`
11
11
 
12
12
  <!-- pi-docs:begin name="command-contract-logs" generator="scripts/docs/generate.mjs" -->
13
- | Command | Description | Provenance |
14
- | --- | --- | --- |
15
- | `/logs` | Show bounded output from a background task: /logs <id> [maxBytes] | `src/extension.ts:618` |
13
+ | Command | Availability | Default | Description | Provenance |
14
+ | --- | --- | --- | --- | --- |
15
+ | `/logs` | `always` | yes | Show bounded output from a background task: /logs <id> [maxBytes] | `src/extension.ts:863` |
16
16
  <!-- pi-docs:end name="command-contract-logs" -->
17
17
 
18
18
  Show bounded output from a background task.
@@ -10,10 +10,10 @@ covers_sources: []
10
10
  # `/tasks` and `/bg-tasks`
11
11
 
12
12
  <!-- pi-docs:begin name="command-contract-tasks-bg-tasks" generator="scripts/docs/generate.mjs" -->
13
- | Command | Description | Provenance |
14
- | --- | --- | --- |
15
- | `/tasks` | Open the Claude-like background task manager UI | `src/extension.ts:543` |
16
- | `/bg-tasks` | Open the background task manager UI | `src/extension.ts:551` |
13
+ | Command | Availability | Default | Description | Provenance |
14
+ | --- | --- | --- | --- | --- |
15
+ | `/tasks` | `always` | yes | Open the Claude-like background task manager UI | `src/extension.ts:777` |
16
+ | `/bg-tasks` | `always` | yes | Open the background task manager UI | `src/extension.ts:785` |
17
17
  <!-- pi-docs:end name="command-contract-tasks-bg-tasks" -->
18
18
 
19
19
  Open the interactive background task manager. `/tasks` and `/bg-tasks` are aliases.
@@ -37,7 +37,7 @@ Use the task manager when you want the host UI: select tasks, inspect a live out
37
37
 
38
38
  ## Lifecycle
39
39
 
40
- The manager is an overlay dock. Opening it sets the footer hint to `focused` and temporarily hides the `/bg-clear` hint; closing returns the footer to the normal `Shift↓` hint. Opening a finished task's detail view marks that task seen. Merely opening the list or closing the dock does **not** clear other finished badges; use [`/bg-clear`](bg-clear.md) to clear them together.
40
+ The manager is an overlay dock. Opening it sets the footer hint to `focused` and temporarily hides the `/bg-clear` hint; closing returns the footer to the configured `Shift↓`, `CtrlAltB`, or `/tasks` hint. `PI_BG_DOCK_SHORTCUT=off` disables only the key registration—both commands on this page continue to work. Opening a finished task's detail view marks that task seen. Merely opening the list or closing the dock does **not** clear other finished badges; use [`/bg-clear`](bg-clear.md) to clear them together.
41
41
 
42
42
  List view sorts tasks as running, failed, killed, then completed; within a status, newest terminal/start time appears first. Status labels shown in the UI are `running`, `error` for `failed`, `stopped` for `killed`, and `done` for `completed`.
43
43
 
@@ -26,7 +26,7 @@ Background tasks can finish silently, notify the terminal, or notify and wake th
26
26
  - `bg_run` defaults to durable notification plus follow-up turn: `notifyOnCompletion:true` and `triggerOnCompletion:true`.
27
27
  - `bg_status` and `bg_logs` are point-in-time inspection tools, not polling primitives.
28
28
  - Tool-launched Fusion tasks default to notification plus follow-up wake and are retrieved once with `bg_result`; `/fusion` uses notification-only.
29
- - A received `<background-task-notification>` is metadata-backed terminal-status truth. The output stream has finished/closed, but ordinary `.output` bytes are not explicitly fsynced. Do not call `bg_status` only to reconfirm status; call `bg_logs` only if output bytes are needed.
29
+ - A received `<background-task-notification>` is metadata-backed terminal-status truth. The output stream has finished/closed; after a requested POSIX tree stop, the originally owned process group has also been observed gone, while a force/proof failure is delivered as `failed` rather than a successful kill. Ordinary `.output` bytes are not explicitly fsynced. Do not call `bg_status` only to reconfirm status; call `bg_logs` only if output bytes are needed.
30
30
 
31
31
  ## Notification payload
32
32
 
@@ -51,7 +51,11 @@ If either completion flag was intentionally disabled, manual inspection is allow
51
51
 
52
52
  ## Failures and suppression
53
53
 
54
- If notification send fails, the task resets `notified:false` and logs the error; it does not silently pretend delivery happened. During Pi session shutdown/reload, notifications are suppressed while running tasks are killed.
54
+ Completion notification receipt and terminal EventBus publication are independent facts. EventBus publication can be pending, delivered, or abandoned without changing durable task status or `notified`. At the retention boundary, an oldest pending publication is abandoned and disposed before that old task is pruned; it cannot evict a newer notified Fusion result before `bg_result` retrieval. If the EventBus service alone is disposed, an otherwise enabled notification may still be sent. During ordinary Pi shutdown, notifications are suppressed and old publication is abandoned.
55
+
56
+ A live opted ordinary reload survivor is different: detach transfers its logical publication ledger and notification latch without sending through the old host. Completion during the gap queues. The fresh activation resumes at the same cumulative EventBus attempt count and sends at most one successful host notification; a reload never resets `notified`. Physical EventBus delivery remains at-least-once under listener failure and consumers still deduplicate by task id.
57
+
58
+ If notification send fails, the task resets `notified:false` and logs the error; it does not silently pretend delivery happened. Likewise, EventBus abandonment—including `reload_handoff_expired` when no fresh activation claims an owner—never becomes successful publication or rewrites the real terminal status.
55
59
 
56
60
  ## Related docs
57
61
 
@@ -36,6 +36,12 @@ pi install .
36
36
  pi install . -l
37
37
  ```
38
38
 
39
+ ## SDK embedding requirement
40
+
41
+ **Initialized-host contract:** package resources that require post-bind initialization are available in normal Pi TUI, RPC, print, and JSON modes. An SDK embedder must call `bindExtensions()` with at least one counted binding for lifecycle restoration—such as `onError`, UI/command actions, or shutdown handling—so `session_start` is emitted again by `reload()`. If the embedder uses an empty or mode-only binding, it must explicitly call `bindExtensions()` again after every reload.
42
+
43
+ Bare `createAgentSession()` does not emit `session_start`; `{}` or `{ mode: "print" }` binds once but does not make a later reload emit it. Post-bind resources such as ambient Anthropic attribution, `/claude-cache`, and session-context EventBus readiness are therefore unavailable on those bare/reloaded paths until an explicit bind. This is a current public Pi SDK blocker, not supported package behavior. The generated availability tables describe this initialized-host contract; they are not a pre-bind availability guarantee.
44
+
39
45
  ## 2. Start your first `/bg` task
40
46
 
41
47
  Inside a project, run:
@@ -55,7 +61,7 @@ Use the footer dock or commands:
55
61
  /logs <task id> 20000
56
62
  ```
57
63
 
58
- Press **Shift↓** to open the dock when the `bg ...` footer appears. `/bg-clear` acknowledges finished-task footer notices.
64
+ Press the configured dock key (**Shift↓** by default, or **Ctrl+Alt+B**) when the `bg ...` footer appears. With `PI_BG_DOCK_SHORTCUT=off`, use `/tasks` or `/bg-tasks`. `/bg-clear` acknowledges finished-task footer notices.
59
65
 
60
66
  ## 4. Start an agent-launched background task
61
67