pi-background-tasks 2.4.0 → 2.5.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/README.md CHANGED
@@ -27,7 +27,7 @@
27
27
  | Fact | Value |
28
28
  | --- | --- |
29
29
  | Package | `pi-background-tasks` |
30
- | Version | `2.4.0` |
30
+ | Version | `2.5.0` |
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) |
package/TESTING.md CHANGED
@@ -196,7 +196,7 @@ npm run test:rpc
196
196
  npm run test:agent-loop
197
197
  ```
198
198
 
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, 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.
200
200
 
201
201
  ## Coverage summary
202
202
 
@@ -204,7 +204,7 @@ Implemented coverage includes:
204
204
 
205
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
206
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
207
- - 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
208
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
209
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
210
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
@@ -77,16 +77,16 @@ SDK/RPC/scripted-provider/package/compatibility coverage asserts exactly four to
77
77
  | Stop task from LLM tool | `bg_kill` | | yes | | | | | | Covers running kill and already-finished loud failure. |
78
78
  | Fusion command background launch | `/fusion`, managed task, terminal notification, `bg_result` | yes | yes | yes | | yes | yes | | Core unit covers deterministic projection, child argv/stdin/metadata parsing, artifacts, pre-abort handling, and orchestration. SDK verifies `/fusion` returns after durable preflight, all five child invocations continue in the managed task, terminal notification is emitted without a parent rewrite, editor/cancel behavior remains correct, and malformed config launches zero children. RPC verifies command discovery, Unicode request preservation, background terminal delivery, no parent `agent_start`, editor protocol, malformed-config admission failure, child failure notification, and child isolation flags. |
79
79
  | Fusion v1 background result | `fusion_reason`, `fusion_investigate`, `fusion_research`, `fusion_validate`, `bg_result` | yes | yes | | | current-host stats/replay | yes | yes | Exactly four workflow tools remain registered; each returns a managed-task receipt after durable no-child preflight. `bg_result` verifies manifest-bound `result.json`/`merged.md`, never truncates, and attaches complete `Usage` exactly once. Failed/cancelled retrieval returns `delivery:"none"` and a closed no-answer view backed only by verified, bounded `failure-summary.json` metadata/refs; it cannot claim usage or expose partial text. SDK pins non-blocking launch under delayed children, tool-signal ownership handoff, clean-context isolation, failure coordinates, shutdown cancellation, and repeated-retrieval no-double-counting. Scripted-provider coverage proves no polling between launch and terminal wake. |
80
- | Global Anthropic attribution and sanitization | package-owned `extensions/anthropic-attribution.ts`, `/claude-cache`, isolated child `--extension` argv | yes | | | | | yes | | Package discovery loads attribution before background tasks for every installation. Unit pins all three exact SPS-derived sanitization variants, non-Anthropic non-mutation, provider/session/account/device metadata, beta-resource transport, cache surfaces, one-hour pricing, the 200K subscription contract, and EventBus duplicate-owner suppression. Fusion Anthropic routes receive exactly two explicit extensions in order: global attribution/sanitization, then runtime governor. Delegate and attested Anthropic routes also load attribution explicitly because attribution cannot rely on ambient discovery; delegates do so in both isolated and ambient modes. Non-Anthropic argv does not resolve or add attribution. Missing bytes fail loudly; the package has no exotic/URL sanitizer dependency. The independent repo-local spawn extension is byte-parity tested but imports nothing from this package. |
80
+ | Global Anthropic attribution and sanitization | package-owned `extensions/anthropic-attribution.ts`, `/claude-cache`, isolated child `--extension` argv | yes | | | | | yes | local SSE + subscription handoff | Package discovery loads attribution before background tasks for every installation. Unit pins all three exact SPS-derived sanitization variants, non-Anthropic non-mutation, provider/session/account/device metadata, beta-resource transport, cache surfaces, one-hour pricing, the 200K subscription contract, and EventBus duplicate-owner suppression. BUG-192 coverage pins provider/api/model-aware Codex/ZAI signature removal, foreign-redacted omission, lineage-proven empty/redacted/non-BMP Claude replay, directional older-Claude→Fable 5.1 compatibility, long→short→long signature-epoch isolation plus stale-prefix/reverse denial, canonical collision-checked tool IDs, stable text-tool-result prefixes, deterministic repeated payloads, official Fable binding/cache-diagnostics betas, fail-before-network profile drift, one-shot hash-bound compaction epochs, one-hour controls, away/back model lanes, exact official HTTPS endpoint/no-redirect policy, strict error/EOF/`message_stop` SSE completion, and official $0.25/M cache-read accounting. BUG-193 coverage proves hookless compaction/branch-summary requests derive mandatory attribution from Pi's fresh request-scoped `options.sessionId`, retain zero cache markers, isolate concurrent IDs, expose an already-attributed payload to optional middleware exactly once, preserve a frozen ordinary-turn payload hash, and reject route, metadata, system-identity, cache-topology, and excess-breakpoint tampering before fetch. The stubbed-fetch no-network loop exercises the real custom transport without weakening its production endpoint policy; release evidence uses subscription OAuth only. Fusion Anthropic routes receive exactly two explicit extensions in order: global attribution/sanitization, then runtime governor. Delegate and attested Anthropic routes also load attribution explicitly because attribution cannot rely on ambient discovery; delegates do so in both isolated and ambient modes. Non-Anthropic argv does not resolve or add attribution. Missing bytes fail loudly; the package has no exotic/URL sanitizer dependency. The independent repo-local spawn extension is byte-parity tested but imports nothing from this package. |
81
81
  | Fusion validation workflow | structured `fusion_validate`, workflow profiles, `fusion-manifest.v4`, `fusion-result.v5` | yes | yes | | | | yes | | Public validation rejects legacy `{prompt}` with a migration error, enforces non-empty `scope`/`acceptanceCriteria`, and loudly validates `verification` cross-fields (`provided` ↔ evidence, `not_run` ↔ reason). Core validate orchestration is clean/read-only/advisory, enforces source-finding accounting including singleton, duplicate, exclusion, and merger add/drop cases, and remains no build/test substitute claim. |
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. |
@@ -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
@@ -132,7 +132,7 @@ Generated navigation for every package-local documentation page. This index inte
132
132
  | command | `bg-clear` | `command:bg-clear` | `src/extension.ts:559` |
133
133
  | command | `bg-tasks` | `command:bg-tasks` | `src/extension.ts:551` |
134
134
  | command | `bg-update` | `command:bg-update` | `src/extension.ts:567` |
135
- | command | `claude-cache` | `command:claude-cache` | `src/core/anthropic-attribution.ts:1928` |
135
+ | command | `claude-cache` | `command:claude-cache` | `src/core/anthropic-attribution.ts:3026` |
136
136
  | command | `fusion` | `command:fusion` | `src/fusion-extension.ts:996` |
137
137
  | command | `fusion-models` | `command:fusion-models` | `src/fusion-extension.ts:1029` |
138
138
  | command | `jobs` | `command:jobs` | `src/extension.ts:605` |
@@ -12,7 +12,7 @@ covers_sources: []
12
12
  <!-- pi-docs:begin name="command-contract-claude-cache" generator="scripts/docs/generate.mjs" -->
13
13
  | Command | Description | Provenance |
14
14
  | --- | --- | --- |
15
- | `/claude-cache` | Show or set Claude cache retention for this session (short, long, default) | `src/core/anthropic-attribution.ts:1928` |
15
+ | `/claude-cache` | Show or set Claude cache retention for this session (short, long, default) | `src/core/anthropic-attribution.ts:3026` |
16
16
  <!-- pi-docs:end name="command-contract-claude-cache" -->
17
17
 
18
18
  Show or change the Anthropic cache-retention preference for the current session.
@@ -32,11 +32,11 @@ Show or change the Anthropic cache-retention preference for the current session.
32
32
  - No argument and `status` show the effective preference.
33
33
  - `short` requests normal ephemeral retention.
34
34
  - `long` requests one-hour retention where the selected model supports it.
35
- - `default` removes the session override and returns to process/package policy.
35
+ - `default` removes the session override and returns to the package's one-hour subscription policy unless `PI_CACHE_RETENTION` says otherwise.
36
36
 
37
37
  The decision is persisted as a branch-local custom session entry and restored after reload, resume, and tree navigation. It does not enter model context.
38
38
 
39
- An explicit call-level cache posture remains authoritative. In particular, Pi compaction calls that request no cache markers are not re-marked by the session default.
39
+ Pi's generic five-minute provider default does not override this package's one-hour subscription policy. Use `/claude-cache short` or `PI_CACHE_RETENTION=short` for an intentional short lane. An explicit call-level `none` remains authoritative, so Pi compaction and branch-summary requests are not re-marked by the session default. Those standalone requests still receive complete subscription attribution from Pi's fresh request-scoped routing ID.
40
40
 
41
41
  ## Errors and boundaries
42
42
 
@@ -26,7 +26,7 @@
26
26
  "state": "stale-authored-prose"
27
27
  },
28
28
  {
29
- "authored_body_sha256": "sha256:72c53dcc104f9b5efa57e38bcb21a4c3c620d5caf1571af82348f94dcafbe941",
29
+ "authored_body_sha256": "sha256:d74e15a5d9c67a6602e341a68a08d8ed06edb0769cc114e689d5702ebab2b575",
30
30
  "covers_sources": [
31
31
  "extensions/anthropic-attribution.ts",
32
32
  "src/core/anthropic-attribution-path.ts",
@@ -96,7 +96,7 @@
96
96
  "state": "stale-authored-prose"
97
97
  },
98
98
  {
99
- "authored_body_sha256": "sha256:b6238bacecf46139dab1d6a8622783db76d913f0ce727205151b65b7cb210481",
99
+ "authored_body_sha256": "sha256:cd882ae075d47e97e2fb45709d7ac044811eedb68e2377b1742ac44147297aa4",
100
100
  "covers_sources": [
101
101
  "extensions/fusion-child.ts",
102
102
  "src/core/fusion/artifacts.ts",
@@ -832,7 +832,7 @@
832
832
  "image": "https://raw.githubusercontent.com/ismailsaleekh/pi-background-tasks/main/logo.png",
833
833
  "name": "pi-background-tasks",
834
834
  "type": "module",
835
- "version": "2.4.0"
835
+ "version": "2.5.0"
836
836
  },
837
837
  "public_surface_ids": [
838
838
  "command:bg",
@@ -902,7 +902,7 @@
902
902
  "id": "command:claude-cache",
903
903
  "kind": "command",
904
904
  "name": "claude-cache",
905
- "source": "src/core/anthropic-attribution.ts:1928"
905
+ "source": "src/core/anthropic-attribution.ts:3026"
906
906
  },
907
907
  {
908
908
  "description": "Start fixed-purpose Fusion reason in the background and return immediately.",
@@ -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
 
@@ -31,10 +31,10 @@ Start from the symptom, verify the source-owned doc, then apply the remediation.
31
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. |
32
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. |
33
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. |
34
- | `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. |
35
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. |
36
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. |
37
- | 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. |
38
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. |
39
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. |
40
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,27 +16,27 @@ 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` |
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
40
  | `PI_BG_DELEGATE_ARTIFACT_DIR` | read, write | `src/core/delegate/launch.ts:353`<br>`src/delegate-child-extension.ts:384` |
41
41
  | `PI_BG_DELEGATE_LAUNCH_NONCE` | read, write | `src/core/delegate/launch.ts:357`<br>`src/delegate-child-extension.ts:388` |
42
42
  | `PI_BG_DELEGATE_SEED_PATH` | read, write | `src/core/delegate/launch.ts:354`<br>`src/delegate-child-extension.ts:385` |
@@ -48,20 +48,20 @@ This generated registry lists production environment-variable references, runtim
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:635`<br>`src/core/anthropic-attribution.ts:646`<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` |
64
- | `PIPELINE_ANTHROPIC_ATTRIBUTION_AUDIT_PATH` | read | `src/core/anthropic-attribution.ts:1013` |
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
+ | `PIPELINE_ANTHROPIC_ATTRIBUTION_AUDIT_PATH` | read | `src/core/anthropic-attribution.ts:1124` |
65
65
  | `SHELL` | read | `src/core/common.ts:718` |
66
66
  | `SystemRoot` | read | `src/core/windows-taskkill.ts:96` |
67
67
  | `WINDIR` | read | `src/core/windows-taskkill.ts:101` |
@@ -15,17 +15,27 @@ This subsystem owns the package-wide Anthropic subscription attribution provider
15
15
 
16
16
  `package.json.pi.extensions` loads `extensions/anthropic-attribution.ts` for every normal `pi-background-tasks` installation, before the background-task entrypoint. The extension is provider-gated: non-Anthropic sessions and payloads are unchanged.
17
17
 
18
- For Anthropic sessions it registers the package-owned `anthropic` provider transport and applies the Claude Code subscription request contract:
18
+ For Anthropic sessions it registers the package-owned `anthropic` provider transport. Mandatory attribution is owned inside that transport from each request's Pi-supplied `options.sessionId`; `before_provider_request` remains optional middleware and is never an identity initializer. The transport applies the Claude Code subscription request contract:
19
19
 
20
- - subscription OAuth token transport only; metered Anthropic credentials are refused;
20
+ - subscription OAuth token transport only; metered Anthropic credentials are refused, the bearer token is sent only to the exact official Anthropic HTTPS origin, and HTTP redirects are disabled;
21
21
  - Claude Code session, account, device, beta, user-agent, and system-identity attribution;
22
22
  - model-specific fixed/adaptive thinking policy;
23
23
  - the conservative 200K subscription context policy;
24
+ - provenance-aware cross-provider history projection and Fable 5.1 thinking binding;
24
25
  - system, final-tool, and final-conversation cache surfaces;
25
- - provider-authoritative usage and one-hour cache-write accounting when reported.
26
+ - provider-authoritative usage, cache diagnostics, and one-hour cache-write accounting when reported;
27
+ - strict SSE completion: matching event names, one `message_start`, closed content blocks, a recognized terminal stop reason, and one `message_stop` are required before success or lineage persistence.
26
28
 
27
29
  The extension reads `userID` and `oauthAccount.accountUuid` from `~/.claude.json` without writing it. Missing/malformed account data, unsupported model policy, malformed payload/cache controls, and non-OAuth transport fail loudly.
28
30
 
31
+ ## Cross-provider history and cache lineage
32
+
33
+ Assistant messages carry their producing `provider`, `api`, and `model`. The transport never parses an opaque reasoning signature. Foreign visible thinking is projected deterministically as text; foreign opaque, redacted, and signature-only blocks are omitted. Claude thinking is replayed only when a successful direct-Anthropic response carries a matching `anthropic-cache-lineage` diagnostic binding response ID, source tuple, assistant-content hash, system/tools attribution profile, effective cache retention, request-message count, and request-prefix hash. Empty, redacted, and valid non-BMP Unicode Claude blocks are preserved byte-for-byte. Fable 5.1 accepts lineage-proven earlier Claude blocks; reverse replay is denied.
34
+
35
+ Every target model has an independent append-only lane. Before transport, the adapter proves the prior successful wire history remains an exact prefix and that model, sanitized system, canonical tools, thinking/effort, beta profile, and effective retention are unchanged. Unexpected drift fails before network. An intentional TTL change starts a cryptographically named signature epoch and suppresses prior-epoch signed thinking permanently, including after a later short→long return. A canonical leading Pi compaction summary likewise opens one hash-bound signature epoch only; retaining that old marker cannot excuse later unrelated history drift. Tool IDs, schemas, arguments, user messages, and text-only tool results have deterministic block-shaped serialization, so advancing the final cache marker does not rewrite prior content. Optional payload middleware runs exactly once after transport-owned attribution and cannot change the protected model/stream route, account/device/session metadata, billing identity, cache-control placement/value topology, four-breakpoint limit, or already-authorized message/static/profile/retention lineage.
36
+
37
+ Fable 5.1 always sends `thinking-binding-controls-2026-08-01` with prefix mismatch set to `error`, plus `cache-diagnosis-2026-04-07`. The previous successful response ID is chained within the same model lane; provider diagnostics and `input_transformations` are persisted outside model context. There is no automatic signature retry or silent thinking drop.
38
+
29
39
  ## Sanitization
30
40
 
31
41
  The package has no runtime dependency on `@ravshansbox/pi-anthropic-sps`. Its three reviewed exact-match prompt-line rules are implemented locally in `src/core/anthropic-attribution.ts`, with the upstream MIT notice retained in `THIRD_PARTY_NOTICES.md`.
@@ -52,7 +62,7 @@ Arbitrary shell commands started through `bg_run` are not rewritten. An Anthropi
52
62
 
53
63
  ## Cache retention
54
64
 
55
- `PI_CACHE_RETENTION=none|short|long` selects process/provider policy. `/claude-cache status|short|long|default` stores a branch-local session override as a custom entry that does not enter model context. Call-level `cacheRetention` remains highest precedence, notably preserving Pi's compaction opt-out.
65
+ `PI_CACHE_RETENTION=none|short|long` selects process/provider policy. `/claude-cache status|short|long|default` stores a branch-local session override as a custom entry that does not enter model context. Registered subscription sessions default to one hour even when Pi supplies its generic five-minute provider default; an intentional short policy must come from the session command or environment. Call-level `cacheRetention:none` remains authoritative for one-off compaction and branch-summary requests and emits no cache markers. Pi gives those standalone requests a fresh routing `options.sessionId`; that exact ID is used consistently in metadata, headers, and request-local lineage without coupling the one-off request to the parent cache lane.
56
66
 
57
67
  ## Related docs
58
68
 
@@ -12,7 +12,7 @@ covers_sources: []
12
12
  This authored section defines the boundary: documentation facts are extracted from package metadata and TypeScript ASTs, then generated into docs and the manifest. Unsupported syntax fails the gate rather than falling back to regex or stale hand-maintained inventories. Public registrations must remain unconditional top-level direct calls or use the one validated local tool-wrapper shape; host/method aliases, computed access, nested or conditional registration, wrapper chaining/passing, constructor helpers, ambiguous public metadata, destructured Pi parameters, and repeated imported registrars are rejected.
13
13
 
14
14
  <!-- pi-docs:begin name="docs-freshness-gate" generator="scripts/docs/generate.mjs" -->
15
- - Canonical package version: `2.4.0`
15
+ - Canonical package version: `2.5.0`
16
16
  - Governed markdown docs: 42
17
17
  - Public surfaces extracted: 31
18
18
  - Governed production sources: 50
@@ -80,11 +80,11 @@ Evaluator, evaluator-repair, and merger always use capability `reason` and empty
80
80
 
81
81
  Research is targeted fetch, not search. The public caller declares exact non-duplicate public `http(s)` URLs and purposes. There is no browser, PDF reader, cache, search provider, page-recrawl loop, or domain allowlist.
82
82
 
83
- `fusion_web_fetch` is private to research children and has a closed `{url, extract?}` schema. It rejects credentials, non-http schemes, localhost/known-metadata names, and enumerated private/reserved address classes; vets all DNS answers against that classifier; pins the request to a vetted address; checks the response socket address; follows at most five re-vetted redirects; accepts only HTML/XHTML/plain text/Markdown; caps response bytes at 2 MiB and extracted output at 32 KiB; uses a 60 second deadline; strips script/style/noscript; and extracts text or Markdown. Source-policy admission also rejects literal Azure service address `168.63.129.16`, but the transport classifier does not currently special-case a public DNS/redirect target resolving to that address.
83
+ `fusion_web_fetch` is private to research children and has a closed `{url, extract?}` schema. It rejects credentials, non-http schemes, localhost/known-metadata names, and enumerated private/reserved address classes; vets all DNS answers against that classifier; pins the request to a vetted address; checks the response socket address; follows at most five re-vetted redirects; accepts only HTML/XHTML/plain text/Markdown; caps response bytes at 4 MiB and extracted output at 32 KiB; uses one 90 second full-operation deadline across DNS, redirects, response transfer, and extraction; strips script/style/noscript; and extracts text or Markdown. Source-policy admission also rejects literal Azure service address `168.63.129.16`, but the transport classifier does not currently special-case a public DNS/redirect target resolving to that address.
84
84
 
85
85
  Research intentionally combines read-only file tools and network fetch in one child. This supports source-backed synthesis but is security-sensitive: operators must not supply secret-bearing URLs or ask children to put private data in URL strings. The package blocks common SSRF targets and credential URLs, but its deny rules are not an exhaustive network sandbox; fetched content remains untrusted and caller-declared public URLs can still disclose access through remote logs/timing.
86
86
 
87
- Inspect/research candidates write sealed tool-call audit logs. The log contains schema version, ordinal, tool name, argument/result byte counts and SHA-256 digests, status, duration, and fetch provenance. Raw arguments, raw results, page content, and rejected raw URLs are not persisted. The parent requires the log and seal, verifies hashes/counts/ordinals/status, enforces the 8 MiB aggregate result-byte cap, and rejects non-allowlisted tools. A child may attempt at most 192 tool calls; crossing that limit aborts the run, emits structured refusal evidence, and prevents a complete audit seal.
87
+ Inspect/research candidates write sealed tool-call audit logs. The log contains schema version, ordinal, tool name, argument/result byte counts and SHA-256 digests, status, duration, and fetch provenance. Raw arguments, raw results, page content, and rejected raw URLs are not persisted. The parent requires the log and seal, verifies hashes/counts/ordinals/status, independently enforces both the 600-call and 32 MiB aggregate result-byte caps, and rejects non-allowlisted tools. A child may attempt at most 600 tool calls; crossing that limit aborts the run, emits structured refusal evidence, and prevents a complete audit seal.
88
88
 
89
89
  ## Child process isolation
90
90
 
@@ -92,7 +92,7 @@ Fusion never calls direct completion APIs. It launches direct child `pi --mode t
92
92
 
