pi-background-tasks 2.3.0 → 2.4.2

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/README.md CHANGED
@@ -27,7 +27,7 @@
27
27
  | Fact | Value |
28
28
  | --- | --- |
29
29
  | Package | `pi-background-tasks` |
30
- | Version | `2.3.0` |
30
+ | Version | `2.4.2` |
31
31
  | Node engine | `>=22.19.0` |
32
32
  | Pi entrypoints | `./extensions/anthropic-attribution.ts`, `./extensions/background-tasks.ts` |
33
33
  | Package image | [logo.png](https://raw.githubusercontent.com/ismailsaleekh/pi-background-tasks/main/logo.png) |
@@ -295,6 +295,7 @@ Agent tasks launched through `pi -p ...` or `pi --mode json ...` and marked `isA
295
295
  ## Architecture, trust, and safety
296
296
 
297
297
  - Runtime task files live under `.pi/tasks/<session-id>-<pid>/`; Fusion artifacts under `.pi/fusion/...`; delegate artifacts under `.pi/delegate/...`.
298
+ - Delegate launch budgeting uses backed route-family calibration for eligible large prompts and records a provable conservative counter-forecast across every byte class. During investigation, text and image-bearing tool output spill losslessly when retaining them would consume protected final-answer runway—even below the normal 64 KiB per-result threshold. Exact artifact ranges return as base64, final capture excludes intermediate tool-use narration, and near the runway boundary tools are disabled for graceful finalization. Runtime token estimates are advisory; Pi/provider own live context admission, avoiding Fusion BUG-185-style false refusals.
298
299
  - Shell jobs are tracked by the package, but they are not sandboxed. Treat commands as local processes with your permissions and credentials.
299
300
  - Delegate and Fusion child Pi processes are route-pinned where applicable; delegate/Fusion paths do not silently substitute routes.
300
301
  - Fusion uses direct child `pi --mode text` processes, not direct completion APIs. Frontier Fusion routes are admitted only through Pi Anthropic or Codex subscription OAuth; metered frontier API credentials are rejected before child creation.
package/TESTING.md CHANGED
@@ -114,8 +114,7 @@ Two unit gates protect Fusion's persisted artifact bytes, which are a frozen for
114
114
  - `tests/unit/delegate-seed.test.ts` — verbatim visible text, thinking/tool-payload
115
115
  exclusion, marker-only images, sibling-batch exclusion, byte-identical construction
116
116
  across repeated builds and separate processes, and receive-side seed verification.
117
- - `tests/unit/delegate-budget.test.ts` — reserve arithmetic, boundary accept/reject,
118
- and the total runtime governor.
117
+ - `tests/unit/delegate-budget.test.ts` — reserve arithmetic, backed-family versus conservative launch policy, incident byte-class fixture replay, provable multibyte counter-forecast, retained-growth runway, boundary accept/reject, and advisory runtime measurement.
119
118
  - `tests/unit/delegate-result-package.test.ts` — hash verification, strict base64,
120
119
  encoding refusal for lone surrogates, route-mismatch and missing-attestation
121
120
  detection, and explicitly unavailable usage.
@@ -132,8 +131,13 @@ Two unit gates protect Fusion's persisted artifact bytes, which are a frozen for
132
131
  resolves the exact pinned provider, retains the package guard, and commits.
133
132
  - `tests/scripted-provider/delegate-child-guard.test.ts` — the child guard inside a
134
133
  real Pi agent loop: a 2 MB tool result spilled to a hashed artifact with the payload
135
- kept out of the transcript, a blocked over-budget model call, exact bounded range
136
- reads, route-drift refusal, and turn-limit enforcement.
134
+ kept out of the transcript, structured preservation of image-bearing results,
135
+ repeated sub-64-KiB results spilling against protected route runway, BUG-185
136
+ regression coverage proving advisory estimates do not abort a valid live request,
137
+ graceful no-tool finalization, byte-exact base64 range reads including a split UTF-8
138
+ sequence, malformed-Unicode spill refusal, exclusion of intermediate tool-use
139
+ narration from the committed answer, multi-turn usage accumulation, route-drift
140
+ refusal, answer capture, and turn-limit enforcement.
137
141
  - `tests/sdk/delegate-sdk.test.ts` — the full public loop through the shipped
138
142
  entrypoint with a fake child `pi`: launch receipt, projected context actually
139
143
  reaching the child, default isolated argv, explicit ambient argv/warning/metadata,
@@ -192,7 +196,7 @@ npm run test:rpc
192
196
  npm run test:agent-loop
193
197
  ```
194
198
 
195
- The Fusion SDK/RPC/scripted-provider tests install a deterministic fake child `pi` in a temp `PATH` from `tests/helpers/fusion-fake-pi.ts`. Parent Pi remains the real SDK/RPC runtime; only direct child `pi --mode text` calls with the package-owned private compact metadata extension are intercepted. SDK coverage proves the Fusion tools return after durable no-child preflight without waiting for delayed children, transfer cancellation ownership away from the completed tool call, remain tracked through status/dock/kill/notification, verify manifest-bound `result.json` plus `merged.md`, and attach complete usage on the first `bg_result` retrieval exactly once. The scripted provider proves launch → no-poll parent response → terminal wake → `bg_result` → verified answer. `PI_CODING_AGENT_DIR` is pointed at the temp agent directory so `fusion-models.json` is never read from the user's real global Pi directory. Fusion v1 public-surface coverage asserts exactly four tools (`fusion_reason`, `fusion_investigate`, `fusion_research`, `fusion_validate`), no public capability argument, retired-tool active-tool removal, `/fusion` mapping to reason/no-tool candidates, closed schemas with Google-compatible enum status, targeted URL fetch not search, URL exfiltration warnings, strict validation verification rules, historical v4 rendering without old-tool activation, and actionable migration errors for `fusion_validate({prompt})`. Fusion context coverage covers both reason conversation projection and clean-task non-interference invariants, including parent-sentinel absence from every clean downstream prompt/artifact and byte-identical clean inputs across unrelated parent sessions. `tests/unit/fusion-context-prompts.test.ts` verifies that a synthetic session carrying more than 1 MB of tool arguments/results still yields a small canonical input, that user and assistant text survive verbatim, that thinking and tool payloads never appear (including no head/tail/preview sentinel), that omission counts, byte totals, and hashes are exact and stable, that repeated construction is byte-identical, that the active Fusion tool leaf and sibling calls stay scope-excluded, that images remain marker-only or ledger-only with no raw base64 in child prompts, and that every retained source block receives exactly one disposition. `tests/unit/fusion-high-cardinality.test.ts` covers the receipt-cardinality regression: a session of many short interleaved tool events (rather than a few enormous results) pins per-receipt cost, proves the compact `omitted_activity` fields are the only model-facing ones, reconciles every receipt against the ledger through `projection_map`, and proves the whole workflow fits a real route budget. `tests/unit/fusion-budget.test.ts` covers stage budgets and stage-local refusal wording; orchestrator coverage derives terminal run progress from durable attempts after usage persistence so late evaluator/merge refusal reports completed, failed, cancelled, and not-started truth instead of claiming zero children. Each route reserves the larger of Fusion's output contract and the model's declared maximum output, the limiting model is selected by conservative byte capacity (including when it is the evaluator rather than a candidate), unknown or too-small capacities fail before spawn, boundary prompts pass at exactly the limit and fail one byte past it, the child system prompt counts as input, dense multi-byte UTF-8 cannot bypass byte accounting, and candidate, evaluator, evaluation-repair, and merger expansions are each rejected before their child is spawned with zero partial launches. `tests/unit/fusion-pi-child.test.ts` covers the post-launch `fusion-runtime-guard.v2` protocol, BUG-185 removal of live token/output-reservation admission, stable payload normalization, 128-request/192-tool limits, malformed or duplicate evidence rejection, typed parent errors, and failed audit sealing. `tests/unit/fusion-claude-cache.test.ts` pins native pre-serialization `ttl: "1h"` requests, explicit short/none/long policy, call-level compaction opt-out, model compatibility fallback, non-mutation, the four-breakpoint ceiling, subscription prompt-caching-scope beta idempotence, malformed-control refusal, and distinct `child_cache_policy_invalid` parent errors. Child argv tests pin the package attribution/sanitization → runtime-governor order; root attribution tests pin linked OAuth account/device/session metadata, all exact-match sanitizer variants, beta-resource request shape, cache surfaces, one-hour provider usage pricing, duplicate-owner suppression, and the 200K subscription policy. Compact/result usage tests preserve Anthropic `cacheWrite1h` and provider `reasoning` subsets, and child metadata binds each requested/effective payload observation plus JSON-rendered output accounting in `fusion-child-result.v4`. Live subscription-OAuth acceptance on 2026-08-04 proved cold writes plus exact-repeat reads on Sonnet 4.5 and Opus 5 while both reported `cacheWrite1h = 0`. Separate normal-spawn and exact Fusion-child Opus 5 controls—with unique prompts—still read their caches after 370 idle seconds; the Fusion control wrote and reread 9,922 tokens through the attribution/sanitization → governor path. Documentation therefore treats positive `cacheWrite1h` as definitive but zero as inconclusive on subscription OAuth; payload intent, provider itemization, and behavioral lifetime remain separate evidence. Terminal `fusion-child-settlement.v3` is published only at `agent_settled`; recovered non-final provider errors must be zero-content/zero-usage retry markers named by that settlement, and one non-final candidate `stop` is accepted only when it is a hash-bound oversized original immediately followed by a same-session replacement. Failed/cancelled Fusion coverage also verifies canonical manifest-bound `failure-summary.json` evidence with no stage-output bodies, truthful classifications/omission receipts, subordinate one-shot summary persistence after `writeError`, and typed answer-free `bg_result` terminal views that never claim usage or expose partial output. `tests/scripted-provider/fusion-output-recovery.test.ts` drives a real Pi print-mode process and proves one PID/session context, one queued continuation before settlement, tool removal on turn two, original-artifact preservation, and replacement-only stdout. Missing/duplicate/tampered/failed settlements or substantive error records remain fatal. `tests/scripted-provider/fusion-runtime-guard.test.ts` drives a real Pi agent loop through Pi's `openai-codex-responses` adapter against local HTTP and proves provider-payload transforms chain in load order and `ctx.abort()` prevents transport for the execution/cache-policy refusals that remain. `tests/package/typebox-compat.test.ts` pins the TypeBox posture and compiles nullable-array schemas. The release-only `npm run test:compat` packs the package, installs exact supported Pi versions, runs `/jobs`, runs `/fusion` through the installed package entrypoint with the fake child Pi, verifies five child invocations, verifies `/fusion-models` rejects non-TUI mode, requires each supported Pi line to declare terminal `agent_settled` and `before_provider_request`, verifies the installed Anthropic adapter exposes cache breakpoints plus long/tool compatibility controls, asserts the resolved `typebox` is Pi's bundled peer rather than a private or nested copy, and scans the installed package bytes for TypeBox APIs removed in the 1.3.x line. Pi 0.75.5 is intentionally unsupported because it lacks the terminal event required to seal a Fusion audit after retries and compaction. It then drives the current host Pi through a real RPC `fusion_reason` parent-agent loop, checks the persisted tool result carries the complete Pi `Usage.cost` object, invokes `get_session_stats` (the same aggregation boundary used by the TUI footer), reopens the durable session, and verifies identical token/cost totals. All parent and child inference remains deterministic and local.
199
+ The Fusion SDK/RPC/scripted-provider tests install a deterministic fake child `pi` in a temp `PATH` from `tests/helpers/fusion-fake-pi.ts`. Parent Pi remains the real SDK/RPC runtime; only direct child `pi --mode text` calls with the package-owned private compact metadata extension are intercepted. SDK coverage proves the Fusion tools return after durable no-child preflight without waiting for delayed children, transfer cancellation ownership away from the completed tool call, remain tracked through status/dock/kill/notification, verify manifest-bound `result.json` plus `merged.md`, and attach complete usage on the first `bg_result` retrieval exactly once. The scripted provider proves launch → no-poll parent response → terminal wake → `bg_result` → verified answer. `PI_CODING_AGENT_DIR` is pointed at the temp agent directory so `fusion-models.json` is never read from the user's real global Pi directory. Fusion v1 public-surface coverage asserts exactly four tools (`fusion_reason`, `fusion_investigate`, `fusion_research`, `fusion_validate`), no public capability argument, retired-tool active-tool removal, `/fusion` mapping to reason/no-tool candidates, closed schemas with Google-compatible enum status, targeted URL fetch not search, URL exfiltration warnings, strict validation verification rules, historical v4 rendering without old-tool activation, and actionable migration errors for `fusion_validate({prompt})`. Fusion context coverage covers both reason conversation projection and clean-task non-interference invariants, including parent-sentinel absence from every clean downstream prompt/artifact and byte-identical clean inputs across unrelated parent sessions. `tests/unit/fusion-context-prompts.test.ts` verifies that a synthetic session carrying more than 1 MB of tool arguments/results still yields a small canonical input, that user and assistant text survive verbatim, that thinking and tool payloads never appear (including no head/tail/preview sentinel), that omission counts, byte totals, and hashes are exact and stable, that repeated construction is byte-identical, that the active Fusion tool leaf and sibling calls stay scope-excluded, that images remain marker-only or ledger-only with no raw base64 in child prompts, and that every retained source block receives exactly one disposition. `tests/unit/fusion-high-cardinality.test.ts` covers the receipt-cardinality regression: a session of many short interleaved tool events (rather than a few enormous results) pins per-receipt cost, proves the compact `omitted_activity` fields are the only model-facing ones, reconciles every receipt against the ledger through `projection_map`, and proves the whole workflow fits a real route budget. `tests/unit/fusion-budget.test.ts` covers stage budgets and stage-local refusal wording; orchestrator coverage derives terminal run progress from durable attempts after usage persistence so late evaluator/merge refusal reports completed, failed, cancelled, and not-started truth instead of claiming zero children. Each route reserves the larger of Fusion's output contract and the model's declared maximum output, the limiting model is selected by conservative byte capacity (including when it is the evaluator rather than a candidate), unknown or too-small capacities fail before spawn, boundary prompts pass at exactly the limit and fail one byte past it, the child system prompt counts as input, dense multi-byte UTF-8 cannot bypass byte accounting, and candidate, evaluator, evaluation-repair, and merger expansions are each rejected before their child is spawned with zero partial launches. `tests/unit/fusion-pi-child.test.ts` covers the post-launch `fusion-runtime-guard.v2` protocol, BUG-185 removal of live token/output-reservation admission, stable payload normalization, 550-request/600-tool limits enforced by child guards and parent-sealed evidence, the 32 MiB aggregate tool-result ceiling enforced at both boundaries, malformed or duplicate evidence rejection, typed parent errors, and failed audit sealing. `tests/unit/fusion-claude-cache.test.ts` pins native pre-serialization `ttl: "1h"` requests, explicit short/none/long policy, call-level compaction opt-out, model compatibility fallback, non-mutation, the four-breakpoint ceiling, subscription prompt-caching-scope beta idempotence, malformed-control refusal, and distinct `child_cache_policy_invalid` parent errors. Child argv tests pin the package attribution/sanitization → runtime-governor order; root attribution tests pin linked OAuth account/device/session metadata, all exact-match sanitizer variants, beta-resource request shape, cache surfaces, one-hour provider usage pricing, duplicate-owner suppression, and the 200K subscription policy. Compact/result usage tests preserve Anthropic `cacheWrite1h` and provider `reasoning` subsets, and child metadata binds each requested/effective payload observation plus JSON-rendered output accounting in `fusion-child-result.v4`. Live subscription-OAuth acceptance on 2026-08-04 proved cold writes plus exact-repeat reads on Sonnet 4.5 and Opus 5 while both reported `cacheWrite1h = 0`. Separate normal-spawn and exact Fusion-child Opus 5 controls—with unique prompts—still read their caches after 370 idle seconds; the Fusion control wrote and reread 9,922 tokens through the attribution/sanitization → governor path. Documentation therefore treats positive `cacheWrite1h` as definitive but zero as inconclusive on subscription OAuth; payload intent, provider itemization, and behavioral lifetime remain separate evidence. Terminal `fusion-child-settlement.v3` is published only at `agent_settled`; recovered non-final provider errors must be zero-content/zero-usage retry markers named by that settlement, and one non-final candidate `stop` is accepted only when it is a hash-bound oversized original immediately followed by a same-session replacement. Failed/cancelled Fusion coverage also verifies canonical manifest-bound `failure-summary.json` evidence with no stage-output bodies, truthful classifications/omission receipts, subordinate one-shot summary persistence after `writeError`, and typed answer-free `bg_result` terminal views that never claim usage or expose partial output. `tests/scripted-provider/fusion-output-recovery.test.ts` drives a real Pi print-mode process and proves one PID/session context, one queued continuation before settlement, tool removal on turn two, original-artifact preservation, and replacement-only stdout. Missing/duplicate/tampered/failed settlements or substantive error records remain fatal. `tests/scripted-provider/fusion-runtime-guard.test.ts` drives a real Pi agent loop through Pi's `openai-codex-responses` adapter against local HTTP and proves provider-payload transforms chain in load order and `ctx.abort()` prevents transport for the execution/cache-policy refusals that remain. `tests/package/typebox-compat.test.ts` pins the TypeBox posture and compiles nullable-array schemas. The release-only `npm run test:compat` packs the package, installs exact supported Pi versions, runs `/jobs`, runs `/fusion` through the installed package entrypoint with the fake child Pi, verifies five child invocations, verifies `/fusion-models` rejects non-TUI mode, requires each supported Pi line to declare terminal `agent_settled` and `before_provider_request`, verifies the installed Anthropic adapter exposes cache breakpoints plus long/tool compatibility controls, asserts the resolved `typebox` is Pi's bundled peer rather than a private or nested copy, and scans the installed package bytes for TypeBox APIs removed in the 1.3.x line. Pi 0.75.5 is intentionally unsupported because it lacks the terminal event required to seal a Fusion audit after retries and compaction. It then drives the current host Pi through a real RPC `fusion_reason` parent-agent loop, checks the persisted tool result carries the complete Pi `Usage.cost` object, invokes `get_session_stats` (the same aggregation boundary used by the TUI footer), reopens the durable session, and verifies identical token/cost totals. All parent and child inference remains deterministic and local.
196
200
 
197
201
  ## Coverage summary
198
202
 
@@ -200,7 +204,7 @@ Implemented coverage includes:
200
204
 
201
205
  - tools: `bg_run`, `bg_run_pi_attested`, `bg_status`, `bg_logs`, `bg_kill`, `fusion_reason`, `fusion_investigate`, `fusion_research`, `fusion_validate`, including required `isAgent` schema/runtime validation, the event-driven no-sleep/no-poll system-prompt contract, truthful launch receipts for all four notification/wake combinations, non-terminating `bg_run` compatibility, point-in-time status/log guidance, durable terminal-notification authority, attested direct Pi spawn validation, Fusion immediate launch receipts, managed progress, hash-verified `bg_result` delivery, exactly-once usage, and context exclusion, reason versioned conversation projection with explicit hash-accounted tool/thinking omissions, clean-task context isolation, pre-spawn stage budget rejection for all four expansion stages, image omission markers with raw image data excluded from child prompts, unknown/ambiguous IDs, completed-kill failure, legacy no-name preparation, head/tail truncation, and notification on/off behavior
202
206
  - commands: `/bg`, `/jobs`, `/logs`, `/kill`, `/tasks`, `/bg-tasks`, `/bg-clear`, `/bg-update`, `/claude-cache`, `/fusion`, `/fusion-models` discovery, happy paths, `/fusion` non-blocking managed-task launch and terminal notification, `/fusion` editor/cancel flow, `/fusion-models` TUI save and non-TUI rejection, `/bg --agent` parsing, finished-notice clearing, malformed `/bg`, unknown/ambiguous IDs, completed-task `/kill`, byte-limit normalization, and RPC no-hang fallback behavior
203
- - update-available notice: semver parse/compare/precedence, `formatUpdateSegment`, npm/`package.json` payload narrowing, and injected-fetch success/404/throw/timeout (unit); localhost-registry footer segment (idle + appended to an active footer), `/bg-update` non-installing instructions, and opt-out/offline/already-current/registry-failure no-segment-and-no-throw paths (SDK); `/bg-update` discovery and offline instructions (RPC). The check is one-shot on `session_start`, time-boxed, offline-safe, gated by `PI_OFFLINE`/`PI_BG_DISABLE_UPDATE_CHECK`, and `PI_BG_REGISTRY_URL` overrides the registry endpoint
207
+ - update-available notice: semver parse/compare/precedence, `formatUpdateSegment`, npm/`package.json` payload narrowing, and injected-fetch success/404/throw, silent timer-owned timeout abort, and loud independently sourced `AbortError` (unit); localhost-registry footer segment (idle + appended to an active footer), `/bg-update` non-installing instructions, and opt-out/offline/already-current/registry-failure no-segment-and-no-throw paths (SDK); `/bg-update` discovery and offline instructions (RPC). The check is one-shot on `session_start`, time-boxed, offline-safe, gated by `PI_OFFLINE`/`PI_BG_DISABLE_UPDATE_CHECK`, and `PI_BG_REGISTRY_URL` overrides the registry endpoint
204
208
  - shortcut/UI: component coverage for focused dock list/detail/key handling, detail output-tail scrolling (arrow/page scroll, follow-pause-on-scroll, `lines X–Y of N` position indicator, resume-follow-at-bottom, and no-scroll when output fits), empty/history/unread states, paging, close aliases, stop/stop-all/rerun/path actions, missing output files; SDK coverage for explicit `/bg-clear` finished-notice clearing, `/bg-clear` footer hinting, optional `Ctrl+Alt+C` fallback shortcut registration, and mixed failed/stopped/done/focused footer status; RPC coverage that `/bg-clear` works as a terminal-independent clear path; and PTY coverage for `/tasks`, `/bg-tasks`, real `Shift+Down`, arrows, page keys, detail/back/history/stop/stop-all/rerun/path/close, failed unread badges, and running/completed/failed/killed rerun paths
205
209
  - runtime files: output and metadata files under `.pi/tasks/`, Fusion private `.pi/fusion/<session-id>-<pid>/<run-id>/` artifacts plus global `fusion-models.json`, persisted `isAgent` classification, task-owned context-window telemetry snapshots, cumulative background Pi-agent token usage, tool-use counts, agent model identifier (preferring the fully-qualified `provider/model` form), explicit `isAgent:true` telemetry wrapping for background `pi` agents, `isAgent:false` non-wrapping for scripts, attested Pi flat siblings (`.pi-events.jsonl`, `.stderr`, `.pi-telemetry-wrapper.cjs`, `.attestation.json`), real child `pi --mode json` tool-event parsing for background-agent telemetry, split/large telemetry ingestion, metadata after completion/failure, Fusion v2 compact final-only metadata and explicitly marked partial-response artifacts, and Fusion manifest token plus complete cost-component aggregates equal to the sum of successful and observed failed/cancelled attempts
206
210
  - extension EventBus API: unit coverage for `pi-background-tasks:request:v1`/`response:v1` closed-frame validation, exact capability handshake, malformed payload rejection, unknown keys, unknown operations, duplicate request IDs, missing `session_start`, shutdown refusal, unsubscribe, strict terminal frame shape, and response-barrier ordering; registry coverage for one terminal publication after durable metadata when emit succeeds plus loud/retriable delivery failure. A retry after a listener throws can redeliver to an earlier listener, so consumers deduplicate by task id. SDK coverage uses a shared real `createEventBus()`, starts `printf api-ok`, reads bounded logs, observes one terminal event after the run response, and kills a real sleep task without model/provider calls
package/TEST_PLAN.md CHANGED
@@ -82,18 +82,18 @@ SDK/RPC/scripted-provider/package/compatibility coverage asserts exactly four to
82
82
  | Fusion context boundaries | canonical input `fusion-input.v5`, reason `context-omission-ledger.json`, clean-task inputs | yes | yes | yes | | | yes | | Unit covers reason/session projection for a >1 MB synthetic tool-heavy session, verbatim user/assistant text, thinking exclusion, zero tool-payload preview bytes, exact and stable omission counts/byte totals/hashes, compact tuple round-trip, receipt-to-ledger reconciliation, active-tool-call-leaf and sibling-call exclusion, and byte-identical repeated construction. Clean-task tests assert investigate/research/validate inputs omit parent system prompt, conversation projection, and omission ledger, stay byte-identical across unrelated parent sessions, and keep parent sentinels out of every clean downstream prompt and artifact. SDK/RPC verify clean validate stdin has no `conversation_projection` or transcript while `/fusion`/reason preserve the projected-conversation path. |
83
83
  | Fusion stage budgets | `budget-plan.json` (v4 per-stage forecasts), typed `prompt_budget_exceeded_forecast` / `prompt_budget_exceeded_measured` | yes | | | | | | | Unit covers stage-local launch-refusal wording plus terminal progress derived from durable attempts and persisted usage, so late evaluator/repair/merger budget failures report completed, failed, cancelled, and not-started run truth. Unit also covers the per-family affine estimator, additive segment accounting, multibyte 1.0-token/byte charging, unknown-provider floor visibility, per-route reservation of `max(Fusion output contract, model maximum output)`, byte-capacity route selection, scope guards for small windows, input-only fatal preflight versus warning-only reservations, rejection of unknown/zero/negative/too-small context windows, boundary accept at exactly the limit and reject one byte past, the child system prompt counted as input, per-stage forecasts built from the real prompt builders against each stage's own route, reservation warnings, breach-detector artifacts, safe prompts completing all five calls, persisted route/plan snapshots including negative slack on fatal rejection, and the reproduced 1 MB failure shape now fitting the smallest configured budget. Errors carry stage, measured size, allowed size, limiting model, estimator source, and remediation in both structured detail and message text. |
84
84
  | Fusion fixed-purpose profiles | `/fusion`, `fusion_reason`, `fusion_investigate`, `fusion_research`, `fusion_validate`, candidate child argv | yes | yes | yes | | | yes | | Public surface has no caller-selected capability. SDK/RPC pin `/fusion` to reason/no-tool candidates; investigate is read-only, research is targeted URL fetch with public http(s) source validation, and evaluator/merger remain no-tools by stage policy. Package guards reject extra keys and verify schemas are closed. |
85
- | Fusion research web fetch | `fusion_web_fetch({ url, extract? })`, `candidate-<slot>.attempt-<n>.tool-calls.jsonl` | yes | | | | | yes | live child | Unit covers the closed schema surface through the child extension, registration only when research mode is enabled, unsupported schemes and URL credentials failing before network access, blocked DNS classes, a mixed public-plus-blocked DNS answer failing loudly, a redirect to a blocked address failing loudly, redirect-loop limit failure, Content-Length and streamed-body response caps, unsupported content types, Markdown extraction preserving links/headings/tables/code blocks, script/style stripping, text/plain passthrough with hash and byte count, UTF-8-safe output truncation, and typed timeout. Unit also proves the resolver is consulted once and the connection is pinned to that vetted address, undeclared research URLs are rejected before fetch with only the attempted URL hash audited, and that audit metadata records `url`, `final_url`, `http_status`, `response_bytes`, and `content_sha256` without raw page content. Package guards require `fusion_web_fetch` registration behind the research env flag. Live verification fetched `https://example.com/`, reported the page H1, read a repository constant, and refused instruction-like text from both a repo file and the fetched page. |
85
+ | Fusion research web fetch | `fusion_web_fetch({ url, extract? })`, `candidate-<slot>.attempt-<n>.tool-calls.jsonl` | yes | | | | | yes | live child | Unit covers the closed schema surface through the child extension, registration only when research mode is enabled, unsupported schemes and URL credentials failing before network access, blocked DNS classes, a mixed public-plus-blocked DNS answer failing loudly, a redirect to a blocked address failing loudly, redirect-loop limit failure, Content-Length and streamed-body response caps, unsupported content types, Markdown extraction preserving links/headings/tables/code blocks, script/style stripping, text/plain passthrough with hash and byte count, UTF-8-safe output truncation, and typed timeout. Unit also proves the resolver is consulted once, the connection is pinned to that vetted address, extraction is raced against the remaining full-operation deadline, undeclared research URLs are rejected before fetch with only the attempted URL hash audited, and that audit metadata records `url`, `final_url`, `http_status`, `response_bytes`, and `content_sha256` without raw page content. Package guards require `fusion_web_fetch` registration behind the research env flag. Live verification fetched `https://example.com/`, reported the page H1, read a repository constant, and refused instruction-like text from both a repo file and the fetched page. |
86
86
  | Fusion multi-message transcript validation | `*.events.jsonl`, `fusion-child-result.v4`, `fusion-child-settlement.v3`, stop reasons `toolUse`/`stop` plus recovered retry/output-cap markers | yes | | | | | | real Pi print mode | Unit accepts a multi-message tool loop, validates increasing cache-observation request ordinals, reconstructs stdout from the final assistant message only, and sums usage across all compact metadata records. Terminal `agent_settled` emits exactly one settlement binding ordered record bytes/count/final hash. Non-final `toolUse` remains normal; non-final `error` is accepted only when it has zero content and usage, is followed by final `stop`, and its ordinal is hash-bound in the settlement. Exactly one non-final `stop` is accepted only as a hash-bound candidate response over 49,152 JSON-rendered bytes immediately followed by its same-session replacement. A real Pi print-mode characterization proves the queued continuation remains in one PID/conversation, disables tools, delays settlement, and outputs only the replacement. Final/non-zero errors, unbound non-final `stop`, `length`, `aborted`, `pending`, missing/duplicate/tampered/failed settlements, and pre-settlement shutdown fail loudly. |
87
- | Fusion runtime execution guard | `pi-background-tasks.fusion-runtime-guard.v2`, typed `child_runtime_limit_exceeded` / `child_runtime_payload_invalid` | yes | | | | | mutation guard | scripted provider | BUG-185 unit coverage proves arbitrarily large stable payloads are not refused by subtracting possible output from the context window. Request 128/tool call 192 are allowed and the next is refused; malformed payloads, malformed/duplicate frames, route drift, and clean-looking post-refusal output fail closed; parent errors preserve observed usage. The package mutation guard forbids runtime token-estimator/output-reservation admission from returning. A real local-HTTP Pi loop through the production `openai-codex-responses` adapter proves earlier payload transforms reach the later hook and `ctx.abort()` prevents transport for retained execution/cache-policy refusals. |
88
- | Fusion stale-action watchdog | `FUSION_CHILD_IDLE_TIMEOUT_MS`, child process lifecycle | yes | | | | | | | Unit proves a child with no stdout or stderr activity fails as `child_timeout` with the stalled-child message and is terminated; stderr activity resets the watchdog and allows success; the 30-minute absolute timeout path remains distinct and is not reported as a stalled child. |
89
- | Fusion candidate tool-call audit log | `candidate-<slot>.attempt-<n>.tool-calls.jsonl`, `*.seal.json`, `pi-background-tasks.fusion-tool-call.v1` | yes | | | | | | | Unit proves completed tool calls are logged with tool name, byte counts, and hashes while raw arguments/results containing a secret never appear. Children are independently bounded to 192 attempted tool calls and 8 MiB aggregate tool-result bytes; a limit refusal emits structured evidence, aborts, and cannot publish a complete seal. Repeated low-level `agent_end` boundaries publish no seal; terminal `agent_settled` exclusively seals the complete log after all retry/compaction/follow-up work. The 46-call incident regression proves a 22-call prefix is never sealed and duplicate settlement fails without replacement. Shutdown before settlement writes failed evidence and latches process failure. The parent rejects extension diagnostics and missing/failed/mismatched seals and independently rejects aggregate result bytes above 8 MiB. A trailing partial line, ordinal gap, duplicate ordinal, wrong schema version, and inspect-child partial log all fail loudly. |
87
+ | Fusion runtime execution guard | `pi-background-tasks.fusion-runtime-guard.v2`, typed `child_runtime_limit_exceeded` / `child_runtime_payload_invalid` | yes | | | | | mutation guard | scripted provider | BUG-185 unit coverage proves arbitrarily large stable payloads are not refused by subtracting possible output from the context window. Request 550/tool call 600 are allowed and request 551/tool call 601 are refused; malformed payloads, malformed/duplicate frames, route drift, and clean-looking post-refusal output fail closed; parent errors preserve observed usage. The package mutation guard forbids runtime token-estimator/output-reservation admission from returning. A real local-HTTP Pi loop through the production `openai-codex-responses` adapter proves earlier payload transforms reach the later hook and `ctx.abort()` prevents transport for retained execution/cache-policy refusals. |
88
+ | Fusion stale-action watchdog | `FUSION_CHILD_IDLE_TIMEOUT_MS`, child process lifecycle | yes | | | | | | | Unit proves a child with no stdout or stderr activity fails as `child_timeout` with the stalled-child message and is terminated; stderr activity resets the watchdog and allows success; the 50-minute absolute timeout path remains distinct and is not reported as a stalled child. |
89
+ | Fusion candidate tool-call audit log | `candidate-<slot>.attempt-<n>.tool-calls.jsonl`, `*.seal.json`, `pi-background-tasks.fusion-tool-call.v1` | yes | | | | | | | Unit proves completed tool calls are logged with tool name, byte counts, and hashes while raw arguments/results containing a secret never appear. Children are independently bounded to 600 attempted tool calls and 32 MiB aggregate tool-result bytes; a limit refusal emits structured evidence, aborts, and cannot publish a complete seal. Repeated low-level `agent_end` boundaries publish no seal; terminal `agent_settled` exclusively seals the complete log after all retry/compaction/follow-up work. The 46-call incident regression proves a 22-call prefix is never sealed and duplicate settlement fails without replacement. Shutdown before settlement writes failed evidence and latches process failure. The parent rejects extension diagnostics and missing/failed/mismatched seals and independently rejects more than 600 sealed calls or aggregate result bytes above 32 MiB. A trailing partial line, ordinal gap, duplicate ordinal, wrong schema version, and inspect-child partial log all fail loudly. |
90
90
  | Pi/TypeBox compatibility | `peerDependencies`, packed bytes | yes | | | | | yes | | Unit pins typebox as a `"*"` peer that is neither a runtime nor bundled dependency, requires the resolved TypeBox to be Pi 0.83/0.84's 1.3.x line, requires all four supported Pi/TUI lines in the peer range, scans all package TypeScript for the seven removed TypeBox APIs, and compiles the shipped tool schema plus nullable-array/nullable-string and optional-field shapes under TypeBox 1.3. The release compat gate repeats the removed-API scan against installed package bytes per Pi version. |
91
91
  | Pi hook contract | `npm run test:hook-contract`, `tests/scripted-provider/pi-hook-contract-evidence.json`, `src/core/delegate/hook-contract-evidence.json` | | | | | | evidence parity | yes | Executes real Pi agent loops and records, rather than assumes: Fusion `before_provider_request` transformations chain in extension load order through the Codex transport adapter and abort before local HTTP transport; Delegate `context` fires once before every model call in load order; returned messages reach the provider; **throwing** in `context` does NOT block dispatch (Pi catches and continues); `ctx.abort()` either skips provider invocation (Pi 0.84) or delivers an already-aborted signal (older supported lines), blocks transport, and terminates the run; `tool_result` fires before the transcript entry, chains in load order, replacement reaches the provider while the original does not, and toolCallId/role/`isError` survive. Handler ordering across two separate probe extensions is pinned. The evidence file is compared, never silently regenerated, and a package test asserts the shipped copy is byte-identical. |
92
92
  | Delegate context seeding | `bg_delegate` seed `pi-background-tasks.delegate-seed.v2`, `seed.json`, `context-omission-ledger.json` | yes | yes | | | | mutation guard | yes | Unit covers verbatim visible user/assistant text, complete exclusion of thinking/tool-call arguments/tool-result payloads, marker-only images with no raw bytes anywhere in the seed, exclusion of the in-flight `bg_delegate` call **and every sibling call in the same assistant message** (two delegates launched together get byte-identical projected history), the prompt preserved exactly and marked `explicit_text` authority, hash-bound `extension_mode`, zero payload-preview bytes, byte-identical construction across repeated builds and across separate processes via a fixed-leaf fixture, and refusal of a blank prompt. Receive-side verification rejects a single mutated byte, a foreign task identity, a structurally malformed seed whose hash matches, a directive whose hash disagrees with its text, an unknown extension mode, and an unsupported capability. SDK proves the projected parent text actually reaches the child. |
93
93
  | Delegate route pinning | `bg_delegate` `route`, `route_attestations` | yes | yes | | | | mutation guard | yes | Unit covers defaulting to the parent current model, explicit pinning, refusal of an unavailable route with `route_unresolved` and no substitution, refusal of a route with no declared context window with `route_capacity_unknown` rather than assuming one, and refusal when neither is available. The result package rejects a mismatched route, a mismatched attestation, and a package carrying no attestation at all. The scripted-provider gate proves an observed route drift prevents a success commit entirely. The mutation guard fails if a fallback list or first-available selection appears. |
94
- | Delegate budget and guard | `budget-plan.json`, typed `seed_budget_exceeded` / `provider_context_budget_exhausted` | yes | yes | | | | mutation guard | yes | Unit covers reserve subtraction, exact-boundary accept and one-byte-past reject, the child system prompt counted as input, multi-byte UTF-8 measured by bytes, refusal of unusable windows without defaulting, and a total runtime governor that cannot throw from inside a hook. Preflight rejection is proven to create **zero** children and **zero** artifacts for hook-contract, budget, and blank-prompt refusals, with the artifact directory verified absent. The scripted-provider gate proves an over-budget model call is blocked in a real agent loop and produces a typed terminal record with no committed result. |
95
- | Delegate tool-result spilling | `spill/` artifacts, `pi-background-tasks.delegate-receipt.v1`, `delegate_read_artifact` | yes | yes | | | | mutation guard | yes | Unit proves a 2 MB payload is written in full, hashed, and represented by a receipt carrying `(turn_sequence, source_call_index, tool_call_id)` assigned before execution, so parallel spills completing out of order cannot be mis-associated; an aggregate-cap refusal writes nothing and emits no receipt; bounded range reads return exactly the requested bytes; a range past end-of-file, a path escaping the artifact directory, and non-positive lengths are refused rather than shortened. The scripted-provider gate proves in a real agent loop that the raw payload never enters the transcript, the transcript instead carries a receipt naming the hash and stating nothing was truncated, and an over-long range read fails loudly. |
96
- | Delegate result commit and `bg_result` | `result.json` (`pi-background-tasks.delegate-result.v1`), `bg_result` | yes | yes | | | | mutation guard | yes | The single atomically-committed package is the sole answer data plane: temp-write, file fsync, and rename, with the rename as the commit point; POSIX additionally fsyncs the parent directory while Windows explicitly lacks that portable directory-entry guarantee. Unit covers round-trip verification, multi-block concatenation, exact preservation of U+2028/U+2029/emoji/unnormalized sequences, refusal of lone surrogates rather than U+FFFD substitution, detection of block-hash, aggregate-hash, declared-length, and non-strict-base64 corruption, foreign identity/seed rejection, and explicitly unavailable usage that is never reported as zero. Terminal evaluation treats a zero-exit child with no committed package as typed `child_exited_without_commit`, reports the child-recorded reason when present, and classifies a killed task as cancelled. SDK covers not-ready (non-blocking), completed, corrupted, route-drift, and unknown-task paths, and proves an oversized answer degrades to an artifact reference while an explicit inline request fails with `result_too_large_for_inline` and is never truncated. |
94
+ | Delegate budget and guard | `budget-plan.json` v3, `runtime-budget.json`, typed `seed_budget_exceeded` | yes | yes | | | | mutation guard | yes | Unit covers reserve subtraction, exact-boundary accept and one-byte-past reject, backed large-prompt family calibration with a provable every-byte-class counter-forecast, the 173,740-byte compacted-session incident byte-class fixture, protected retained-growth/finalization runway, child system prompt input, multi-byte UTF-8, and conservative fallback for small/unbacked routes. Preflight refusal creates **zero** children and **zero** artifacts. Scripted-provider coverage pins Fusion BUG-185, image-bearing structured spill fidelity, base64 range fidelity across split UTF-8, final-answer-only capture, and truthful diagnostics. An advisory overage does not self-report provider exhaustion or abort valid transport; it disables tools for graceful finalization. Runtime evidence records retained/spilled bytes, context estimates, first-request usage, and calibration underforecast. |
95
+ | Delegate tool-result spilling | `spill/` artifacts, `pi-background-tasks.delegate-receipt.v1`, `delegate_read_artifact` | yes | yes | | | | mutation guard | yes | Unit proves a 2 MB payload is written in full, hashed, and represented by a receipt carrying `(turn_sequence, source_call_index, tool_call_id)` assigned before execution, so parallel spills completing out of order cannot be mis-associated; an aggregate-cap refusal writes nothing and emits no receipt; bounded range reads return exactly the requested bytes; a range past end-of-file, a path escape, and non-positive lengths are refused. Scripted-provider coverage additionally proves repeated 32 KiB results—each below the fixed 64 KiB cap—spill proactively when retaining them would consume protected final-answer runway, while the child still commits a complete answer. |
96
+ | Delegate result commit and `bg_result` | `result.json` (`pi-background-tasks.delegate-result.v1`), `bg_result` | yes | yes | | | | mutation guard | yes | The single atomically-committed package is the sole answer data plane: temp-write, file fsync, and rename, with the rename as the commit point; POSIX additionally fsyncs the parent directory. Unit covers round-trip verification, exact encoding, corruption/identity/route rejection, explicitly unavailable usage, answer capture enforcement, zero-exit/no-commit, child-recorded failure, and cancellation. SDK covers not-ready, completed, corrupted, route-drift, unknown-task, and no-truncation delivery. Terminal diagnostics dynamically advertise only paths that exist: the merged `.pi/tasks/...output` path is reported when available, while absent delegate-local `child.stdout.txt`/`child.stderr.txt` files are never claimed. |
97
97
  | Delegate extension/isolation boundary | `extensionMode`, child argv, `--session-id`, `--session-dir`, `--tools` | yes | yes | | | | mutation guard | yes | Unit proves default/explicit isolated mode sets `--no-extensions`; ambient mode omits only that flag; both modes retain separate task-owned session identity, inspect-only model tools, the explicit denylist, disabled skills/prompt templates/themes/context files, and the package guard; Anthropic keeps attribution-before-guard in both modes. Public schema/preparation rejects unknown modes, arbitrary `extensions`/`extensionPaths`, and route substitution. Seed/task facts/launch details/manifest bind the selected mode, and ambient receipt text warns that arbitrary extension code executes outside the tool sandbox. SDK verifies argv, warning, and metadata. A fresh-process scripted-provider regression installs a provider only in a temp global extension directory: isolated mode fails unknown-provider with no result, while ambient mode resolves that exact pinned provider and commits under the guard. |
98
98
  | Fusion artifact byte immutability | `canonical-input.json`, `context-omission-ledger.json`, `budget-plan.json` | yes | | | | | shared-source guard | | A 28-case differential corpus is rendered to raw bytes and compared against a committed golden file that is never auto-updated once present, and separately against `tests/oracle/fusion-context-pre-extraction.ts`, a verbatim pre-extraction copy of the projection engine used as an **independent oracle**. Equivalence covers canonical input, ledger, and budget-plan bytes, `Object.is` comparison of budget floats including `utilization`, field-for-field accounting parity, ledger root hashes, and identical error type and message for unknown blocks and blank requests. The package guard now scans the shared `context/` modules, not only the Fusion facade, and pins both the shared divisor definition and Fusion's binding to it. |
99
99
  | Fusion model selector | `/fusion-models`, `fusion-models.json`, `FusionModelSelector` | yes | yes | yes | yes | yes | yes | | Unit covers strict config parsing, duplicates, slash-containing model IDs, stale model failures, `$current`, atomic save, inter-process lock/revision compare-and-swap, and deterministic concurrent-save conflict. Component covers all five slots, duplicate selection, searchable model list, stale display, reset/save/cancel, persistence errors, and width safety. SDK drives the real command in a synthetic TUI context without `ctx.mode` to cover old Pi compatibility and verifies duplicate `$current`/explicit model persistence; SDK also verifies headless no-UI rejection. RPC verifies non-TUI notification without hanging, PTY verifies the real selector opens in a TUI, and compatibility smoke verifies print-mode rejection for every supported Pi version. |
@@ -116,7 +116,7 @@ SDK/RPC/scripted-provider/package/compatibility coverage asserts exactly four to
116
116
  | Dock close | `x`/`Esc`/`q` | | | | yes | yes | | | Component + PTY. |
117
117
  | Shortcut opens dock | `Shift+Down` | | registration | | | yes | | | PTY sends xterm `ESC [ 1 ; 2 B`. |
118
118
  | Clear finished notices | `/bg-clear`, optional `Ctrl+Alt+C` fallback | | yes | yes | | | | | `/bg-clear` is the canonical terminal-independent path and is advertised while unseen finished tasks exist and the dock is closed. SDK invokes the slash-command handler and verifies fallback shortcut registration; RPC verifies `/bg-clear` clears finished notices. Opening a finished task's detail marks that task seen; merely opening the list or closing the dock does not clear other badges. |
119
- | Update-available footer notice | `⬆ v<latest> /bg-update` footer segment + `/bg-update` command | yes | yes | yes | | | | | Unit covers semver parse/compare/precedence, `isNewerVersion`, `formatUpdateSegment`, npm/`package.json` payload narrowing, injected-fetch success/404/throw/timeout, and `package.json` read/degrade. SDK uses a localhost registry to verify the idle and append-to-active footer segment, `/bg-update` non-installing instructions, and that opt-out (`PI_BG_DISABLE_UPDATE_CHECK=1`), offline (`PI_OFFLINE=1`), already-current, and registry-failure paths render no segment and never throw. RPC verifies `/bg-update` discovery and offline non-installing instructions. The check is one-shot per `session_start`, time-boxed, offline-safe, and never runs on the status tick. |
119
+ | Update-available footer notice | `⬆ v<latest> /bg-update` footer segment + `/bg-update` command | yes | yes | yes | | | | | Unit covers semver parse/compare/precedence, `isNewerVersion`, `formatUpdateSegment`, npm/`package.json` payload narrowing, injected-fetch success/404/throw, silent internally owned timeout abort, loud independently sourced `AbortError`, and `package.json` read/degrade. SDK uses a localhost registry to verify the idle and append-to-active footer segment, `/bg-update` non-installing instructions, and that opt-out (`PI_BG_DISABLE_UPDATE_CHECK=1`), offline (`PI_OFFLINE=1`), already-current, and registry-failure paths render no segment and never throw. RPC verifies `/bg-update` discovery and offline non-installing instructions. The check is one-shot per `session_start`, time-boxed, offline-safe, and never runs on the status tick. |
120
120
  | Durable file writes | `src/core/durable-fs.ts` (metadata, event/stderr buffers, attestations, delegate/Fusion artifacts, `fusion-models.json`) | yes | | | | | mutation guard | | Unit covers single-open write/sync/close ordering, exclusive `wx` temp at `0o600`, direct `w` writes with inherited mode, never reopening a pathname to flush it, temp ownership (never deleting another writer's colliding temp), primary-versus-cleanup error precedence across write/sync/close/rename/remove, `renameCompleted` after post-rename directory failure, the Windows directory-sync skip, real-filesystem overwrite/atomic-replace/concurrency, and fatal `fsync` failures. Ordinary task `.output` uses a streaming writer that is ended/drained before terminal metadata but is not explicitly fsynced; the durable-fs claim does not extend to that stream. Package mutation guards pin the durable helper invariants. Fixes the Windows `EPERM: operation not permitted, fsync` reported in PR #1. |
121
121
  | Runtime output files | `.pi/tasks/...output` | yes | yes | | | | | | SDK asserts existence. |
122
122
  | Runtime metadata files | `.pi/tasks/...json` | yes | yes | | | | | | SDK asserts shape/status/name/context usage; registry unit tests cover metadata failure/update ordering. |
package/docs/INDEX.md CHANGED
@@ -142,7 +142,7 @@ Generated navigation for every package-local documentation page. This index inte
142
142
  | tool | `bg_delegate` | `tool:bg_delegate` | `src/delegate-extension.ts:340` |
143
143
  | tool | `bg_kill` | `tool:bg_kill` | `src/extension.ts:908` |
144
144
  | tool | `bg_logs` | `tool:bg_logs` | `src/extension.ts:863` |
145
- | tool | `bg_result` | `tool:bg_result` | `src/delegate-extension.ts:515` |
145
+ | tool | `bg_result` | `tool:bg_result` | `src/delegate-extension.ts:516` |
146
146
  | tool | `bg_run` | `tool:bg_run` | `src/extension.ts:682` |
147
147
  | tool | `bg_run_pi_attested` | `tool:bg_run_pi_attested` | `src/extension.ts:767` |
148
148
  | tool | `bg_status` | `tool:bg_status` | `src/extension.ts:832` |
@@ -62,7 +62,8 @@ Calibration facts in code:
62
62
  - observed large Fusion prompt corpus: 882 prompts, dated 2026-08-02;
63
63
  - Anthropic configured rate: 1.73 B/token after haircut;
64
64
  - OpenAI Codex configured rate: 2.89 B/token after haircut;
65
- - conservative/floor rates are used for delegate scope, small prompts, unbacked models, unknown providers, capacity guards, and dense-ASCII out-of-domain cases.
65
+ - delegate launch uses backed family calibration only for large prompts on routes that can hold the calibration domain; small prompts/routes, unbacked models, unknown providers, and dense-ASCII out-of-domain cases use conservative/floor rates;
66
+ - delegate runtime context estimates are advisory, while a separate provable `1.00 B/token` retained-growth budget drives explicit spilling and no-tool finalization.
66
67
 
67
68
  The dense-ASCII gate is explicitly a low-whitespace heuristic proxy, not a tokenizer guarantee. Calibration applies only when the input is in the measured domain and the route capacity can hold that domain.
68
69
 
@@ -70,9 +71,10 @@ The dense-ASCII gate is explicitly a low-whitespace heuristic proxy, not a token
70
71
 
71
72
  - `allowedInputTokens()` returns a signed number and never clamps unusable routes to zero.
72
73
  - Callers must reject unusable or too-small context windows before spawning children.
73
- - Multibyte bytes cannot bypass accounting.
74
+ - Multibyte bytes cannot bypass accounting; delegate's published provable counter-forecast charges them at the same `1.00 B/token` ceiling as every other byte class.
74
75
  - Unknown output contracts are charged separately; future output cannot be assumed to be cheap.
75
76
  - Rate-source warnings are part of the contract and should be surfaced in refusal details.
77
+ - A package-local estimate must not reject a live provider payload by subtracting hypothetical output; Fusion BUG-185 established this as a false-refusal shape. Use conservative estimates for lossless spill decisions, where a false positive preserves bytes rather than failing work.
76
78
 
77
79
  ## No silent truncation
78
80
 
@@ -13,18 +13,17 @@
13
13
  "state": "pass"
14
14
  },
15
15
  {
16
- "authored_body_sha256": "sha256:36942d62aa217dad07813c7900291adbc39b1ac859b18d94f606db0ce2e13a9f",
16
+ "authored_body_sha256": "sha256:810591c82ced086e1622be9b037db88c8a667856580895dc90cd5af54c6e2107",
17
17
  "covers_sources": [
18
18
  "src/core/context/parent-snapshot.ts",
19
19
  "src/core/context/token-budget.ts",
20
20
  "src/core/context/visible-conversation-v2.ts"
21
21
  ],
22
22
  "doc_id": "concepts/context-projection-and-budgeting",
23
- "notes": "Reviewed visible conversation transform identity, active-branch snapshotting, omission receipts and hashes, text/tool/thinking/image dispositions, token estimators, calibration, reserves, warnings, and fail-loud no-truncation behavior.",
24
23
  "rel": "docs/concepts/context-projection-and-budgeting.md",
25
24
  "required": true,
26
25
  "reviewer": "gpt-5.5-final-context-projection-reviewer",
27
- "state": "pass"
26
+ "state": "stale-authored-prose"
28
27
  },
29
28
  {
30
29
  "authored_body_sha256": "sha256:72c53dcc104f9b5efa57e38bcb21a4c3c620d5caf1571af82348f94dcafbe941",
@@ -75,7 +74,7 @@
75
74
  "state": "stale-authored-prose"
76
75
  },
77
76
  {
78
- "authored_body_sha256": "sha256:bc319ce7e62d2592c71476f950947c8ff011fb74e7f2093b0169f77d59980f18",
77
+ "authored_body_sha256": "sha256:67fbb232e66bf0b9f4c1a81b54ec5476c0aae929ed0f0777d57601afcac8e08b",
79
78
  "covers_sources": [
80
79
  "extensions/delegate-child.ts",
81
80
  "src/core/delegate/artifacts.ts",
@@ -97,7 +96,7 @@
97
96
  "state": "stale-authored-prose"
98
97
  },
99
98
  {
100
- "authored_body_sha256": "sha256:b6238bacecf46139dab1d6a8622783db76d913f0ce727205151b65b7cb210481",
99
+ "authored_body_sha256": "sha256:cd882ae075d47e97e2fb45709d7ac044811eedb68e2377b1742ac44147297aa4",
101
100
  "covers_sources": [
102
101
  "extensions/fusion-child.ts",
103
102
  "src/core/fusion/artifacts.ts",
@@ -833,7 +832,7 @@
833
832
  "image": "https://raw.githubusercontent.com/ismailsaleekh/pi-background-tasks/main/logo.png",
834
833
  "name": "pi-background-tasks",
835
834
  "type": "module",
836
- "version": "2.3.0"
835
+ "version": "2.4.2"
837
836
  },
838
837
  "public_surface_ids": [
839
838
  "command:bg",
@@ -1172,7 +1171,7 @@
1172
1171
  ],
1173
1172
  "type": "object"
1174
1173
  },
1175
- "source": "src/delegate-extension.ts:515"
1174
+ "source": "src/delegate-extension.ts:516"
1176
1175
  },
1177
1176
  {
1178
1177
  "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.",
@@ -111,18 +111,18 @@ These are source constants, not documented operator env knobs:
111
111
 
112
112
  | Limit | Value |
113
113
  |---|---:|
114
- | Child absolute timeout | 30 minutes |
115
- | Child stale-output watchdog | 20 minutes |
114
+ | Child absolute timeout | 50 minutes |
115
+ | Child stale-output watchdog | 35 minutes |
116
116
  | Child stdout cap | 32 MiB |
117
- | Child stderr cap | 4 MiB |
118
- | Provider requests per child | 128 |
119
- | Tool calls per child | 192 |
120
- | Aggregate candidate tool-result bytes | 8 MiB |
117
+ | Child stderr cap | 16 MiB |
118
+ | Provider requests per child | 550 |
119
+ | Tool calls per child | 600 |
120
+ | Aggregate candidate tool-result bytes | 32 MiB |
121
121
  | Candidate output contract | 48 KiB JSON-rendered bytes |
122
122
  | Evaluator output contract | 64 KiB JSON-rendered bytes |
123
123
  | Merger output contract | 64 KiB JSON-rendered bytes |
124
- | `fusion_web_fetch` timeout | 60 seconds |
125
- | `fusion_web_fetch` response body cap | 2 MiB |
124
+ | `fusion_web_fetch` timeout | 90 seconds |
125
+ | `fusion_web_fetch` response body cap | 4 MiB |
126
126
  | `fusion_web_fetch` returned content cap | 32 KiB |
127
127
  | `fusion_web_fetch` redirect cap | 5 hops |
128
128
 
@@ -24,15 +24,17 @@ Start from the symptom, verify the source-owned doc, then apply the remediation.
24
24
  | Terminal metadata/output durability failure | Fsync/close/metadata write failed | Treat terminal state as failed; inspect `DurableFileError` operation/path/cause. See `docs/subsystems/child-launch-durability-and-safety.md`. |
25
25
  | Delegate refuses with `delegate_hook_contract_unsupported` | Current Pi hook behavior does not match committed evidence | Re-run the hook characterization gate during release work and re-review the guard; do not weaken the guard. See `docs/operations/testing.md`. |
26
26
  | Delegate refuses with `route_unresolved` or `route_capacity_unknown` | Requested/default route unavailable or lacks usable context window | Pin an available provider/model with declared context window; no substitute route is selected. |
27
- | Delegate refuses with `seed_budget_exceeded` | Frozen seed plus child prompt/system prompt exceeds allowed input tokens | Use a larger-context subscription route, delegate earlier, or reduce visible parent text. Nothing was clipped. |
27
+ | Delegate refuses with `seed_budget_exceeded` | Exact child prompt plus system prompt exceeds allowed input under the backed route-family policy or conservative fallback | Use a larger-context subscription route, delegate earlier, or reduce visible parent text. Nothing was clipped. |
28
+ | Delegate spills many individually small tool results | Retaining them would consume protected final-answer runway | This is expected lossless pressure control. The full bytes are in `spill/`; inspect `runtime-budget.json`. Narrow the investigation only if receipt-driven range reads become excessive. |
29
+ | Delegate enters finalization runway | Advisory context/runway pressure disabled tools so the child can answer from gathered evidence | Let the child finish. Do not re-enable tools or treat the advisory estimate as a provider context rejection. |
28
30
  | `bg_result` says not ready | Child has not committed `result.json` yet | Wait for terminal notification or inspect later; do not poll tightly. |
29
31
  | Delegate result corruption/hash/identity mismatch | Result package does not match task/seed/route/hash contract | Treat as invalid; inspect artifact bytes. Do not synthesize an answer. |
30
32
  | Fusion model unavailable or metered route refusal | Frontier route is not admitted as a Pi subscription/OAuth route, or configured model is stale | Fix `/fusion-models` config to available subscription routes. Never route GPT/Claude-class work through metered APIs. |
31
33
  | Fusion prompt budget exceeded | Stage forecast or measured prompt exceeds limiting route capacity after reserving the larger of the Fusion output contract and route maximum output | Use a larger-context or lower-max-output configured subscription route, or reduce explicit request/context. Do not route-substitute after planning. |
32
- | `child_runtime_limit_exceeded` | The child exceeded 128 provider requests or 192 tool calls | Inspect the failed audit seal and split an unbounded task; do not raise limits blindly. |
34
+ | `child_runtime_limit_exceeded` | The child exceeded 550 provider requests or 600 tool calls | Inspect the failed audit seal and split an unbounded task; do not raise limits blindly. |
33
35
  | `child_runtime_payload_invalid` | The final provider payload could not be normalized as one stable JSON object | Fix the provider/payload integration; Fusion does not fall back to a lossy serializer. |
34
36
  | `child_cache_policy_invalid` | Claude cache policy, retention configuration, or final cache-control shape was invalid | Set `PI_CACHE_RETENTION` to `none`, `short`, or `long`; do not bypass its four-breakpoint or shape checks. |
35
- | Fusion child timeout vs idle timeout | Absolute timeout is 30 minutes; idle watchdog is 20 minutes of no stdout/stderr activity | Preserve distinction in errors. `FUSION_CHILD_IDLE_TIMEOUT_MS` is a source constant, not documented as env-configurable. |
37
+ | Fusion child timeout vs idle timeout | Absolute timeout is 50 minutes; idle watchdog is 35 minutes of no stdout/stderr activity | Preserve distinction in errors. `FUSION_CHILD_IDLE_TIMEOUT_MS` is a source constant, not documented as env-configurable. |
36
38
  | Fusion research URL rejected | Source URL is not declared/public http(s), has credentials, resolves to blocked address class, or redirects unsafely | Provide declared public source URLs with purpose; targeted fetch is not search. |
37
39
  | `/fusion-models` rejects in non-TUI mode | Selector requires interactive UI | Use an interactive Pi TUI session to edit config; headless path should fail loudly. |
38
40
  | `/bg-update` shows no update | Offline/opt-out/current-version/registry failure path | Check `PI_OFFLINE`, `PI_BG_DISABLE_UPDATE_CHECK`, `PI_BG_REGISTRY_URL`; update check is one-shot and non-blocking. |
@@ -16,51 +16,51 @@ This generated registry lists production environment-variable references, runtim
16
16
 
17
17
  | Name | Access | Provenance |
18
18
  | --- | --- | --- |
19
- | `ANTHROPIC_API_KEY` | remove | `src/core/attested-pi-run.ts:135`<br>`src/core/fusion/pi-child.ts:98` |
20
- | `ANTHROPIC_AUTH_TOKEN` | remove | `src/core/fusion/pi-child.ts:98` |
21
- | `ANTHROPIC_BASE_URL` | remove | `src/core/attested-pi-run.ts:135`<br>`src/core/fusion/pi-child.ts:98` |
22
- | `AZURE_OPENAI_AD_TOKEN` | remove | `src/core/fusion/pi-child.ts:98` |
23
- | `AZURE_OPENAI_API_KEY` | remove | `src/core/fusion/pi-child.ts:98` |
24
- | `AZURE_OPENAI_API_VERSION` | remove | `src/core/fusion/pi-child.ts:98` |
25
- | `AZURE_OPENAI_BASE_URL` | remove | `src/core/fusion/pi-child.ts:98` |
26
- | `AZURE_OPENAI_DEPLOYMENT_NAME_MAP` | remove | `src/core/fusion/pi-child.ts:98` |
27
- | `AZURE_OPENAI_ENDPOINT` | remove | `src/core/fusion/pi-child.ts:98` |
28
- | `AZURE_OPENAI_RESOURCE_NAME` | remove | `src/core/fusion/pi-child.ts:98` |
19
+ | `ANTHROPIC_API_KEY` | remove | `src/core/attested-pi-run.ts:135`<br>`src/core/fusion/pi-child.ts:100` |
20
+ | `ANTHROPIC_AUTH_TOKEN` | remove | `src/core/fusion/pi-child.ts:100` |
21
+ | `ANTHROPIC_BASE_URL` | remove | `src/core/attested-pi-run.ts:135`<br>`src/core/fusion/pi-child.ts:100` |
22
+ | `AZURE_OPENAI_AD_TOKEN` | remove | `src/core/fusion/pi-child.ts:100` |
23
+ | `AZURE_OPENAI_API_KEY` | remove | `src/core/fusion/pi-child.ts:100` |
24
+ | `AZURE_OPENAI_API_VERSION` | remove | `src/core/fusion/pi-child.ts:100` |
25
+ | `AZURE_OPENAI_BASE_URL` | remove | `src/core/fusion/pi-child.ts:100` |
26
+ | `AZURE_OPENAI_DEPLOYMENT_NAME_MAP` | remove | `src/core/fusion/pi-child.ts:100` |
27
+ | `AZURE_OPENAI_ENDPOINT` | remove | `src/core/fusion/pi-child.ts:100` |
28
+ | `AZURE_OPENAI_RESOURCE_NAME` | remove | `src/core/fusion/pi-child.ts:100` |
29
29
  | `ComSpec` | read | `src/core/common.ts:726`<br>`src/core/common.ts:737` |
30
- | `OPENAI_API_KEY` | remove | `src/core/attested-pi-run.ts:135`<br>`src/core/fusion/pi-child.ts:98` |
31
- | `OPENAI_BASE_URL` | remove | `src/core/attested-pi-run.ts:135`<br>`src/core/fusion/pi-child.ts:98` |
32
- | `OPENROUTER_API_KEY` | remove | `src/core/attested-pi-run.ts:135`<br>`src/core/fusion/pi-child.ts:98` |
33
- | `OPENROUTER_BASE_URL` | remove | `src/core/attested-pi-run.ts:135`<br>`src/core/fusion/pi-child.ts:98` |
30
+ | `OPENAI_API_KEY` | remove | `src/core/attested-pi-run.ts:135`<br>`src/core/fusion/pi-child.ts:100` |
31
+ | `OPENAI_BASE_URL` | remove | `src/core/attested-pi-run.ts:135`<br>`src/core/fusion/pi-child.ts:100` |
32
+ | `OPENROUTER_API_KEY` | remove | `src/core/attested-pi-run.ts:135`<br>`src/core/fusion/pi-child.ts:100` |
33
+ | `OPENROUTER_BASE_URL` | remove | `src/core/attested-pi-run.ts:135`<br>`src/core/fusion/pi-child.ts:100` |
34
34
  | `path` | read | `src/core/common.ts:681` |
35
35
  | `Path` | read | `src/core/common.ts:681` |
36
36
  | `PATH` | read | `src/core/common.ts:681` |
37
- | `PI_API_BASE_URL` | remove | `src/core/attested-pi-run.ts:135`<br>`src/core/fusion/pi-child.ts:98` |
38
- | `PI_API_KEY` | remove | `src/core/attested-pi-run.ts:135`<br>`src/core/fusion/pi-child.ts:98` |
39
- | `PI_AUTH_FILE` | remove | `src/core/attested-pi-run.ts:135`<br>`src/core/fusion/pi-child.ts:98` |
40
- | `PI_BG_DELEGATE_ARTIFACT_DIR` | read, write | `src/core/delegate/launch.ts:353`<br>`src/delegate-child-extension.ts:279` |
41
- | `PI_BG_DELEGATE_LAUNCH_NONCE` | read, write | `src/core/delegate/launch.ts:357`<br>`src/delegate-child-extension.ts:283` |
42
- | `PI_BG_DELEGATE_SEED_PATH` | read, write | `src/core/delegate/launch.ts:354`<br>`src/delegate-child-extension.ts:280` |
43
- | `PI_BG_DELEGATE_SEED_SHA256` | read, write | `src/core/delegate/launch.ts:355`<br>`src/delegate-child-extension.ts:281` |
44
- | `PI_BG_DELEGATE_TASK_ID` | read, write | `src/core/delegate/launch.ts:356`<br>`src/delegate-child-extension.ts:282` |
37
+ | `PI_API_BASE_URL` | remove | `src/core/attested-pi-run.ts:135`<br>`src/core/fusion/pi-child.ts:100` |
38
+ | `PI_API_KEY` | remove | `src/core/attested-pi-run.ts:135`<br>`src/core/fusion/pi-child.ts:100` |
39
+ | `PI_AUTH_FILE` | remove | `src/core/attested-pi-run.ts:135`<br>`src/core/fusion/pi-child.ts:100` |
40
+ | `PI_BG_DELEGATE_ARTIFACT_DIR` | read, write | `src/core/delegate/launch.ts:353`<br>`src/delegate-child-extension.ts:384` |
41
+ | `PI_BG_DELEGATE_LAUNCH_NONCE` | read, write | `src/core/delegate/launch.ts:357`<br>`src/delegate-child-extension.ts:388` |
42
+ | `PI_BG_DELEGATE_SEED_PATH` | read, write | `src/core/delegate/launch.ts:354`<br>`src/delegate-child-extension.ts:385` |
43
+ | `PI_BG_DELEGATE_SEED_SHA256` | read, write | `src/core/delegate/launch.ts:355`<br>`src/delegate-child-extension.ts:386` |
44
+ | `PI_BG_DELEGATE_TASK_ID` | read, write | `src/core/delegate/launch.ts:356`<br>`src/delegate-child-extension.ts:387` |
45
45
  | `PI_BG_DISABLE_PI_TELEMETRY` | read | `src/core/registry.ts:195` |
46
46
  | `PI_BG_DISABLE_UPDATE_CHECK` | read | `src/extension.ts:455` |
47
47
  | `PI_BG_MAX_OUTPUT_BYTES` | read | `src/core/registry.ts:69` |
48
48
  | `PI_BG_REGISTRY_URL` | read | `src/extension.ts:464` |
49
49
  | `PI_BG_SHELL` | read | `src/core/common.ts:722` |
50
50
  | `PI_BG_SHELL_PATH` | read | `src/core/common.ts:723` |
51
- | `PI_CACHE_RETENTION` | read, write | `src/core/anthropic-attribution.ts:587`<br>`src/core/fusion/claude-cache.ts:57`<br>`src/core/fusion/pi-child.ts:271`<br>`src/core/fusion/pi-child.ts:272` |
52
- | `PI_FUSION_CANDIDATE_OUTPUT_RECOVERY_PATH` | read, remove, write | `src/core/fusion/pi-child.ts:1872`<br>`src/core/fusion/pi-child.ts:98`<br>`src/fusion-child-extension.ts:599` |
53
- | `PI_FUSION_RESEARCH_ENABLED` | read, remove, write | `src/core/fusion/pi-child.ts:1895`<br>`src/core/fusion/pi-child.ts:98`<br>`src/fusion-child-extension.ts:608` |
54
- | `PI_FUSION_SOURCE_POLICY_PATH` | read, remove, write | `src/core/fusion/pi-child.ts:1896`<br>`src/core/fusion/pi-child.ts:98`<br>`src/fusion-child-extension.ts:555` |
55
- | `PI_FUSION_SOURCE_POLICY_SHA256` | read, remove, write | `src/core/fusion/pi-child.ts:1897`<br>`src/core/fusion/pi-child.ts:98`<br>`src/fusion-child-extension.ts:556` |
56
- | `PI_FUSION_TOOL_CALL_LOG_PATH` | read, remove, write | `src/core/fusion/pi-child.ts:1884`<br>`src/core/fusion/pi-child.ts:98`<br>`src/fusion-child-extension.ts:598` |
57
- | `PI_MODEL` | remove | `src/core/delegate/launch.ts:330`<br>`src/core/fusion/pi-child.ts:98` |
51
+ | `PI_CACHE_RETENTION` | read, write | `src/core/anthropic-attribution.ts:587`<br>`src/core/fusion/claude-cache.ts:57`<br>`src/core/fusion/pi-child.ts:273`<br>`src/core/fusion/pi-child.ts:274` |
52
+ | `PI_FUSION_CANDIDATE_OUTPUT_RECOVERY_PATH` | read, remove, write | `src/core/fusion/pi-child.ts:100`<br>`src/core/fusion/pi-child.ts:1879`<br>`src/fusion-child-extension.ts:599` |
53
+ | `PI_FUSION_RESEARCH_ENABLED` | read, remove, write | `src/core/fusion/pi-child.ts:100`<br>`src/core/fusion/pi-child.ts:1902`<br>`src/fusion-child-extension.ts:608` |
54
+ | `PI_FUSION_SOURCE_POLICY_PATH` | read, remove, write | `src/core/fusion/pi-child.ts:100`<br>`src/core/fusion/pi-child.ts:1903`<br>`src/fusion-child-extension.ts:555` |
55
+ | `PI_FUSION_SOURCE_POLICY_SHA256` | read, remove, write | `src/core/fusion/pi-child.ts:100`<br>`src/core/fusion/pi-child.ts:1904`<br>`src/fusion-child-extension.ts:556` |
56
+ | `PI_FUSION_TOOL_CALL_LOG_PATH` | read, remove, write | `src/core/fusion/pi-child.ts:100`<br>`src/core/fusion/pi-child.ts:1891`<br>`src/fusion-child-extension.ts:598` |
57
+ | `PI_MODEL` | remove | `src/core/delegate/launch.ts:330`<br>`src/core/fusion/pi-child.ts:100` |
58
58
  | `PI_OFFLINE` | read | `src/extension.ts:456` |
59
- | `PI_PROVIDER` | remove | `src/core/delegate/launch.ts:330`<br>`src/core/fusion/pi-child.ts:98` |
60
- | `PI_REASONING_LEVEL` | remove | `src/core/delegate/launch.ts:330`<br>`src/core/fusion/pi-child.ts:98` |
61
- | `PI_SESSION_FILE` | remove | `src/core/delegate/launch.ts:330`<br>`src/core/fusion/pi-child.ts:98` |
62
- | `PI_SESSION_ID` | remove | `src/core/delegate/launch.ts:330`<br>`src/core/fusion/pi-child.ts:98` |
63
- | `PI_SKIP_VERSION_CHECK` | write | `src/core/delegate/launch.ts:352`<br>`src/core/fusion/pi-child.ts:270` |
59
+ | `PI_PROVIDER` | remove | `src/core/delegate/launch.ts:330`<br>`src/core/fusion/pi-child.ts:100` |
60
+ | `PI_REASONING_LEVEL` | remove | `src/core/delegate/launch.ts:330`<br>`src/core/fusion/pi-child.ts:100` |
61
+ | `PI_SESSION_FILE` | remove | `src/core/delegate/launch.ts:330`<br>`src/core/fusion/pi-child.ts:100` |
62
+ | `PI_SESSION_ID` | remove | `src/core/delegate/launch.ts:330`<br>`src/core/fusion/pi-child.ts:100` |
63
+ | `PI_SKIP_VERSION_CHECK` | write | `src/core/delegate/launch.ts:352`<br>`src/core/fusion/pi-child.ts:272` |
64
64
  | `PIPELINE_ANTHROPIC_ATTRIBUTION_AUDIT_PATH` | read | `src/core/anthropic-attribution.ts:1013` |
65
65
  | `SHELL` | read | `src/core/common.ts:718` |
66
66
  | `SystemRoot` | read | `src/core/windows-taskkill.ts:96` |
@@ -71,16 +71,14 @@ This generated registry lists production environment-variable references, runtim
71
71
  | Kind | Path/artifact | Provenance |
72
72
  | --- | --- | --- |
73
73
  | config | `fusion-models.json` | `src/core/fusion/config.ts:21` |
74
- | delegate-artifact | `budget-plan.json` | `src/core/delegate/artifacts.ts:43` |
75
- | delegate-artifact | `child-prompt.txt` | `src/core/delegate/artifacts.ts:47` |
76
- | delegate-artifact | `child.stderr.txt` | `src/core/delegate/artifacts.ts:48` |
77
- | delegate-artifact | `child.stdout.txt` | `src/core/delegate/artifacts.ts:49` |
78
- | delegate-artifact | `context-omission-ledger.json` | `src/core/delegate/artifacts.ts:42` |
74
+ | delegate-artifact | `budget-plan.json` | `src/core/delegate/artifacts.ts:44` |
75
+ | delegate-artifact | `child-prompt.txt` | `src/core/delegate/artifacts.ts:48` |
76
+ | delegate-artifact | `context-omission-ledger.json` | `src/core/delegate/artifacts.ts:43` |
79
77
  | delegate-artifact | `error.json` | `src/core/delegate/artifacts.ts:50` |
80
- | delegate-artifact | `manifest.json` | `src/core/delegate/artifacts.ts:44` |
81
- | delegate-artifact | `outcome.json` | `src/core/delegate/artifacts.ts:45` |
78
+ | delegate-artifact | `manifest.json` | `src/core/delegate/artifacts.ts:45` |
79
+ | delegate-artifact | `outcome.json` | `src/core/delegate/artifacts.ts:46` |
82
80
  | delegate-artifact | `result.json` | `src/core/delegate/result-package.ts:28` |
83
- | delegate-artifact | `seed.json` | `src/core/delegate/artifacts.ts:41` |
81
+ | delegate-artifact | `seed.json` | `src/core/delegate/artifacts.ts:42` |
84
82
  | delegate-artifact | `spill/<receipt-named-file>` | `src/core/delegate/artifacts.ts:54` |
85
83
  | directory | `.pi/delegate/<session-id>-<pid>/<task-id>/` | `src/core/delegate/artifacts.ts:160` |
86
84
  | directory | `.pi/fusion/<session-id>-<pid>/<run-id>/` | `src/core/fusion/artifacts.ts:563` |
@@ -116,17 +114,19 @@ This generated registry lists production environment-variable references, runtim
116
114
  | Schema | Provenance |
117
115
  | --- | --- |
118
116
  | `phase2.pi_task_attestation.v1` | `src/core/attested-pi-run.ts:20` |
119
- | `pi-background-tasks.delegate-budget-plan.v2` | `src/core/delegate/types.ts:21` |
120
- | `pi-background-tasks.delegate-child-terminal.v1` | `src/delegate-child-extension.ts:387` |
117
+ | `pi-background-tasks.delegate-budget-plan.v3` | `src/core/delegate/types.ts:21` |
118
+ | `pi-background-tasks.delegate-child-terminal.v1` | `src/delegate-child-extension.ts:584` |
121
119
  | `pi-background-tasks.delegate-hook-contract.v1` | `src/core/delegate/hook-contract.ts:15` |
122
120
  | `pi-background-tasks.delegate-launch.v1` | `src/delegate-extension.ts:458` |
123
121
  | `pi-background-tasks.delegate-ledger.v1` | `src/core/delegate/types.ts:16` |
124
122
  | `pi-background-tasks.delegate-manifest.v2` | `src/core/delegate/types.ts:22` |
125
- | `pi-background-tasks.delegate-outcome.v1` | `src/core/delegate/runner.ts:224` |
123
+ | `pi-background-tasks.delegate-outcome.v1` | `src/core/delegate/runner.ts:228` |
126
124
  | `pi-background-tasks.delegate-receipt.v1` | `src/core/delegate/types.ts:19` |
127
- | `pi-background-tasks.delegate-result-view.v1` | `src/delegate-extension.ts:731` |
125
+ | `pi-background-tasks.delegate-result-view.v1` | `src/delegate-extension.ts:734` |
128
126
  | `pi-background-tasks.delegate-result.v1` | `src/core/delegate/types.ts:18` |
127
+ | `pi-background-tasks.delegate-runtime-budget.v1` | `src/delegate-child-extension.ts:489` |
129
128
  | `pi-background-tasks.delegate-seed.v2` | `src/core/delegate/types.ts:15` |
129
+ | `pi-background-tasks.delegate-tool-result-content.v1` | `src/delegate-child-extension.ts:190` |
130
130
  | `pi-background-tasks.extension-request.v1` | `src/core/extension-api.ts:15` |
131
131
  | `pi-background-tasks.extension-response.v1` | `src/core/extension-api.ts:16` |
132
132
  | `pi-background-tasks.extension-terminal.v1` | `src/core/extension-api.ts:17` |
@@ -149,7 +149,7 @@ This generated registry lists production environment-variable references, runtim
149
149
  | `pi-background-tasks.fusion-merge-input.v1` | `src/core/fusion/prompts.ts:336` |
150
150
  | `pi-background-tasks.fusion-models.v1` | `src/core/fusion/types.ts:13` |
151
151
  | `pi-background-tasks.fusion-progress.v1` | `src/fusion-extension.ts:59` |
152
- | `pi-background-tasks.fusion-result-view.v1` | `src/delegate-extension.ts:772` |
152
+ | `pi-background-tasks.fusion-result-view.v1` | `src/delegate-extension.ts:775` |
153
153
  | `pi-background-tasks.fusion-result.v4` | `src/core/fusion/types.ts:19` |
154
154
  | `pi-background-tasks.fusion-result.v5` | `src/core/fusion/types.ts:20` |
155
155
  | `pi-background-tasks.fusion-runtime-guard.v2` | `src/core/fusion/child-protocol.ts:24` |
@@ -82,43 +82,52 @@ Routes are pinned once:
82
82
  - unavailable/unknown-capacity routes fail;
83
83
  - no substitution, fallback, or retry on a different route.
84
84
 
85
- Budgets use a delegate-specific conservative estimator. Constants currently documented by source/tests:
85
+ Budgeting separates admission from package-owned runtime growth. Constants currently documented by source/tests:
86
86
 
87
87
  - reserved output: `16,384` tokens;
88
88
  - framing reserve: `8,192` tokens;
89
89
  - safety reserve: `4,096` tokens;
90
90
  - minimum usable input: `8,192` tokens;
91
+ - protected finalization input runway: `32,768` tokens;
92
+ - finalization trigger inside retained-growth runway: `8,192` tokens;
91
93
  - default turns/tools/timeout: `24` / `120` / `1200s`;
92
94
  - per-result transcript cap: `64 KiB`;
93
- - aggregate tool-output cap: `64 MiB`;
94
- - answer capture cap value carried in the seed: `4 MiB`; current child code does not separately enforce this before packaging;
95
+ - aggregate raw tool-output cap: `64 MiB`;
96
+ - answer capture cap: `4 MiB`, enforced before result packaging;
95
97
  - inline answer cap: `48 KiB`.
96
98
 
97
- Launch admission measures the child system prompt plus the actual child prompt carrying the seed. Runtime guard measures retained input before every model call. An over-budget runtime call latches `provider_context_budget_exhausted`, aborts the run, and suppresses outgoing content.
99
+ Launch admission measures the child system prompt plus the exact child prompt carrying the seed. Backed large prompts use the shared family calibration; prompts or routes below the calibration domain and unknown/unbacked routes use the provable `1.00 B/token` profile. `budget-plan.json` v3 also records the provable conservative counter-forecast, protected finalization reserve, and retained-growth budget.
100
+
101
+ After launch, token measurements are advisory. Fusion BUG-185 proved that a package-local estimate must not reject a live provider payload after subtracting hypothetical output. Delegate therefore does not self-report provider exhaustion from that estimate. Pi and the provider own live context handling; a genuine provider context error remains loud. Package-owned growth is controlled before transcript entry: a tool result spills whenever it exceeds the per-result cap **or** retaining it would consume protected final-answer runway. Conservative false positives therefore create explicit hash receipts rather than failed tasks. Near the end of the runway the child disables tools and injects one finalization instruction so it can answer from evidence already gathered.
98
102
 
99
103
  ## Child guard and commit discipline
100
104
 
101
105
  The child verifies seed hash, task id, and launch nonce at extension load before the first model call. It then enforces:
102
106
 
103
- - context budget before every provider call;
104
- - per-result spill receipts before tool output enters the transcript;
105
- - aggregate tool-output cap;
107
+ - advisory retained-context measurement before every provider call, without a BUG-185-style token abort;
108
+ - route-runway-aware and per-result spill receipts before tool output enters the transcript, including structured preservation of image-bearing results;
109
+ - bounded artifact range reads returned as lossless base64 against remaining inline runway;
110
+ - aggregate raw tool-output cap;
111
+ - protected no-tool finalization when retained-growth runway becomes low;
106
112
  - turn and tool-call limits;
107
113
  - route attestation for assistant messages;
108
- - complete usage records only (missing/partial usage is `unavailable`, never zero);
114
+ - per-turn usage accumulation across the full agent loop; if any turn is missing/partial, aggregate usage is `unavailable` rather than understated or replaced by a later record;
109
115
  - accepted final stop reason `stop` only, so provider `length` stops become `child_model_output_limit` rather than partial success;
116
+ - answer capture from only the final clean-stop assistant message, never intermediate tool-use narration;
110
117
  - non-empty, non-whitespace answer text;
111
- - well-formed UTF-8 answer blocks.
118
+ - the declared answer capture cap, with no partial result on overflow;
119
+ - well-formed UTF-8 answer blocks;
120
+ - durable `runtime-budget.json` evidence containing context measurements, retained/spilled bytes, finalization state, first-request observed usage, and calibration-underforecast evidence.
112
121
 
113
122
  A terminal latch prevents later success commit after any degraded/refused condition. This avoids a hash-valid result built on silently modified context.
114
123
 
115
124
  `result.json` is the single answer data plane. It is child-written by temp file, file fsync, and rename; POSIX then fsyncs the parent directory, while Windows skips directory fsync because Node does not provide the same portable guarantee there. Final-name presence is the child commit point. No final `result.json` means no accepted answer, regardless of process exit code. `child-terminal.json` records child-side terminal failures when no success package is committed.
116
125
 
117
- After adjudication, the parent makes a best-effort durable write of `outcome.json`. This is separate from `result.json` so child and parent cannot race over one state field. An `outcome.json` write failure is currently ignored and does not change the returned adjudication, so the artifact may be absent even though evaluation completed. Child stdout/stderr are currently captured in the background task output file; although delegate artifact constants name `child.stdout.txt` and `child.stderr.txt`, current registry finalization does not populate those files in the delegate artifact directory.
126
+ After adjudication, the parent makes a best-effort durable write of `outcome.json`. This is separate from `result.json` so child and parent cannot race over one state field. An `outcome.json` write failure is currently ignored and does not change the returned adjudication, so the artifact may be absent even though evaluation completed. Child stdout/stderr are captured in the background task output file. Delegate-local `child.stdout.txt` and `child.stderr.txt` are not currently populated, and terminal reporting now lists only diagnostic paths that actually exist.
118
127
 
119
128
  ## Spill artifacts and `delegate_read_artifact`
120
129
 
121
- Oversized tool results are durably written in full under `spill/` and replaced with receipts. A failed spill withholds the original payload and latches a terminal failure; no uncommitted artifact is claimed by receipt.
130
+ Oversized tool results are durably written in full under `spill/` and replaced with receipts. A single text block is stored as exact UTF-8; malformed lone-surrogate text is rejected rather than silently converted to U+FFFD. Multi-block or image-bearing content is stored in a closed JSON envelope that preserves block boundaries, MIME types, text, and complete base64 image data. New receipts record `content_format`; historical v1 receipts without that optional field remain readable. A failed spill withholds the original payload and latches a terminal failure; no uncommitted artifact is claimed by receipt.
122
131
 
123
132
  `delegate_read_artifact` requires:
124
133
 
@@ -126,7 +135,7 @@ Oversized tool results are durably written in full under `spill/` and replaced w
126
135
  - `offset: non-negative safe integer`;
127
136
  - `length: positive safe integer`.
128
137
 
129
- It reads the whole artifact file, verifies the requested range is in bounds, and returns exactly that UTF-8-decoded range. Path escape and short reads fail loudly.
138
+ It reads the whole artifact file, verifies the requested range is in bounds, and returns the exact bytes as base64 plus offset/length metadata. Arbitrary ranges are never decoded as UTF-8, so a range that splits a multibyte sequence remains byte-exact rather than becoming U+FFFD. Path escape and short reads fail loudly.
130
139
 
131
140
  ## Retrieval contract
132
141
 
@@ -163,7 +172,7 @@ Launch / execution:
163
172
 
164
173
  Budget / limits:
165
174
 
166
- - `provider_context_budget_exhausted`
175
+ - `provider_context_budget_exhausted` (legacy terminal records only; current children do not infer provider exhaustion from an advisory estimate)
167
176
  - `aggregate_tool_output_cap`
168
177
  - `child_model_output_limit`
169
178
  - `child_capture_limit`