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.
- package/BACKGROUND-TASKS-INSTRUCTIONS.md +1 -1
- package/README.md +70 -25
- package/TESTING.md +42 -2
- package/TEST_PLAN.md +7 -4
- package/dist/extensions/anthropic-attribution-child.js +4 -0
- package/dist/extensions/anthropic-attribution-child.js.map +1 -0
- package/dist/extensions/anthropic-attribution.js +107 -0
- package/dist/extensions/anthropic-attribution.js.map +1 -0
- package/dist/extensions/background-tasks.js +2 -0
- package/dist/extensions/background-tasks.js.map +1 -0
- package/dist/extensions/delegate-child.js +2 -0
- package/dist/extensions/delegate-child.js.map +1 -0
- package/dist/extensions/fusion-child.js +2 -0
- package/dist/extensions/fusion-child.js.map +1 -0
- package/dist/package.json +5 -0
- package/dist/src/core/anthropic-attribution-path.js +24 -0
- package/dist/src/core/anthropic-attribution-path.js.map +1 -0
- package/dist/src/core/anthropic-attribution.js +2247 -0
- package/dist/src/core/anthropic-attribution.js.map +1 -0
- package/dist/src/core/attested-pi-contract.js +5 -0
- package/dist/src/core/attested-pi-contract.js.map +1 -0
- package/dist/src/core/attested-pi-run.js +749 -0
- package/dist/src/core/attested-pi-run.js.map +1 -0
- package/dist/src/core/canonical-json.js +19 -0
- package/dist/src/core/canonical-json.js.map +1 -0
- package/dist/src/core/common.js +787 -0
- package/dist/src/core/common.js.map +1 -0
- package/dist/src/core/config.js +78 -0
- package/dist/src/core/config.js.map +1 -0
- package/dist/src/core/context/parent-snapshot.js +75 -0
- package/dist/src/core/context/parent-snapshot.js.map +1 -0
- package/dist/src/core/context/token-budget.js +631 -0
- package/dist/src/core/context/token-budget.js.map +1 -0
- package/dist/src/core/context/visible-conversation-v2.js +390 -0
- package/dist/src/core/context/visible-conversation-v2.js.map +1 -0
- package/dist/src/core/delegate/artifacts.js +357 -0
- package/dist/src/core/delegate/artifacts.js.map +1 -0
- package/dist/src/core/delegate/budget.js +266 -0
- package/dist/src/core/delegate/budget.js.map +1 -0
- package/dist/src/core/delegate/facade-contract.js +7 -0
- package/dist/src/core/delegate/facade-contract.js.map +1 -0
- package/dist/src/core/delegate/hook-contract-evidence.json +18 -0
- package/dist/src/core/delegate/hook-contract.js +124 -0
- package/dist/src/core/delegate/hook-contract.js.map +1 -0
- package/dist/src/core/delegate/launch.js +366 -0
- package/dist/src/core/delegate/launch.js.map +1 -0
- package/dist/src/core/delegate/result-package.js +293 -0
- package/dist/src/core/delegate/result-package.js.map +1 -0
- package/dist/src/core/delegate/runner.js +398 -0
- package/dist/src/core/delegate/runner.js.map +1 -0
- package/dist/src/core/delegate/seed.js +334 -0
- package/dist/src/core/delegate/seed.js.map +1 -0
- package/dist/src/core/delegate/types.js +103 -0
- package/dist/src/core/delegate/types.js.map +1 -0
- package/dist/src/core/durable-fs.js +372 -0
- package/dist/src/core/durable-fs.js.map +1 -0
- package/dist/src/core/extension-api.js +382 -0
- package/dist/src/core/extension-api.js.map +1 -0
- package/dist/src/core/fusion/artifacts.js +654 -0
- package/dist/src/core/fusion/artifacts.js.map +1 -0
- package/dist/src/core/fusion/budget.js +949 -0
- package/dist/src/core/fusion/budget.js.map +1 -0
- package/dist/src/core/fusion/child-protocol.js +176 -0
- package/dist/src/core/fusion/child-protocol.js.map +1 -0
- package/dist/src/core/fusion/claude-cache.js +156 -0
- package/dist/src/core/fusion/claude-cache.js.map +1 -0
- package/dist/src/core/fusion/clean-context.js +64 -0
- package/dist/src/core/fusion/clean-context.js.map +1 -0
- package/dist/src/core/fusion/config.js +349 -0
- package/dist/src/core/fusion/config.js.map +1 -0
- package/dist/src/core/fusion/context.js +184 -0
- package/dist/src/core/fusion/context.js.map +1 -0
- package/dist/src/core/fusion/evaluation.js +696 -0
- package/dist/src/core/fusion/evaluation.js.map +1 -0
- package/dist/src/core/fusion/facade-contract.js +3 -0
- package/dist/src/core/fusion/facade-contract.js.map +1 -0
- package/dist/src/core/fusion/orchestrator.js +867 -0
- package/dist/src/core/fusion/orchestrator.js.map +1 -0
- package/dist/src/core/fusion/output-contract.js +27 -0
- package/dist/src/core/fusion/output-contract.js.map +1 -0
- package/dist/src/core/fusion/pi-child.js +1741 -0
- package/dist/src/core/fusion/pi-child.js.map +1 -0
- package/dist/src/core/fusion/prompts.js +260 -0
- package/dist/src/core/fusion/prompts.js.map +1 -0
- package/dist/src/core/fusion/result-package.js +857 -0
- package/dist/src/core/fusion/result-package.js.map +1 -0
- package/dist/src/core/fusion/source-policy.js +259 -0
- package/dist/src/core/fusion/source-policy.js.map +1 -0
- package/dist/src/core/fusion/types.js +207 -0
- package/dist/src/core/fusion/types.js.map +1 -0
- package/dist/src/core/fusion/web-fetch.js +777 -0
- package/dist/src/core/fusion/web-fetch.js.map +1 -0
- package/dist/src/core/fusion/workflows.js +126 -0
- package/dist/src/core/fusion/workflows.js.map +1 -0
- package/dist/src/core/lazy-module.js +181 -0
- package/dist/src/core/lazy-module.js.map +1 -0
- package/dist/src/core/pi-launch.js +431 -0
- package/dist/src/core/pi-launch.js.map +1 -0
- package/dist/src/core/registry.js +3331 -0
- package/dist/src/core/registry.js.map +1 -0
- package/dist/src/core/reload-shell-owner.js +1396 -0
- package/dist/src/core/reload-shell-owner.js.map +1 -0
- package/dist/src/core/shell-policy.js +80 -0
- package/dist/src/core/shell-policy.js.map +1 -0
- package/dist/src/core/task-durable.js +51 -0
- package/dist/src/core/task-durable.js.map +1 -0
- package/dist/src/core/update-check.js +92 -0
- package/dist/src/core/update-check.js.map +1 -0
- package/dist/src/core/windows-taskkill.js +185 -0
- package/dist/src/core/windows-taskkill.js.map +1 -0
- package/dist/src/delegate-child-extension.js +776 -0
- package/dist/src/delegate-child-extension.js.map +1 -0
- package/dist/src/delegate-extension.js +746 -0
- package/dist/src/delegate-extension.js.map +1 -0
- package/dist/src/extension.js +953 -0
- package/dist/src/extension.js.map +1 -0
- package/dist/src/fusion-child-extension.js +760 -0
- package/dist/src/fusion-child-extension.js.map +1 -0
- package/dist/src/fusion-extension.js +1030 -0
- package/dist/src/fusion-extension.js.map +1 -0
- package/dist/src/ui/background-tasks-manager.js +689 -0
- package/dist/src/ui/background-tasks-manager.js.map +1 -0
- package/dist/src/ui/fusion-model-selector.js +277 -0
- package/dist/src/ui/fusion-model-selector.js.map +1 -0
- package/docs/INDEX.md +35 -33
- package/docs/api/eventbus-v1.md +21 -5
- package/docs/choose-a-workflow.md +2 -0
- package/docs/commands/bg-clear.md +3 -3
- package/docs/commands/bg-update.md +3 -3
- package/docs/commands/bg.md +18 -6
- package/docs/commands/claude-cache.md +4 -4
- package/docs/commands/fusion-models.md +49 -4
- package/docs/commands/fusion.md +3 -3
- package/docs/commands/jobs.md +3 -3
- package/docs/commands/kill.md +3 -3
- package/docs/commands/logs.md +3 -3
- package/docs/commands/task-manager.md +5 -5
- package/docs/concepts/completion-delivery.md +6 -2
- package/docs/getting-started.md +7 -1
- package/docs/manifest.json +228 -42
- package/docs/operations/configuration.md +58 -5
- package/docs/operations/releasing.md +4 -2
- package/docs/operations/testing.md +22 -0
- package/docs/operations/troubleshooting.md +6 -2
- package/docs/read-before-edit.md +44 -31
- package/docs/reference/runtime-contracts.md +87 -51
- package/docs/reference/shortcuts-and-dock.md +28 -14
- package/docs/subsystems/anthropic-attribution.md +23 -11
- package/docs/subsystems/attested-pi-runs.md +12 -7
- package/docs/subsystems/background-task-runtime.md +53 -11
- package/docs/subsystems/child-launch-durability-and-safety.md +28 -9
- package/docs/subsystems/delegation.md +16 -3
- package/docs/subsystems/docs-freshness-gate.md +14 -7
- package/docs/subsystems/fusion.md +36 -11
- package/docs/subsystems/host-ui-and-telemetry.md +22 -9
- package/docs/tools/bg_delegate.md +3 -1
- package/docs/tools/bg_kill.md +3 -1
- package/docs/tools/bg_logs.md +3 -1
- package/docs/tools/bg_result.md +5 -3
- package/docs/tools/bg_run.md +31 -5
- package/docs/tools/bg_run_pi_attested.md +3 -1
- package/docs/tools/bg_status.md +3 -1
- package/docs/tools/fusion_investigate.md +3 -1
- package/docs/tools/fusion_reason.md +3 -1
- package/docs/tools/fusion_research.md +3 -1
- package/docs/tools/fusion_validate.md +3 -1
- package/extensions/anthropic-attribution-child.ts +3 -0
- package/extensions/anthropic-attribution.ts +166 -1
- package/package.json +12 -10
- package/src/core/anthropic-attribution-path.ts +5 -4
- package/src/core/anthropic-attribution.ts +293 -113
- package/src/core/attested-pi-contract.ts +4 -0
- package/src/core/attested-pi-run.ts +468 -81
- package/src/core/canonical-json.ts +21 -0
- package/src/core/common.ts +450 -40
- package/src/core/config.ts +121 -0
- package/src/core/context/visible-conversation-v2.ts +3 -6
- package/src/core/delegate/artifacts.ts +13 -9
- package/src/core/delegate/budget.ts +13 -6
- package/src/core/delegate/facade-contract.ts +6 -0
- package/src/core/delegate/result-package.ts +32 -14
- package/src/core/delegate/runner.ts +129 -58
- package/src/core/delegate/seed.ts +5 -7
- package/src/core/durable-fs.ts +178 -30
- package/src/core/extension-api.ts +36 -8
- package/src/core/fusion/artifacts.ts +17 -16
- package/src/core/fusion/clean-context.ts +8 -5
- package/src/core/fusion/config.ts +2 -1
- package/src/core/fusion/context.ts +2 -6
- package/src/core/fusion/facade-contract.ts +2 -0
- package/src/core/fusion/orchestrator.ts +3 -3
- package/src/core/fusion/prompts.ts +1 -1
- package/src/core/fusion/result-package.ts +259 -72
- package/src/core/fusion/source-policy.ts +43 -17
- package/src/core/lazy-module.ts +215 -0
- package/src/core/pi-launch.ts +390 -65
- package/src/core/registry.ts +2010 -381
- package/src/core/reload-shell-owner.ts +1662 -0
- package/src/core/shell-policy.ts +134 -0
- package/src/core/task-durable.ts +67 -0
- package/src/delegate-extension.ts +404 -144
- package/src/extension.ts +387 -114
- package/src/fusion-extension.ts +202 -96
|
@@ -5,25 +5,25 @@ mode: mixed
|
|
|
5
5
|
review_policy: behavioral
|
|
6
6
|
stability: stable
|
|
7
7
|
covers_surfaces: [renderer:fusion-result, workflow:investigate, workflow:reason, workflow:research, workflow:validate]
|
|
8
|
-
covers_sources: [extensions/fusion-child.ts, src/core/fusion/artifacts.ts, src/core/fusion/budget.ts, src/core/fusion/child-protocol.ts, src/core/fusion/claude-cache.ts, src/core/fusion/clean-context.ts, src/core/fusion/config.ts, src/core/fusion/context.ts, src/core/fusion/evaluation.ts, src/core/fusion/orchestrator.ts, src/core/fusion/output-contract.ts, src/core/fusion/pi-child.ts, src/core/fusion/prompts.ts, src/core/fusion/result-package.ts, src/core/fusion/source-policy.ts, src/core/fusion/types.ts, src/core/fusion/web-fetch.ts, src/core/fusion/workflows.ts, src/fusion-child-extension.ts, src/fusion-extension.ts, src/ui/fusion-model-selector.ts]
|
|
8
|
+
covers_sources: [extensions/fusion-child.ts, src/core/fusion/artifacts.ts, src/core/fusion/budget.ts, src/core/fusion/child-protocol.ts, src/core/fusion/claude-cache.ts, src/core/fusion/clean-context.ts, src/core/fusion/config.ts, src/core/fusion/context.ts, src/core/fusion/evaluation.ts, src/core/fusion/facade-contract.ts, src/core/fusion/orchestrator.ts, src/core/fusion/output-contract.ts, src/core/fusion/pi-child.ts, src/core/fusion/prompts.ts, src/core/fusion/result-package.ts, src/core/fusion/source-policy.ts, src/core/fusion/types.ts, src/core/fusion/web-fetch.ts, src/core/fusion/workflows.ts, src/fusion-child-extension.ts, src/fusion-extension.ts, src/ui/fusion-model-selector.ts]
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
# Fusion subsystem
|
|
12
12
|
|
|
13
13
|
<!-- pi-docs:begin name="fusion-workflows" generator="scripts/docs/generate.mjs" -->
|
|
14
|
-
| Workflow | Tool | Context | Candidate capability | Candidate tools | Evaluator/merger tools | Provenance |
|
|
15
|
-
| --- | --- | --- | --- | --- | --- | --- |
|
|
16
|
-
| `investigate` | `fusion_investigate` | `clean_task` | `inspect` | `read`, `grep`, `find`, `ls` | none | `src/core/fusion/workflows.ts:80` |
|
|
17
|
-
| `reason` | `fusion_reason` | `session_projection` | `reason` | none | none | `src/core/fusion/workflows.ts:61` |
|
|
18
|
-
| `research` | `fusion_research` | `clean_task` | `research` | `read`, `grep`, `find`, `ls`, `fusion_web_fetch` | none | `src/core/fusion/workflows.ts:99` |
|
|
19
|
-
| `validate` | `fusion_validate` | `clean_task` | `inspect` | `read`, `grep`, `find`, `ls` | none | `src/core/fusion/workflows.ts:118` |
|
|
14
|
+
| Workflow | Availability | Default | Tool | Context | Candidate capability | Candidate tools | Evaluator/merger tools | Provenance |
|
|
15
|
+
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
|
16
|
+
| `investigate` | `feature:fusion` | yes | `fusion_investigate` | `clean_task` | `inspect` | `read`, `grep`, `find`, `ls` | none | `src/core/fusion/workflows.ts:80` |
|
|
17
|
+
| `reason` | `feature:fusion` | yes | `fusion_reason` | `session_projection` | `reason` | none | none | `src/core/fusion/workflows.ts:61` |
|
|
18
|
+
| `research` | `feature:fusion` | yes | `fusion_research` | `clean_task` | `research` | `read`, `grep`, `find`, `ls`, `fusion_web_fetch` | none | `src/core/fusion/workflows.ts:99` |
|
|
19
|
+
| `validate` | `feature:fusion` | yes | `fusion_validate` | `clean_task` | `inspect` | `read`, `grep`, `find`, `ls` | none | `src/core/fusion/workflows.ts:118` |
|
|
20
20
|
<!-- pi-docs:end name="fusion-workflows" -->
|
|
21
21
|
|
|
22
22
|
This document is the primary behavioral owner for Fusion's package-owned source files listed in frontmatter. Shared parent-context and token-budget modules are referenced here only as dependencies; their behavior is not owned by this document.
|
|
23
23
|
|
|
24
24
|
## Public v1 surface
|
|
25
25
|
|
|
26
|
-
Fusion v1 exposes exactly two commands and exactly four public tools:
|
|
26
|
+
When `PI_BG_FEATURES` includes `fusion`, Fusion v1 exposes exactly two commands and exactly four public tools; all are absent otherwise:
|
|
27
27
|
|
|
28
28
|
- `/fusion` — command shorthand for fixed-purpose `reason`.
|
|
29
29
|
- `/fusion-models` — TUI-only global five-slot model selector.
|
|
@@ -34,6 +34,10 @@ Fusion v1 exposes exactly two commands and exactly four public tools:
|
|
|
34
34
|
|
|
35
35
|
Every public tool schema is closed and has no public capability/mode switch. The retired `fusion_brainstorm` surface is never registered; session start removes it from active tools while preserving rendering of historical completed v4 result messages.
|
|
36
36
|
|
|
37
|
+
Fusion's facade registers those schemas, commands, fixed workflow metadata, and the `fusion-result` renderer immediately. Context projection, clean-context construction, model configuration/resolution, and the orchestrator are loaded as one activation-local single-flight module on the first workflow invocation. `/fusion-models` has a separate config/selector loader, so opening the selector does not preload the orchestrator, and cancelling an empty `/fusion` editor does not load either lane. Concurrent cold calls share module loading only; every workflow keeps its own controller, readiness gate, managed task, and orchestrator run.
|
|
38
|
+
|
|
39
|
+
A deferred import failure is bounded, names the failed lane, and is retained for that activation. Fusion joins the package-wide synchronous close fence, so both lanes close, lifecycle generation advances, and every active controller aborts in the same first shutdown handler that closes delegate/result/core lanes, before active-run cleanup can pause sequential Pi dispatch. Retained tools reject before loader or host effects. Retained `/fusion` and `/fusion-models` commands assert activation before editor, idle wait, mode/UI handling, selector loading, or notification; once closure is detected their error paths do not notify the old context. A real reload creates fresh loaders rather than reviving the old instance. The shared state-machine and conditional compiled-startup details are documented in [Delegation](delegation.md#lazy-facade-activation).
|
|
40
|
+
|
|
37
41
|
## Commands
|
|
38
42
|
|
|
39
43
|
`/fusion <prompt>` trims the command text and starts the reason workflow as a managed background task. `/fusion` with no arguments opens the multiline editor when UI is available; editor cancellation or blank edited text returns without child spawn. Durable preflight and task registration finish before the command returns; no loader remains open and no premature result message is appended. Terminal state uses the standard background notification, and `bg_result` verifies and retrieves the committed result.
|
|
@@ -59,7 +63,7 @@ All workflows use the same orchestrator shape:
|
|
|
59
63
|
7. run a no-tool merger;
|
|
60
64
|
8. durably commit `merged.md` plus manifest-bound `result.json`, then publish terminal task state.
|
|
61
65
|
|
|
62
|
-
|
|
66
|
+
The five slots are role assignments, not a guarantee of five distinct models or of five provider calls. An ordinary success uses five child invocations (three candidates, one evaluator, one merger); the one permitted evaluator repair makes six. Fatal preflight launches zero children. Failed or cancelled waves may create or complete fewer children, a transient pre-creation spawn failure may be retried once, and a child agent loop may make multiple provider requests. Attempt and provider-request counts therefore remain runtime facts, not values inferred from the slot count.
|
|
63
67
|
|
|
64
68
|
Candidate tool policies are fixed by workflow:
|
|
65
69
|
|
|
@@ -72,6 +76,27 @@ Candidate tool policies are fixed by workflow:
|
|
|
72
76
|
|
|
73
77
|
Evaluator, evaluator-repair, and merger always use capability `reason` and empty tool lists. Tool-enabled children run with built-in tools disabled and an explicit allowlist plus a denylist that includes shell/write/edit, Fusion recursion, and background/delegate tools.
|
|
74
78
|
|
|
79
|
+
### Model roles, fan-in, and latency
|
|
80
|
+
|
|
81
|
+
Candidate 1/2/3 are independent child attempts, not assigned specialties. The orchestrator gives all three the same canonical input, candidate system instruction, workflow capability, and fixed candidate-tool policy, then starts their promises as one parallel wave. Tool-enabled candidates can make different investigative choices, but no slot is pre-designated as researcher, critic, or writer. Different capable routes may improve diversity without guaranteeing it; duplicate routes are valid but can yield similar answers. On a successful wave, evaluation cannot start until all three settle, so the slowest candidate determines wave latency.
|
|
82
|
+
|
|
83
|
+
The evaluator receives the canonical input plus all three completed responses after their slot identities have been randomly mapped to anonymous A/B/C labels. It has no tools and must emit the closed `pi-background-tasks.fusion-evaluation.v1` structure: exactly three assessments plus agreements, conflicts, and a constrained synthesis plan (with validation accounting only for `fusion_validate`). It neither ranks a winner nor emits the final answer. If parsing or schema validation fails, the evaluation repair uses the same configured and resolved evaluator model. That single repair receives the complete original blind input, the invalid evaluator output, and bounded validation errors; another invalid result terminates the run.
|
|
84
|
+
|
|
85
|
+
The merger receives the canonical input, all three anonymous candidate responses, and the validated evaluation. It has no tools and is the last model stage. For reason, investigate, and research, its output is the sole final answer; candidate and evaluator responses are never returned directly. For `fusion_validate`, the host subsequently renders the final report from the validated finding accounting, preventing raw merger prose from adding or dropping findings.
|
|
86
|
+
|
|
87
|
+
The normal-path critical path is `preflight → max(candidate 1, candidate 2, candidate 3) → evaluator → merger`; repair inserts another evaluator-model stage before merger. Its stage fan-in is:
|
|
88
|
+
|
|
89
|
+
| Stage | Prompt fan-in | Route/timing consequence |
|
|
90
|
+
| --- | --- | --- |
|
|
91
|
+
| Candidate | Candidate system prompt + canonical input | Three parallel routes; the slowest successful candidate gates evaluation. |
|
|
92
|
+
| Evaluator | Evaluator system prompt + canonical input + all three bounded candidate outputs | First sequential stage; schema reliability matters because invalid output triggers the sole repair. |
|
|
93
|
+
| Evaluation repair | Repair system prompt + original blind evaluator input + invalid evaluator output + bounded errors | Conditional, same evaluator route, and potentially the largest prompt. |
|
|
94
|
+
| Merger | Merger system prompt + canonical input + all three candidate outputs + validated evaluation | Final sequential stage and normally the greatest synthesis/context burden. |
|
|
95
|
+
|
|
96
|
+
Budget planning reserves upstream output contracts for every fan-in stage and records worst-case reservation pressure as warnings; immediately before each child launch, the exact rendered prompt must fit that role's route. A nominally stronger model with a smaller usable context window can therefore be an invalid choice. Quality-first routing favors capable, genuinely diverse candidates, a strong schema-following evaluator, and the strongest available long-context synthesizer for merger. Speed-first routing avoids a slow candidate outlier, uses a fast schema-reliable evaluator to avoid repair, and keeps the sequential merger fast. In both cases frontier routes remain subscription OAuth only; duplicates and `$current` are valid, and metered APIs are not a tradeoff option.
|
|
97
|
+
|
|
98
|
+
See the user-facing [model-selection guide](../commands/fusion-models.md), [budgets and output contracts](#budgets-and-output-contracts), and the configured [Fusion runtime limits](../operations/configuration.md#fusion-runtime-limits).
|
|
99
|
+
|
|
75
100
|
## Validation specifics
|
|
76
101
|
|
|
77
102
|
`fusion_validate` enforces a strict public verification contract: `provided` requires non-empty evidence and no reason; `not_run` requires a reason and empty/omitted evidence. Reviewers return exactly one bare, closed candidate-report JSON object. The host keeps its shared JSON parser strict; a single complete `json` fence can be removed only by the validation-specific audited recovery path, which writes a contract-event artifact and surfaces a limitation. One irrecoverable minority report is also recorded and surfaced as a limitation, while two invalid reports fail the workflow. The host assigns stable finding ids after anonymization, the evaluator must account for every source finding exactly once, and the host renders the final report from validated accounting after the merger. Validation is advisory and read-only: it never edits files, runs tests, gates a release, or replaces builds, linters, scanners, or human review.
|
|
@@ -88,7 +113,7 @@ Inspect/research candidates write sealed tool-call audit logs. The log contains
|
|
|
88
113
|
|
|
89
114
|
## Child process isolation
|
|
90
115
|
|
|
91
|
-
Fusion never calls direct completion APIs. It launches direct child `pi --mode text` processes and writes the prompt over stdin. Child argv includes `--no-session`, `--no-extensions`, `--no-skills`, `--no-prompt-templates`, `--no-themes`, and `--no-context-files`; explicit extensions still load. Non-Anthropic children receive only the package-owned compact metadata/runtime-governor extension. Anthropic children receive, in fixed order, the
|
|
116
|
+
Fusion never calls direct completion APIs. It launches direct child `pi --mode text` processes and writes the prompt over stdin. Child argv includes `--no-session`, `--no-extensions`, `--no-skills`, `--no-prompt-templates`, `--no-themes`, and `--no-context-files`; explicit extensions still load. Non-Anthropic children receive only the package-owned compact metadata/runtime-governor extension. Anthropic children receive, in fixed order, the always-on `extensions/anthropic-attribution-child.ts` attribution/sanitization entrypoint and the runtime governor. Ordinary parent sessions use the separate feature-aware ambient entrypoint; disabling that ambient capability cannot disable explicit child safety. Attribution adds the Claude Code OAuth session header, linked account/device/session metadata, model-policy beta headers, system identity, beta-resource transport, cache surfaces, and model-aware cache usage pricing. It reads `userID` and `oauthAccount.accountUuid` from `~/.claude.json` without writing the file and fails loudly when required attribution data is absent or malformed. Its internal sanitizer removes all reviewed exact-match rejected prompt lines while preserving unrelated text and cache controls; no external sanitizer package is resolved.
|
|
92
117
|
|
|
93
118
|
Child text mode writes the final full answer to stdout. The private child extension emits compact reasoning-free metadata frames to stderr for finalized assistant messages: provider/model, stop reason, text block byte counts and hashes, aggregate text hash, the complete Pi `Usage` object (including Anthropic `cacheWrite1h` and provider-reported reasoning subsets), and a closed cache-policy observation. It governs every final `before_provider_request` payload after attribution and sanitization. For Anthropic routes, the child environment defaults `PI_CACHE_RETENTION` to `long` before provider serialization, so the attribution/Pi adapter creates system, final-tool, and final-conversation breakpoints with `ttl: "1h"`; inherited `PI_CACHE_RETENTION=short|none|long` remains explicit, and call-level `cacheRetention="none"` still wins for compaction. The final governor validates and normalizes those upstream-selected breakpoints, falls back to short when model compatibility rejects long retention, preserves no-marker compaction payloads, enforces Anthropic's four-breakpoint ceiling, and appends the subscription prompt-caching-scope beta idempotently. Its `effective_retention` field describes the final payload, not provider acceptance. Provider usage is preserved verbatim: `cacheWrite1h > 0` proves a one-hour write, but zero is inconclusive on subscription OAuth. Live normal-spawn and exact Fusion-child controls each observed a unique cache read after 370 idle seconds despite `cacheWrite1h = 0`; therefore payload observations prove request intent and `cacheRead` proves reuse, while neither zero telemetry nor a six-minute hit alone proves the full one-hour lifetime. Malformed controls or policy values abort before transport. Non-Anthropic payloads and child environments remain unchanged apart from the governor's existing JSON normalization.
|
|
94
119
|
|
|
@@ -118,7 +143,7 @@ Artifact writes use durable private temp-file/fsync/rename. Manifests enforce le
|
|
|
118
143
|
|
|
119
144
|
For tool-enabled children, the private audit journal remains open across every low-level `agent_end`, because Pi may still retry, compact and retry, or process a queued continuation. Only terminal `agent_settled` can exclusively publish the complete hash/count/byte seal. Runtime-guard refusal latches process failure, makes that seal incomplete, and forces the result settlement to failed. The child emits one closed `pi-background-tasks.fusion-runtime-guard.v2` stderr frame for malformed provider payloads, malformed Claude cache policy, provider-request loops, or tool-call loops. The frame contains the refusal code, route, request/tool ordinals, bounded payload byte/hash evidence where applicable, and a bounded message; it never emits the payload itself. The parent validates this frame and reports typed `child_runtime_limit_exceeded`, `child_runtime_payload_invalid`, or `child_cache_policy_invalid` instead of accepting a later clean-looking result or reducing it to an unexplained exit code. Tool activity after finalization, duplicate settlement, pre-settlement shutdown, extension diagnostics, malformed/duplicate runtime-guard frames, and missing/failed/stale seals are fatal. This lifecycle requires Pi 0.81.1 or newer; older Pi lines do not expose the required terminal event and are not claimed as compatible.
|
|
120
145
|
|
|
121
|
-
The four public Fusion tools return a background launch receipt after the readiness barrier. Tool-launched runs default to terminal notification plus follow-up wake; `/fusion` uses notification-only. The first successful `bg_result` retrieval durably claims and attaches complete Fusion usage exactly once; repeated retrieval returns the answer without duplicating session accounting. Running retrieval never waits.
|
|
146
|
+
The four public Fusion tools return a background launch receipt after the readiness barrier. Tool-launched runs default to terminal notification plus follow-up wake; `/fusion` uses notification-only. The first successful `bg_result` retrieval durably claims and attaches complete Fusion usage exactly once; repeated retrieval returns the answer without duplicating session accounting. Verification, delivery checks, and usage cloning finish before the claim starts. That durable claim is the retrieval settlement point: closure before it prevents a claim, while closure during its metadata write lets the already-started invocation finish and return the usage without any later host effect. Metadata failure remains loud and is not silently reset. Running retrieval never waits.
|
|
122
147
|
|
|
123
148
|
Cancellation and shutdown are loud and durable when a run store exists. The extension tracks active runs, managed tasks own their abort controllers, `bg_kill` and session shutdown abort them, and terminal task publication waits for workflow settlement. Child processes have a 50 minute wall timeout, 35 minute idle watchdog, SIGTERM grace, SIGKILL wait, process-group kill on POSIX, bounded stdout/stderr, and cleanup-error propagation.
|
|
124
149
|
|
|
@@ -5,7 +5,7 @@ mode: authored
|
|
|
5
5
|
review_policy: behavioral
|
|
6
6
|
stability: stable
|
|
7
7
|
covers_surfaces: []
|
|
8
|
-
covers_sources: [extensions/background-tasks.ts, src/core/update-check.ts, src/extension.ts, src/ui/background-tasks-manager.ts]
|
|
8
|
+
covers_sources: [extensions/background-tasks.ts, src/core/config.ts, src/core/update-check.ts, src/extension.ts, src/ui/background-tasks-manager.ts]
|
|
9
9
|
---
|
|
10
10
|
# Host UI and telemetry
|
|
11
11
|
|
|
@@ -13,12 +13,21 @@ This subsystem owns the extension entrypoint, command/tool registration, footer
|
|
|
13
13
|
|
|
14
14
|
## Entrypoint and registration
|
|
15
15
|
|
|
16
|
-
`extensions/background-tasks.
|
|
16
|
+
The published `dist/extensions/background-tasks.js` entrypoint is compiled from `extensions/background-tasks.ts`, which re-exports the authoritative `src/extension.ts`. Before creating the registry or registering a surface, the extension strictly parses the shared capability/shortcut configuration. It dynamically imports delegate/Fusion facade registration only for enabled capabilities; the dock component is imported only when an interactive manager is opened. `process` is mandatory and registers:
|
|
17
17
|
|
|
18
18
|
- commands: `/bg`, `/tasks`, `/bg-tasks`, `/bg-clear`, `/bg-update`, `/jobs`, `/logs`, `/kill`;
|
|
19
|
-
- tools: `bg_run`, `bg_status`, `bg_logs`, `bg_kill
|
|
20
|
-
-
|
|
21
|
-
- renderer: `background-task-notification
|
|
19
|
+
- tools: `bg_run`, `bg_status`, `bg_logs`, `bg_kill`;
|
|
20
|
+
- shortcut: the selected dock key (`shift+down`, `ctrl+alt+b`, or none for `off`) plus unconditional `ctrl+alt+c`;
|
|
21
|
+
- renderer: `background-task-notification`;
|
|
22
|
+
- the task UI and EventBus service.
|
|
23
|
+
|
|
24
|
+
Delegate, Fusion, attested-run, and ambient attribution registrations are independently selected by `PI_BG_FEATURES`. `bg_result` is derived and registered once iff delegate or Fusion is enabled. Disabled facade/attribution modules are absent from the process-only static startup graph rather than merely inactive. Active-tool cleanup is delegated to Pi's registration rebuild: a stale package name with no current definition is dropped, while an active definition from another extension remains active even when it uses a disabled package capability name such as `bg_delegate` or the retired `fusion_brainstorm`. The package does not perform name-wide subtraction. The default selection preserves the complete historical surface. Capability flags alone make no startup-performance claim.
|
|
25
|
+
|
|
26
|
+
## Agent-visible shell guidance
|
|
27
|
+
|
|
28
|
+
A dedicated `before_agent_start` hook adds the activation's actual background-shell executable, dialect, and `-c`/cmd argument shape before the model generates a command. The hook uses an independently replaceable prompt section when the host supports structured sections and a chained, idempotent section on older supported hosts. It preserves guidance added by other background-feature hooks in either registration order.
|
|
29
|
+
|
|
30
|
+
An inherited Nu, fish, csh, or unknown shell is explicitly described as `user-non-posix`, with instructions not to assume Bash syntax and remediation to set `PI_BG_POSIX_SHELL=bash` before startup or `/reload`. The guidance contains only resolved launch facts, not the process environment or credentials. The registry receives the same immutable selection; task snapshots and metadata make that match observable.
|
|
22
31
|
|
|
23
32
|
## Footer status
|
|
24
33
|
|
|
@@ -30,7 +39,7 @@ When visible, the footer label includes counts in this order:
|
|
|
30
39
|
2. failed,
|
|
31
40
|
3. stopped (`killed`),
|
|
32
41
|
4. done (`completed`),
|
|
33
|
-
5. entry hint (`focused` while the dock is open, otherwise `Shift
|
|
42
|
+
5. entry hint (`focused` while the dock is open, otherwise `Shift↓`, `CtrlAltB`, or `/tasks` from the parsed dock setting),
|
|
34
43
|
6. `/bg-clear` hint when there are unseen finished tasks **and the dock is closed**,
|
|
35
44
|
7. optional update segment.
|
|
36
45
|
|
|
@@ -38,9 +47,9 @@ A finished badge is cleared when that task's detail view is opened, or when `/bg
|
|
|
38
47
|
|
|
39
48
|
## Task manager UI
|
|
40
49
|
|
|
41
|
-
`/tasks`, `/bg-tasks`, and `
|
|
50
|
+
`/tasks`, `/bg-tasks`, and the configured dock shortcut (when not `off`) open the same overlay. The two commands are unconditional process surfaces, including when the shortcut is disabled. Non-interactive contexts receive an error notification directing users to `/jobs`, `/logs`, `bg_status`, or `bg_logs`.
|
|
42
51
|
|
|
43
|
-
The list view supports selection, paging, stop, confirmed stop-all, history toggle, rerun, output path, and close. Rerun is shell-task-only: typed delegate and Fusion tasks fail with guidance to relaunch through their owning tool rather than executing their display command as a shell command. The detail view shows task identity, status, runtime, output path, description, task-owned model/context/tokens/tools when reported, command, error, and an output tail.
|
|
52
|
+
The list view supports selection, paging, stop, confirmed stop-all, history toggle, rerun, output path, and close. Rerun is shell-task-only: typed delegate and Fusion tasks fail with guidance to relaunch through their owning tool rather than executing their display command as a shell command. Rerunning an opted survivor preserves `surviveReload:true`, but it is a new execution with a new id/nonce and the current activation's shell policy, timeout, and output cap. The detail view shows task identity, status, runtime, output path, description, task-owned model/context/tokens/tools when reported, command, error, and an output tail.
|
|
44
53
|
|
|
45
54
|
Detail output semantics:
|
|
46
55
|
|
|
@@ -67,7 +76,11 @@ The host UI displays telemetry only from task snapshots: context, model, token t
|
|
|
67
76
|
|
|
68
77
|
## Shutdown
|
|
69
78
|
|
|
70
|
-
On session shutdown, the
|
|
79
|
+
On session shutdown, one early synchronous lifecycle fence permanently closes the activation. Only `reason:"reload"` may first detach admission-committed `surviveReload:true`, `isAgent:false` ordinary executions; detachment removes the old host adapter before any await while preserving logical publication and notification state. The same call stack then closes admissions, aborts admission-owned cancellation scopes, closes registry publication and the EventBus service, clears pending retry/status handles, suppresses old-host notifications, closes every enabled Fusion/delegate/result lazy lane, aborts Fusion controllers, and aborts delegate preparation. The entrypoint registers this shared fence before any facade asynchronous cleanup, so sequential Pi dispatch cannot pause with another old lane still open. Later handlers drain workflow/preparation/admission-owned subprocess, file, and managed cleanup; kill every remaining registry-owned task with the specific lifecycle reason; and report cleanup failures through the UI when possible. Preflight that crosses closure cannot insert, spawn, or return EventBus success.
|
|
80
|
+
|
|
81
|
+
The first fresh `session_start` callback synchronously claims the exact `(process.pid, session id, canonical cwd)` slot, imports/durably audits the same task records, commits the fresh adapter, then allows existing UI/status/update setup. Completion during the gap queues for that adapter. Status, logs, kill, commands, and dock therefore use the same id/PID/path/nonce/execution after reload. A malformed new activation or removed extension cannot steal the owner; its fixed handoff deadline cleans up.
|
|
82
|
+
|
|
83
|
+
Shutdown terminal metadata and task waiters remain truthful even when old-activation EventBus publication is abandoned. Shutdown is idempotent, including handle cleanup on repeated calls. `session_start` rechecks activation after runtime-directory setup and before status interval/update-check creation, so an overlapping late continuation cannot recreate old resources. Pi `AgentSession.reload()` replaces the extension runner; `AgentSessionRuntime` new/switch/fork/clone/dispose flows do not claim reload handoffs and kill opted work. Kill-on-reload remains the default unless an ordinary task explicitly opts in. Hard crash/restart survival is not supported.
|
|
71
84
|
|
|
72
85
|
## Related docs
|
|
73
86
|
|
|
@@ -11,7 +11,9 @@ covers_sources: []
|
|
|
11
11
|
|
|
12
12
|
<!-- pi-docs:begin name="tool-contract-bg_delegate" generator="scripts/docs/generate.mjs" -->
|
|
13
13
|
- Label: **Background Delegate**
|
|
14
|
-
- Source: `src/delegate-extension.ts:
|
|
14
|
+
- Source: `src/delegate-extension.ts:500`
|
|
15
|
+
- Availability: `feature:delegate`
|
|
16
|
+
- Available by default: **yes**
|
|
15
17
|
- Description: Launch one background Pi agent seeded with a frozen projection of the current conversation, then return a launch receipt immediately. The child has its own session, a route pinned at launch that is never substituted, and read-only tools. Extension discovery is isolated by default; ambient mode supports extension-registered providers but executes arbitrary discovered extension code. Retrieve its verified answer with bg_result.
|
|
16
18
|
- Root schema: `object`; additionalProperties: `false`
|
|
17
19
|
|
package/docs/tools/bg_kill.md
CHANGED
|
@@ -11,7 +11,9 @@ covers_sources: []
|
|
|
11
11
|
|
|
12
12
|
<!-- pi-docs:begin name="tool-contract-bg_kill" generator="scripts/docs/generate.mjs" -->
|
|
13
13
|
- Label: **Background Kill**
|
|
14
|
-
- Source: `src/extension.ts:
|
|
14
|
+
- Source: `src/extension.ts:1181`
|
|
15
|
+
- Availability: `always`
|
|
16
|
+
- Available by default: **yes**
|
|
15
17
|
- Description: Stop a running background task by ID. Fails loudly if the task is unknown or already finished.
|
|
16
18
|
- Root schema: `object`
|
|
17
19
|
|
package/docs/tools/bg_logs.md
CHANGED
|
@@ -11,7 +11,9 @@ covers_sources: []
|
|
|
11
11
|
|
|
12
12
|
<!-- pi-docs:begin name="tool-contract-bg_logs" generator="scripts/docs/generate.mjs" -->
|
|
13
13
|
- Label: **Background Logs**
|
|
14
|
-
- Source: `src/extension.ts:
|
|
14
|
+
- Source: `src/extension.ts:1136`
|
|
15
|
+
- Availability: `always`
|
|
16
|
+
- Available by default: **yes**
|
|
15
17
|
- Description: Read bounded output from a background task for deliberate inspection; this is not a waiting primitive. Output is capped at 50.0KB for model safety and points to the full output file when truncated.
|
|
16
18
|
- Root schema: `object`
|
|
17
19
|
|
package/docs/tools/bg_result.md
CHANGED
|
@@ -11,7 +11,9 @@ covers_sources: []
|
|
|
11
11
|
|
|
12
12
|
<!-- pi-docs:begin name="tool-contract-bg_result" generator="scripts/docs/generate.mjs" -->
|
|
13
13
|
- Label: **Background Result**
|
|
14
|
-
- Source: `src/delegate-extension.ts:
|
|
14
|
+
- Source: `src/delegate-extension.ts:757`
|
|
15
|
+
- Availability: `any(feature:delegate,feature:fusion)`
|
|
16
|
+
- Available by default: **yes**
|
|
15
17
|
- Description: Retrieve a hash-verified result from a bg_delegate or background Fusion task. Never blocks: a running task returns a typed not-ready result. Oversized answers are never truncated.
|
|
16
18
|
- Root schema: `object`; additionalProperties: `false`
|
|
17
19
|
|
|
@@ -47,7 +49,7 @@ covers_sources: []
|
|
|
47
49
|
</details>
|
|
48
50
|
<!-- pi-docs:end name="tool-contract-bg_result" -->
|
|
49
51
|
|
|
50
|
-
`bg_result` retrieves the result of a `bg_delegate` or background Fusion task. It never blocks: a running task returns a typed not-ready view, and a terminal task is verified before any answer bytes are returned.
|
|
52
|
+
`bg_result` retrieves the result of a `bg_delegate` or background Fusion task. It is a derived surface: registered exactly once when `PI_BG_FEATURES` includes `delegate` or `fusion`, and absent when both producers are disabled. Delegate-only and Fusion-only configurations use the same verifier and retain their respective producer path. It never blocks: a running task returns a typed not-ready view, and a terminal task is verified before any answer bytes are returned.
|
|
51
53
|
|
|
52
54
|
## Public arguments
|
|
53
55
|
|
|
@@ -92,7 +94,7 @@ The returned text is decoded from the same aggregate buffer that was hashed. Cor
|
|
|
92
94
|
|
|
93
95
|
## Fusion retrieval
|
|
94
96
|
|
|
95
|
-
A completed Fusion task is accepted only when `manifest.json` is terminal `completed`, its `result.json` and `merged.md` fixed references match, both files match manifest-bound byte lengths and SHA-256 values, run/workflow/artifact identity matches the task, result details carry the current schema, usage is complete, and merged bytes are well-formed UTF-8. The first successful retrieval attaches complete Fusion usage exactly once; later retrievals omit usage to prevent double-counting.
|
|
97
|
+
A completed Fusion task is accepted only when `manifest.json` is terminal `completed`, its `result.json` and `merged.md` fixed references match, both files match manifest-bound byte lengths and SHA-256 values, run/workflow/artifact identity matches the task, result details carry the current schema, usage is complete, and merged bytes are well-formed UTF-8. The first successful retrieval attaches complete Fusion usage exactly once; later retrievals omit usage to prevent double-counting. Verification and delivery checks precede the durable claim. If shutdown begins after that claim starts, the in-flight retrieval finishes with usage and performs no later host effect; closure before the claim leaves it available to a fresh retrieval. A metadata-write failure remains loud and is not silently reset.
|
|
96
98
|
|
|
97
99
|
## Fusion failed/cancelled terminal view
|
|
98
100
|
|
package/docs/tools/bg_run.md
CHANGED
|
@@ -11,7 +11,9 @@ covers_sources: []
|
|
|
11
11
|
|
|
12
12
|
<!-- pi-docs:begin name="tool-contract-bg_run" generator="scripts/docs/generate.mjs" -->
|
|
13
13
|
- Label: **Background Run**
|
|
14
|
-
- Source: `src/extension.ts:
|
|
14
|
+
- Source: `src/extension.ts:927`
|
|
15
|
+
- Availability: `always`
|
|
16
|
+
- Available by default: **yes**
|
|
15
17
|
- Description: Start a named long-running shell command in the background and return immediately with a task ID and output path. By default, completed, failed, or killed terminal state is delivered automatically as <background-task-notification> and starts a follow-up agent turn; do not sleep or poll merely to wait. Output is written to .pi/tasks and model-visible logs are bounded to 50.0KB.
|
|
16
18
|
- Root schema: `object`
|
|
17
19
|
|
|
@@ -22,6 +24,7 @@ covers_sources: []
|
|
|
22
24
|
| `isAgent` | yes | `boolean` | Required. Set true only when this background task launches an LLM/agent process, such as a child `pi -p ...` or `pi --mode json ...`, so Pi-agent telemetry can be collected. Set false for scripts, tests, servers, sleeps, and ordinary shell commands. | |
|
|
23
25
|
| `name` | yes | `string` | Short human-readable task name shown in the bg footer dock. Required; use 2-6 words, not the raw command. | |
|
|
24
26
|
| `notifyOnCompletion` | no | `boolean` | Whether to deliver the durable terminal notification. Default: true; disable only when deliberately taking over completion monitoring. | |
|
|
27
|
+
| `surviveReload` | no | `boolean` | Opt in to retaining this exact ordinary isAgent:false shell execution across a real same-process Pi reload. Default: false. Unsupported for agent, managed, delegate, Fusion, and attested tasks. | |
|
|
25
28
|
| `timeoutSeconds` | no | `number` | Optional timeout; task is failed and killed when exceeded | |
|
|
26
29
|
| `triggerOnCompletion` | no | `boolean` | Whether that notification should automatically trigger a follow-up agent turn. Default: true for bg_run; requires notifyOnCompletion. | |
|
|
27
30
|
|
|
@@ -52,6 +55,10 @@ covers_sources: []
|
|
|
52
55
|
"description": "Whether to deliver the durable terminal notification. Default: true; disable only when deliberately taking over completion monitoring.",
|
|
53
56
|
"type": "boolean"
|
|
54
57
|
},
|
|
58
|
+
"surviveReload": {
|
|
59
|
+
"description": "Opt in to retaining this exact ordinary isAgent:false shell execution across a real same-process Pi reload. Default: false. Unsupported for agent, managed, delegate, Fusion, and attested tasks.",
|
|
60
|
+
"type": "boolean"
|
|
61
|
+
},
|
|
55
62
|
"timeoutSeconds": {
|
|
56
63
|
"description": "Optional timeout; task is failed and killed when exceeded",
|
|
57
64
|
"type": "number"
|
|
@@ -90,6 +97,7 @@ Optional fields:
|
|
|
90
97
|
- `timeoutSeconds: number`
|
|
91
98
|
- `notifyOnCompletion: boolean`
|
|
92
99
|
- `triggerOnCompletion: boolean`
|
|
100
|
+
- `surviveReload: boolean` — opt in only for ordinary `isAgent:false` shell work that must keep the same live execution across a supported real reload.
|
|
93
101
|
|
|
94
102
|
Legacy argument preparation can derive a missing `name` from `description` or `command`, but the public schema remains strict and requires `name`, `command`, and `isAgent`.
|
|
95
103
|
|
|
@@ -97,7 +105,7 @@ Legacy argument preparation can derive a missing `name` from `description` or `c
|
|
|
97
105
|
|
|
98
106
|
Use for long-running tests, builds, servers, watchers, sleeps, and child agent work. Do not use normal foreground shell tools for commands expected to outlive the current turn.
|
|
99
107
|
|
|
100
|
-
For an Anthropic child `pi`, keep normal extension discovery enabled. Do not pass `--no-extensions` unless the command also explicitly loads this package's `extensions/anthropic-attribution.ts` with `-e`/`--extension`.
|
|
108
|
+
For an Anthropic child `pi`, keep normal extension discovery enabled when parent ambient attribution is enabled. Do not pass `--no-extensions` unless the command also explicitly loads this package's always-on `extensions/anthropic-attribution-child.ts` with `-e`/`--extension`.
|
|
101
109
|
|
|
102
110
|
## Defaults
|
|
103
111
|
|
|
@@ -105,10 +113,21 @@ For an Anthropic child `pi`, keep normal extension discovery enabled. Do not pas
|
|
|
105
113
|
- `triggerOnCompletion`: `true`.
|
|
106
114
|
- `timeoutSeconds`: absent means no timeout.
|
|
107
115
|
- `isAgent`: no default in the tool contract; callers must provide a boolean. It is not inferred from command text.
|
|
116
|
+
- `surviveReload`: `false`. True is valid only with `isAgent:false` and never changes the default for other launches.
|
|
117
|
+
|
|
118
|
+
## Shell selection and guidance
|
|
119
|
+
|
|
120
|
+
The extension resolves one immutable shell selection per activation. Before the model generates a command, agent guidance states the exact executable, dialect, and argument shape used by `bg_run`. The same facts appear in the returned task's `shellPolicy` and durable metadata. Guidance never includes the full environment.
|
|
121
|
+
|
|
122
|
+
The compatible POSIX default is `PI_BG_POSIX_SHELL=inherit`: use non-empty `SHELL`, otherwise `/bin/sh`, with `-c`. To deliberately request the Bash syntax models commonly generate, set `PI_BG_POSIX_SHELL=bash` before starting or reloading Pi. `PI_BG_POSIX_SHELL=sh` selects portable sh. An optional `PI_BG_POSIX_SHELL_PATH` is valid only for explicit Bash/sh and must be an absolute regular executable. Bash/sh use `-c`, not a login shell. See [Configuration](../operations/configuration.md) for exact `/bin`-then-`PATH` search and validation.
|
|
123
|
+
|
|
124
|
+
Inherited Nu, fish, csh, and unknown shells remain the default when selected through `SHELL`, but are labeled `user-non-posix`, not POSIX or Bash. Their guidance says not to generate Bash syntax and gives explicit Bash remediation. Windows keeps its separate `PI_BG_SHELL` policy and ignores the POSIX variables.
|
|
108
125
|
|
|
109
126
|
## Lifecycle
|
|
110
127
|
|
|
111
|
-
The tool returns a task id, current `running` status, pid if known, output path,
|
|
128
|
+
The tool returns a task id, current `running` status, pid if known, output path, completion-delivery guidance, and structured task details including the actual shell policy. Terminal statuses are exactly `completed`, `failed`, or `killed`. With default delivery, terminal state is sent as `<background-task-notification>` and starts a follow-up agent turn; after launching, do not sleep or poll merely to wait.
|
|
129
|
+
|
|
130
|
+
An opted ordinary task crosses a supported real same-process reload as the same living child, task id, PID, launch nonce, completion id, output path/stream, shell policy, absolute timeout, launch-time cap, cumulative byte count, and tree authority. Completion in the hostless gap queues for the fresh activation. Status/logs/kill/commands/dock continue against that same object. Hard crash/process restart, new/resume/fork/clone/quit, and SDK reload without a fresh counted binding are not survival paths.
|
|
112
131
|
|
|
113
132
|
## Examples
|
|
114
133
|
|
|
@@ -124,6 +143,10 @@ The tool returns a task id, current `running` status, pid if known, output path,
|
|
|
124
143
|
{"name":"Manual server","command":"npm run dev","isAgent":false,"notifyOnCompletion":false,"triggerOnCompletion":false}
|
|
125
144
|
```
|
|
126
145
|
|
|
146
|
+
```json
|
|
147
|
+
{"name":"Reload-safe watcher","command":"npm run watch","isAgent":false,"surviveReload":true}
|
|
148
|
+
```
|
|
149
|
+
|
|
127
150
|
## Output/result
|
|
128
151
|
|
|
129
152
|
Text result:
|
|
@@ -138,7 +161,7 @@ Automatic follow-up turn: enabled.
|
|
|
138
161
|
...
|
|
139
162
|
```
|
|
140
163
|
|
|
141
|
-
Structured details include `task`, a snapshot with command, status, output path, cwd, timing, pid, byte count, `isAgent`, delivery flags, telemetry fields when available, and error when present.
|
|
164
|
+
Structured details include `task`, a snapshot with command, status, output path, cwd, timing, pid, byte count, `isAgent`, `surviveReload`, optional non-authoritative `reloadSurvival` audit facts, delivery flags, the non-secret `shellPolicy` (`policy`, `executable`, `argvPrefix`, `dialect`) for ordinary shell tasks, telemetry fields when available, and error when present.
|
|
142
165
|
|
|
143
166
|
## Errors
|
|
144
167
|
|
|
@@ -146,11 +169,14 @@ Structured details include `task`, a snapshot with command, status, output path,
|
|
|
146
169
|
- Missing command: `bg_run requires command string`.
|
|
147
170
|
- Missing `isAgent`: loud error explaining true/false use.
|
|
148
171
|
- Empty command at execution: `Background command is empty`.
|
|
172
|
+
- Invalid explicit POSIX shell mode/path or an unavailable requested Bash/sh route fails loudly without fallback.
|
|
173
|
+
- A malformed survival value fails as `pi_bg_survive_reload_invalid`; true with `isAgent:true` fails as `pi_bg_survive_reload_requires_non_agent`, both before side effects.
|
|
174
|
+
- Missing/stale/conflicting/incompatible activation ownership fails with a stable `pi_bg_reload_owner_*` code. A handoff that receives no compatible claimant reaches fixed-deadline cleanup as `pi_bg_reload_handoff_expired`.
|
|
149
175
|
- Shell/spawn/timeout/output-cap failures become loud task failures.
|
|
150
176
|
|
|
151
177
|
## Runtime artifacts
|
|
152
178
|
|
|
153
|
-
Creates `.pi/tasks/<session-id>-<pid>/<task-id>.output` and `.json
|
|
179
|
+
Creates `.pi/tasks/<session-id>-<pid>/<task-id>.output` and `.json`; the JSON records the actual ordinary-task shell policy. If `isAgent:true`, the command matches an interceptable `pi -p`, `pi --print`, or `pi --mode json` invocation, and the resolved shell supports POSIX function syntax, a temporary telemetry wrapper file is also written in the task directory. Unknown/non-POSIX inherited shells are left unwrapped with a diagnostic.
|
|
154
180
|
|
|
155
181
|
## Safety boundaries
|
|
156
182
|
|
|
@@ -11,7 +11,9 @@ covers_sources: []
|
|
|
11
11
|
|
|
12
12
|
<!-- pi-docs:begin name="tool-contract-bg_run_pi_attested" generator="scripts/docs/generate.mjs" -->
|
|
13
13
|
- Label: **Attested Pi Run**
|
|
14
|
-
- Source: `src/extension.ts:
|
|
14
|
+
- Source: `src/extension.ts:1036`
|
|
15
|
+
- Availability: `feature:attested`
|
|
16
|
+
- Available by default: **yes**
|
|
15
17
|
- Description: Opt-in evidence-oriented direct Pi spawn. Launches exactly one `pi --mode json` child, records raw Pi events/stderr, hashes prompt/report/output, observes OAuth through ModelRegistry, and emits a strict attestation sidecar only after successful completion.
|
|
16
18
|
- Root schema: `object`
|
|
17
19
|
|
package/docs/tools/bg_status.md
CHANGED
|
@@ -11,7 +11,9 @@ covers_sources: []
|
|
|
11
11
|
|
|
12
12
|
<!-- pi-docs:begin name="tool-contract-bg_status" generator="scripts/docs/generate.mjs" -->
|
|
13
13
|
- Label: **Background Status**
|
|
14
|
-
- Source: `src/extension.ts:
|
|
14
|
+
- Source: `src/extension.ts:1105`
|
|
15
|
+
- Availability: `always`
|
|
16
|
+
- Available by default: **yes**
|
|
15
17
|
- Description: Inspect one background task or list all running/recent background tasks. This is a point-in-time inspection tool, not a waiting primitive.
|
|
16
18
|
- Root schema: `object`
|
|
17
19
|
|
|
@@ -11,7 +11,9 @@ covers_sources: []
|
|
|
11
11
|
|
|
12
12
|
<!-- pi-docs:begin name="tool-contract-fusion_investigate" generator="scripts/docs/generate.mjs" -->
|
|
13
13
|
- Label: **Fusion Investigate**
|
|
14
|
-
- Source: `src/fusion-extension.ts:
|
|
14
|
+
- Source: `src/fusion-extension.ts:1305`
|
|
15
|
+
- Availability: `feature:fusion`
|
|
16
|
+
- Available by default: **yes**
|
|
15
17
|
- Description: Start a five-model Fusion investigation as a tracked background task and return immediately after durable preflight. Retrieve the verified result with bg_result after notification. Candidate children run in clean bounded read-only contexts.
|
|
16
18
|
- Root schema: `object`; additionalProperties: `false`
|
|
17
19
|
|
|
@@ -11,7 +11,9 @@ covers_sources: []
|
|
|
11
11
|
|
|
12
12
|
<!-- pi-docs:begin name="tool-contract-fusion_reason" generator="scripts/docs/generate.mjs" -->
|
|
13
13
|
- Label: **Fusion Reason**
|
|
14
|
-
- Source: `src/fusion-extension.ts:
|
|
14
|
+
- Source: `src/fusion-extension.ts:1287`
|
|
15
|
+
- Availability: `feature:fusion`
|
|
16
|
+
- Available by default: **yes**
|
|
15
17
|
- Description: Start a five-model Fusion reason workflow as a tracked background task and return immediately after durable preflight. Retrieve the verified result with bg_result after notification. Candidate children receive the reason projection and no tools; evaluator and merger also run without tools.
|
|
16
18
|
- Root schema: `object`; additionalProperties: `false`
|
|
17
19
|
|
|
@@ -11,7 +11,9 @@ covers_sources: []
|
|
|
11
11
|
|
|
12
12
|
<!-- pi-docs:begin name="tool-contract-fusion_research" generator="scripts/docs/generate.mjs" -->
|
|
13
13
|
- Label: **Fusion Research**
|
|
14
|
-
- Source: `src/fusion-extension.ts:
|
|
14
|
+
- Source: `src/fusion-extension.ts:1324`
|
|
15
|
+
- Availability: `feature:fusion`
|
|
16
|
+
- Available by default: **yes**
|
|
15
17
|
- Description: Start a five-model Fusion research workflow as a tracked background task and return immediately after durable preflight. Retrieve the verified result with bg_result after notification. Targeted URL fetch is not web search; fetched pages and URLs are untrusted.
|
|
16
18
|
- Root schema: `object`; additionalProperties: `false`
|
|
17
19
|
|
|
@@ -11,7 +11,9 @@ covers_sources: []
|
|
|
11
11
|
|
|
12
12
|
<!-- pi-docs:begin name="tool-contract-fusion_validate" generator="scripts/docs/generate.mjs" -->
|
|
13
13
|
- Label: **Fusion Validate**
|
|
14
|
-
- Source: `src/fusion-extension.ts:
|
|
14
|
+
- Source: `src/fusion-extension.ts:1344`
|
|
15
|
+
- Availability: `feature:fusion`
|
|
16
|
+
- Available by default: **yes**
|
|
15
17
|
- Description: Start an advisory, read-only Fusion validation review as a tracked background task and return immediately after durable preflight. Retrieve the verified result with bg_result after notification. It is not a build/test/lint substitute and never modifies files.
|
|
16
18
|
- Root schema: `object`; additionalProperties: `false`
|
|
17
19
|
|
|
@@ -1 +1,166 @@
|
|
|
1
|
-
|
|
1
|
+
import type { Provider } from '@earendil-works/pi-ai';
|
|
2
|
+
import type { ModelRegistry } from '@earendil-works/pi-coding-agent';
|
|
3
|
+
import type { PiContextLike, PiExtensionHost } from '../src/core/anthropic-attribution.js';
|
|
4
|
+
import { parseBackgroundTasksConfig } from '../src/core/config.js';
|
|
5
|
+
|
|
6
|
+
const ANTHROPIC_PROVIDER = 'anthropic';
|
|
7
|
+
|
|
8
|
+
type RegisteredProviderConfig = NonNullable<
|
|
9
|
+
ReturnType<ModelRegistry['getRegisteredProviderConfig']>
|
|
10
|
+
>;
|
|
11
|
+
|
|
12
|
+
interface AmbientAttributionContext extends PiContextLike {
|
|
13
|
+
readonly modelRegistry: ModelRegistry;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface ProviderSnapshot {
|
|
17
|
+
readonly effective: Provider;
|
|
18
|
+
readonly legacy: RegisteredProviderConfig | undefined;
|
|
19
|
+
readonly native: Provider | undefined;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface ProviderInstallation {
|
|
23
|
+
readonly registry: ModelRegistry;
|
|
24
|
+
readonly before: ProviderSnapshot;
|
|
25
|
+
readonly token: RegisteredProviderConfig;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function captureProviderSnapshot(registry: ModelRegistry): ProviderSnapshot {
|
|
29
|
+
const effective = registry.getProvider(ANTHROPIC_PROVIDER);
|
|
30
|
+
if (effective === undefined) {
|
|
31
|
+
throw new Error(
|
|
32
|
+
'pi_anthropic_attribution_unsupported: the host exposes no effective anthropic provider to preserve',
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
effective,
|
|
37
|
+
legacy: registry.getRegisteredProviderConfig(ANTHROPIC_PROVIDER),
|
|
38
|
+
native: registry.getRegisteredNativeProvider(ANTHROPIC_PROVIDER),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function confirmProviderInstallation(
|
|
43
|
+
registry: ModelRegistry,
|
|
44
|
+
before: ProviderSnapshot,
|
|
45
|
+
): ProviderInstallation | undefined {
|
|
46
|
+
const token = registry.getRegisteredProviderConfig(ANTHROPIC_PROVIDER);
|
|
47
|
+
const native = registry.getRegisteredNativeProvider(ANTHROPIC_PROVIDER);
|
|
48
|
+
const effective = registry.getProvider(ANTHROPIC_PROVIDER);
|
|
49
|
+
|
|
50
|
+
// The accepted duplicate-owner protocol returned without registering anything.
|
|
51
|
+
if (token === before.legacy && native === before.native && effective === before.effective) {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (
|
|
56
|
+
token === undefined ||
|
|
57
|
+
token === before.legacy ||
|
|
58
|
+
native !== undefined ||
|
|
59
|
+
effective === undefined ||
|
|
60
|
+
effective === before.effective ||
|
|
61
|
+
token.streamSimple === before.legacy?.streamSimple
|
|
62
|
+
) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
'pi_anthropic_attribution_install_failed: host provider registration did not install the package transport atomically',
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return { registry, before, token };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function sameConfigValues(
|
|
72
|
+
left: RegisteredProviderConfig,
|
|
73
|
+
right: RegisteredProviderConfig,
|
|
74
|
+
): boolean {
|
|
75
|
+
const leftKeys = Object.keys(left).sort();
|
|
76
|
+
const rightKeys = Object.keys(right).sort();
|
|
77
|
+
return (
|
|
78
|
+
leftKeys.length === rightKeys.length &&
|
|
79
|
+
leftKeys.every(
|
|
80
|
+
(key, index) =>
|
|
81
|
+
key === rightKeys[index] && Object.is(Reflect.get(left, key), Reflect.get(right, key)),
|
|
82
|
+
)
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function restoreProviderInstallation(installation: ProviderInstallation): void {
|
|
87
|
+
const { registry, before, token } = installation;
|
|
88
|
+
if (
|
|
89
|
+
registry.getRegisteredProviderConfig(ANTHROPIC_PROVIDER) !== token ||
|
|
90
|
+
registry.getRegisteredNativeProvider(ANTHROPIC_PROVIDER) !== undefined
|
|
91
|
+
) {
|
|
92
|
+
// A later owner replaced this exact installation. It owns teardown now.
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (before.native !== undefined) {
|
|
97
|
+
registry.registerProvider(before.native);
|
|
98
|
+
} else if (before.legacy !== undefined) {
|
|
99
|
+
// Native replacement removes only the still-current package legacy layer. Reapplying
|
|
100
|
+
// the prior legacy snapshot then starts from an empty legacy layer, so package-only
|
|
101
|
+
// fields cannot leak through ModelRuntime's documented merge semantics.
|
|
102
|
+
registry.registerProvider(before.effective);
|
|
103
|
+
registry.registerProvider(ANTHROPIC_PROVIDER, before.legacy);
|
|
104
|
+
} else {
|
|
105
|
+
// The exact package token and absence of a later native owner were proven above.
|
|
106
|
+
// Public unregister is therefore owner-conditional here: it removes only this
|
|
107
|
+
// still-current package layer and restores the captured built-in source absence.
|
|
108
|
+
registry.unregisterProvider(ANTHROPIC_PROVIDER);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const restoredConfig = registry.getRegisteredProviderConfig(ANTHROPIC_PROVIDER);
|
|
112
|
+
const restoredNative = registry.getRegisteredNativeProvider(ANTHROPIC_PROVIDER);
|
|
113
|
+
const restoredEffective = registry.getProvider(ANTHROPIC_PROVIDER);
|
|
114
|
+
const restoredIds = registry.getRegisteredProviderIds();
|
|
115
|
+
const registrationRestored =
|
|
116
|
+
before.legacy !== undefined
|
|
117
|
+
? restoredNative === undefined &&
|
|
118
|
+
restoredConfig !== undefined &&
|
|
119
|
+
sameConfigValues(restoredConfig, before.legacy)
|
|
120
|
+
: restoredConfig === undefined &&
|
|
121
|
+
(before.native !== undefined
|
|
122
|
+
? restoredNative === before.native
|
|
123
|
+
: restoredNative === undefined);
|
|
124
|
+
const streamIdentityRestored =
|
|
125
|
+
before.legacy !== undefined
|
|
126
|
+
? restoredConfig?.streamSimple === before.legacy.streamSimple
|
|
127
|
+
: restoredEffective?.streamSimple === before.effective.streamSimple;
|
|
128
|
+
const effectiveIdentityRestored =
|
|
129
|
+
before.legacy !== undefined || restoredEffective === before.effective;
|
|
130
|
+
const registeredIdRestored =
|
|
131
|
+
before.legacy !== undefined || before.native !== undefined
|
|
132
|
+
? restoredIds.includes(ANTHROPIC_PROVIDER)
|
|
133
|
+
: !restoredIds.includes(ANTHROPIC_PROVIDER);
|
|
134
|
+
if (
|
|
135
|
+
!registrationRestored ||
|
|
136
|
+
restoredEffective === undefined ||
|
|
137
|
+
!streamIdentityRestored ||
|
|
138
|
+
!effectiveIdentityRestored ||
|
|
139
|
+
!registeredIdRestored
|
|
140
|
+
) {
|
|
141
|
+
throw new Error(
|
|
142
|
+
'pi_anthropic_attribution_restore_failed: the preexisting host provider was not restored by identity',
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export default async function ambientAnthropicAttribution(pi: PiExtensionHost): Promise<void> {
|
|
148
|
+
const config = parseBackgroundTasksConfig();
|
|
149
|
+
if (config.features.attribution) {
|
|
150
|
+
const { default: spawnAnthropicAttribution } = await import(
|
|
151
|
+
'../src/core/anthropic-attribution.js'
|
|
152
|
+
);
|
|
153
|
+
let installation: ProviderInstallation | undefined;
|
|
154
|
+
pi.on('session_start', (_event, context) => {
|
|
155
|
+
const registry = (context as AmbientAttributionContext).modelRegistry;
|
|
156
|
+
const before = captureProviderSnapshot(registry);
|
|
157
|
+
spawnAnthropicAttribution(pi);
|
|
158
|
+
installation = confirmProviderInstallation(registry, before) ?? installation;
|
|
159
|
+
});
|
|
160
|
+
pi.on('session_shutdown', () => {
|
|
161
|
+
const current = installation;
|
|
162
|
+
installation = undefined;
|
|
163
|
+
if (current !== undefined) restoreProviderInstallation(current);
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|