93
93
  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
94
 
95
- After cache normalization, the governor validates a stable JSON-object serialization and enforces the 128-provider-request execution limit. It does not estimate live payload tokens, subtract possible model output from the context window, or reject a request by payload size; Pi and the provider own live context handling after the pre-spawn Fusion stage checks. A provider-side context rejection remains a loud child failure and is never route-substituted or hidden. Pi's provider-hook behavior is characterized through the same `openai-codex-responses` transport adapter used by subscription Codex routes in a real local HTTP agent loop: transforms chain in extension load order and `ctx.abort()` prevents transport for the retained cache-policy and execution-limit refusals. Cache observations use `pi-background-tasks.fusion-claude-cache-observation.v1`, state requested/effective retention, source, breakpoint count, and provider-request ordinal, and are hash-bound inside `pi-background-tasks.fusion-child-result.v4` attempt event artifacts. At terminal `agent_settled`, the extension emits exactly one `pi-background-tasks.fusion-child-settlement.v3` frame binding the complete ordered metadata stream by count and SHA-256, the final record/hash, recovered retry-marker ordinals, and any one recovered oversized-original ordinal. The parent validates closed cache/output-contract evidence and increasing request ordinals, reconstructs stdout against the final metadata, requires final stop reason `stop`, verifies model identity, and preserves usage/cost exactly. Non-final `toolUse` records remain normal. A non-final `error` is accepted only when it is a zero-content, empty-hash, zero-usage retry marker, a later final `stop` exists, and the terminal settlement hash/accounts for that exact ordinal. Exactly one non-final `stop` is accepted only as a hash-bound oversized candidate original immediately followed by its same-session replacement. `length`, `aborted`, `pending`, final `error`, error records carrying text or usage, unbound non-final `stop`, missing/duplicate/tampered settlement, and settlement before terminal idleness all fail loudly.
95
+ After cache normalization, the governor validates a stable JSON-object serialization and enforces the 550-provider-request execution limit. It does not estimate live payload tokens, subtract possible model output from the context window, or reject a request by payload size; Pi and the provider own live context handling after the pre-spawn Fusion stage checks. A provider-side context rejection remains a loud child failure and is never route-substituted or hidden. Pi's provider-hook behavior is characterized through the same `openai-codex-responses` transport adapter used by subscription Codex routes in a real local HTTP agent loop: transforms chain in extension load order and `ctx.abort()` prevents transport for the retained cache-policy and execution-limit refusals. Cache observations use `pi-background-tasks.fusion-claude-cache-observation.v1`, state requested/effective retention, source, breakpoint count, and provider-request ordinal, and are hash-bound inside `pi-background-tasks.fusion-child-result.v4` attempt event artifacts. At terminal `agent_settled`, the extension emits exactly one `pi-background-tasks.fusion-child-settlement.v3` frame binding the complete ordered metadata stream by count and SHA-256, the final record/hash, recovered retry-marker ordinals, and any one recovered oversized-original ordinal. The parent validates closed cache/output-contract evidence and increasing request ordinals, reconstructs stdout against the final metadata, requires final stop reason `stop`, verifies model identity, and preserves usage/cost exactly. Non-final `toolUse` records remain normal. A non-final `error` is accepted only when it is a zero-content, empty-hash, zero-usage retry marker, a later final `stop` exists, and the terminal settlement hash/accounts for that exact ordinal. Exactly one non-final `stop` is accepted only as a hash-bound oversized candidate original immediately followed by its same-session replacement. `length`, `aborted`, `pending`, final `error`, error records carrying text or usage, unbound non-final `stop`, missing/duplicate/tampered settlement, and settlement before terminal idleness all fail loudly.
96
96
 
97
97
  Fusion child environments strip session/model/provider variables plus metered credential/base-url variables for OpenRouter, OpenAI, Anthropic, Azure OpenAI, and generic Pi API credentials before launch. Frontier model routes are admitted only when the registry reports subscription OAuth for trusted `anthropic` or `openai-codex` endpoints. There is no fallback, model substitution, endpoint override, or metered API-key route.
98
98
 
@@ -102,7 +102,7 @@ Budget planning is per route and per stage. Every configured candidate, evaluato
102
102
 
103
103
  `budget-plan.json` uses `pi-background-tasks.fusion-budget-plan.v4` and records route capacities, stage forecasts for candidate/evaluation/evaluation-repair/merge, conditional repair reservation, warnings, blockers, empty-request counterfactuals, and remediation. Each route reserves the larger of Fusion's 32,768-token output contract reserve and the resolved model's declared maximum output; a model advertising a 128,000-token maximum therefore receives the full 128,000-token reserve. Fatal preflight blockers launch zero children. High utilization or worst-case reservation pressure is a warning when input still fits. Exact rendered prompt checks happen again immediately before candidate, evaluation, repair, and merge launches.
104
104
 
105
- Every candidate system prompt discloses the exact 49,152 JSON-rendered UTF-8 byte hard maximum and requires explicit limitations when the requested scope cannot fit. Output contracts are checked after durable attempt recording: candidate responses up to 48 KiB JSON-rendered bytes, evaluator up to 64 KiB, merger/final report up to 64 KiB, diagnostics contract 8 KiB, child stdout cap 32 MiB, child stderr cap 4 MiB.
105
+ Every candidate system prompt discloses the exact 49,152 JSON-rendered UTF-8 byte hard maximum and requires explicit limitations when the requested scope cannot fit. Output contracts are checked after durable attempt recording: candidate responses up to 48 KiB JSON-rendered bytes, evaluator up to 64 KiB, merger/final report up to 64 KiB, diagnostics contract 8 KiB, child stdout cap 32 MiB, child stderr cap 16 MiB.
106
106
 
107
107
  When a candidate's first complete `stop` response exceeds 48 KiB, the private child extension durably preserves that full original, removes all active tools, and queues one `followUp` user message before `agent_settled`. The same live Pi process, route, model, and in-memory conversation may only compress/restructure its immediately previous answer; it may not investigate again. Pi text mode then emits only the replacement to stdout. A conforming replacement proceeds normally. A second oversized response hard-fails as `child_output_cap`, preserves both the original artifact and replacement partial response, and never queues another continuation. Cancellation during compression preserves any hash-verified original already written. Fusion never clips, silently forwards, or repairs output in a new child session.
108
108
 
@@ -120,7 +120,7 @@ For tool-enabled children, the private audit journal remains open across every l
120
120
 
121
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.
122
122
 
123
- 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 30 minute wall timeout, 20 minute idle watchdog, SIGTERM grace, SIGKILL wait, process-group kill on POSIX, bounded stdout/stderr, and cleanup-error propagation.
123
+ 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
124
 
125
125
  ## Troubleshooting
126
126
 
@@ -129,7 +129,7 @@ Cancellation and shutdown are loud and durable when a run store exists. The exte
129
129
  - Frontier/API route rejected: use Pi Anthropic or Codex subscription OAuth, not OpenAI/OpenRouter/Azure/API-key routes.
130
130
  - `prompt_budget_exceeded_forecast`: inspect `budget-plan.json`; the error says whether shortening the request can help or whether session history/scope/model context window is the blocker.
131
131
  - `prompt_budget_exceeded_measured`: an exact rendered prompt exceeded capacity after upstream output was known; split the workflow or choose a larger-context subscription route.
132
- - `child_runtime_limit_exceeded`: a child crossed the 128-provider-request or 192-tool-call execution limit. Inspect the failed tool seal and split an unbounded task rather than raising limits blindly.
132
+ - `child_runtime_limit_exceeded`: a child crossed the 550-provider-request or 600-tool-call execution limit. Inspect the failed tool seal and split an unbounded task rather than raising limits blindly.
133
133
  - `child_runtime_payload_invalid`: the final provider payload could not be normalized as one stable JSON object. Fix the payload/provider integration; no fallback serialization is attempted.
134
134
  - `child_cache_policy_invalid`: `PI_CACHE_RETENTION` or Claude cache-control evidence was malformed. Use exactly `none`, `short`, or `long`; do not bypass cache-policy validation.
135
135
  - `child_output_cap` after candidate compression: inspect the final candidate response, its `response.oversized.*` original, and v4/v3 child protocol evidence. The single same-session no-tool compression attempt also exceeded 49,152 JSON-rendered bytes; split the task rather than truncating or starting a repair child.
@@ -147,8 +147,8 @@ The private `fusion_web_fetch` schema is closed: `{url, extract?: 'text'|'markdo
147
147
  - vets every DNS answer against the transport classifier, pins the request to a vetted address, and verifies the response socket address;
148
148
  - follows at most five redirects, revalidating each hop;
149
149
  - accepts only HTML/XHTML, plain text, and Markdown content;
150
- - caps response bytes at 2 MiB and extracted output at 32 KiB;
151
- - uses a 60 second deadline;
150
+ - caps response bytes at 4 MiB and extracted output at 32 KiB;
151
+ - uses one 90 second deadline across DNS, redirects, response transfer, and extraction;
152
152
  - strips script/style/noscript blocks and extracts text or Markdown with table preservation.
153
153
 
154
154
  Failures use typed error codes such as `invalid_url`, `unsupported_scheme`, `blocked_address`, `dns_failure`, `redirect_limit`, `redirect_blocked`, `response_too_large`, `unsupported_content_type`, `request_timeout`, `network_error`, `extraction_failed`, and `http_error`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-background-tasks",
3
- "version": "2.4.0",
3
+ "version": "2.5.0",
4
4
  "description": "Pi extension for durable background shell tasks, read-only delegated agents, local attested Pi runs, and fixed-purpose Fusion workflows through child Pi processes.",
5
5
  "type": "module",
6
6
  "license": "ISC",