pi-background-tasks 2.6.0 → 2.6.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/PUBLISHING.md +1 -1
- package/README.md +2 -2
- package/TESTING.md +3 -1
- package/TEST_PLAN.md +4 -3
- package/dist/extensions/anthropic-attribution-child.js +5 -1
- package/dist/extensions/anthropic-attribution-child.js.map +1 -1
- package/dist/extensions/anthropic-attribution.js +7 -12
- package/dist/extensions/anthropic-attribution.js.map +1 -1
- package/dist/package.json +1 -1
- package/dist/src/core/anthropic-attribution.js +14 -8
- package/dist/src/core/anthropic-attribution.js.map +1 -1
- package/docs/INDEX.md +1 -1
- package/docs/commands/claude-cache.md +1 -1
- package/docs/manifest.json +5 -5
- package/docs/operations/releasing.md +1 -1
- package/docs/operations/testing.md +1 -1
- package/docs/operations/troubleshooting.md +2 -0
- package/docs/reference/runtime-contracts.md +3 -3
- package/docs/subsystems/anthropic-attribution.md +4 -4
- package/docs/subsystems/delegation.md +1 -1
- package/docs/subsystems/docs-freshness-gate.md +1 -1
- package/docs/subsystems/host-ui-and-telemetry.md +3 -1
- package/extensions/anthropic-attribution-child.ts +8 -1
- package/extensions/anthropic-attribution.ts +7 -16
- package/package.json +2 -1
- package/src/core/anthropic-attribution.ts +21 -7
package/PUBLISHING.md
CHANGED
|
@@ -47,7 +47,7 @@ npm view pi-background-tasks name version --json
|
|
|
47
47
|
|
|
48
48
|
## Payload verification
|
|
49
49
|
|
|
50
|
-
Use `npm pack --dry-run --json --ignore-scripts` output as the payload source of truth for payload inspection. Verify that `extensions/`, `src/`, `docs/`, `README.md`, `TESTING.md`, `TEST_PLAN.md`, `PUBLISHING.md`, `BACKGROUND-TASKS-INSTRUCTIONS.md`, root `logo.png`, and `LICENSE` match current `package.json.files`, and that tests/scripts/local `.pi` artifacts/node_modules/nested tarballs are excluded.
|
|
50
|
+
Use `npm pack --dry-run --json --ignore-scripts` output as the payload source of truth for payload inspection. Verify that `extensions/`, `src/`, `docs/`, `README.md`, `TESTING.md`, `TEST_PLAN.md`, `PUBLISHING.md`, `BACKGROUND-TASKS-INSTRUCTIONS.md`, root `logo.png`, and `LICENSE` match current `package.json.files`, and that tests/scripts/local `.pi` artifacts/node_modules/nested tarballs are excluded. Pi AI and TypeBox must remain host-provided peers rather than private runtime copies.
|
|
51
51
|
|
|
52
52
|
## Publish to npm
|
|
53
53
|
|
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
| Fact | Value |
|
|
28
28
|
| --- | --- |
|
|
29
29
|
| Package | `pi-background-tasks` |
|
|
30
|
-
| Version | `2.6.
|
|
30
|
+
| Version | `2.6.2` |
|
|
31
31
|
| Node engine | `>=22.19.0` |
|
|
32
32
|
| Pi entrypoints | `./dist/extensions/anthropic-attribution.js`, `./dist/extensions/background-tasks.js` |
|
|
33
33
|
| Package image | [logo.png](https://raw.githubusercontent.com/ismailsaleekh/pi-background-tasks/main/logo.png) |
|
|
@@ -376,4 +376,4 @@ Operations are `capabilities`, `run`, `status`, `logs`, and `kill`. This is the
|
|
|
376
376
|
|
|
377
377
|
Keep user-facing claims tied to source. If you change public schemas, command behavior, durability, model routing, or environment variables, update these package-local docs in the same change and run focused checks appropriate to the edit.
|
|
378
378
|
|
|
379
|
-
For startup work, use `scripts/benchmark-cold-load.mjs` with an owned output/scratch root, explicit `--runtime source|compiled`, and at least 30 fresh-process samples; the exact command and interpretation rules are in [Testing operations](docs/operations/testing.md#cold-load-measurement-discipline). Its “cold” result means an empty JavaScript/Jiti module cache, not a flushed filesystem cache. It is distribution evidence, not a flaky CI threshold. Published Pi entrypoints use precompiled JavaScript; disabled delegate/Fusion/attribution lanes, the dock UI, and attested execution stay outside process-only static startup. Native-Windows and vendor compiled-Bun timing still require their own evidence.
|
|
379
|
+
For startup work, use `scripts/benchmark-cold-load.mjs` with an owned output/scratch root, explicit `--runtime source|compiled`, and at least 30 fresh-process samples; the exact command and interpretation rules are in [Testing operations](docs/operations/testing.md#cold-load-measurement-discipline). Its “cold” result means an empty JavaScript/Jiti module cache, not a flushed filesystem cache. It is distribution evidence, not a flaky CI threshold. Published Pi entrypoints use precompiled JavaScript; disabled delegate/Fusion/attribution lanes, the dock UI, and attested execution stay outside process-only static startup. Load those entrypoints through Pi; `dist/src/**` contains private implementation chunks and is not a set of standalone Node entrypoints. Native-Windows and vendor compiled-Bun timing still require their own evidence.
|
package/TESTING.md
CHANGED
|
@@ -199,6 +199,8 @@ The package URL guard separates syntax-wide discovery of self-contained file-URL
|
|
|
199
199
|
|
|
200
200
|
The local-tarball consumer test does not borrow a warm user npm cache or npm configuration. It first proves a truly empty isolated cache fails with `ENOTCACHED`, then archives the already-published files in the installed `turndown` production closure through the public portable `tar` API bundled with the active npm CLI, without invoking npm's directory-pack lifecycle. A task-owned package with executable `prepack`, `prepare`, and `postpack` hooks proves plain packing attempts lifecycle code, characterizes npm 10.9.3's unsafe `--ignore-scripts` prepare behavior versus npm 11.13.0, and then proves the fixture archive path runs no hook on either line. The installed source hashes and original script-bearing manifest bytes must remain unchanged. The resulting real closure is exposed through a temporary loopback-only registry to seed a separate empty task-owned cache. Every npm subprocess uses explicit task-owned user/global config, a task-owned project cwd with empty project config, `GIT_ALLOW_PROTOCOL=file`, and absolute filesystem operands. A no-request config probe demonstrates that a hostile scoped global/project registry would win without isolation but is absent from the effective preparation environment. After the registry is closed, consumer installation runs with `--offline`; the test checks exact Turndown/Domino versions and performs a real HTML-to-Markdown conversion to prove the transitive dependency loads. Missing registry inputs, failed cache preparation, lifecycle execution, source mutation, or missing runtime dependencies are hard failures.
|
|
201
201
|
|
|
202
|
+
A separate packed-runtime regression copies only that production Turndown/Domino closure beneath the extracted tarball and proves `@earendil-works/pi-ai`, coding-agent, TUI, and TypeBox are absent from the package-local `node_modules`. Pi's real SDK loader must still activate both compiled default entrypoints, invoke the delegate, attested, Fusion, task-manager, and Fusion-selector lazy lanes through their public surfaces, reach post-import semantic validation, and shut down cleanly. A source-graph guard requires every host-sensitive internal dynamic-import issuer to retain its alias-aware host import. This catches native lazy imports that bypass Pi's host aliases; the fixture no longer symlinks the development `node_modules`, which previously masked that entire installed-package failure class. Direct Node imports of private `dist/src/**` implementation chunks are not package entrypoint tests.
|
|
203
|
+
|
|
202
204
|
Fusion-specific targeted gates:
|
|
203
205
|
|
|
204
206
|
```bash
|
|
@@ -253,7 +255,7 @@ Implemented coverage includes:
|
|
|
253
255
|
- durability: `tests/unit/durable-fs.test.ts` covers the shared `src/core/durable-fs.ts` primitive used for metadata, event/stderr buffers, attestations, delegate/Fusion artifacts, and configuration — single-open write/sync/close ordering, exclusive `wx` temp creation at `0o600`, direct `w` writes with inherited mode, never reopening a pathname merely to flush it, temp ownership, primary-versus-cleanup error precedence, `renameCompleted` after a post-rename directory failure, the Windows directory-sync skip, atomic replacement, and fatal `fsync` failures. Ordinary task `.output` is a streaming file ended/drained before terminal metadata and is not explicitly fsynced. `tests/package/package.test.ts` mutation guards pin the durable-helper invariants
|
|
254
256
|
- safety: kill, already-finished kill failure, timeout failure, spawn failure, low output-cap failure, multi-task shutdown cleanup, POSIX process-group kill fallback, Windows `taskkill /T` then `/T /F` tree termination with shared soft attempts, soft-abort-on-force, exit-128 race tolerance, loud force failures, no root-only fallback, SIGKILL escalation that terminates instead of re-arming (a SIGKILL never schedules a further escalation, and concurrent stop requests share exactly one escalation timer that is cleared on finalize), duplicate finalization/notification races, metadata/notification failure handling, and pruning
|
|
255
257
|
- agent loop: deterministic scripted-provider coverage against `extensions/background-tasks.ts` for actual event-driven `bg_run` behavior. The provider observes the effective system prompt, public tool descriptions, and real launch receipt and deliberately emits the pre-fix `bg_status` poll if any contract layer is absent; the passing path proves one launch, no sleep/status/log polling, one durable terminal notification, and exactly one follow-up turn. It also covers notification-only `triggerOnCompletion:false`, `/bg` display-only behavior, `notifyOnCompletion:false`, failed-task notification error fields, and parent-model `fusion_reason` tool use followed by normal parent response
|
|
256
|
-
- package: manifest, docs, ordered `pi.extensions`, exported `src/core/extension-api.ts`, registry-only production dependencies, peer
|
|
258
|
+
- package: manifest, docs, ordered `pi.extensions`, exported `src/core/extension-api.ts`, registry-only production dependencies, host-provided Pi AI peer posture, packed runtime files/notices, production-only no-host-peer activation plus all public lazy lanes and shutdown, alias-aware dynamic-import issuer guards, tarball-install smoke, direct-completion import bans, test/helper/script/artifact exclusion, compiler-tree type-escape and file-URL provenance guards, explicitly prepared isolated offline npm installation with an empty-cache negative control and real transitive load, exact-version compatibility, and current-host persisted/replayed tool-usage safety
|
|
257
259
|
|
|
258
260
|
## PTY notes
|
|
259
261
|
|
package/TEST_PLAN.md
CHANGED
|
@@ -79,7 +79,7 @@ SDK/RPC/scripted-provider/package/compatibility coverage asserts exactly four to
|
|
|
79
79
|
| Stop task from LLM tool | `bg_kill` | | yes | | | | | | Covers running kill and already-finished loud failure. |
|
|
80
80
|
| 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. |
|
|
81
81
|
| 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. |
|
|
82
|
-
| Global Anthropic attribution and sanitization | package-owned compiled attribution entrypoint, `/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. |
|
|
82
|
+
| Global Anthropic attribution and sanitization | package-owned compiled attribution entrypoint, `/claude-cache`, isolated child `--extension` argv | yes | | | | | yes | local SSE + subscription handoff | Package discovery loads attribution before background tasks for every installation. Both ambient and child gateways resolve Pi's host-owned anthropic-messages adapter before entering the compiled lazy core; the core has no runtime Pi-package import, missing injection fails loudly, and a production-only packed fixture with no private Pi peers proves default activation plus shutdown through the real loader. 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. |
|
|
83
83
|
| 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. |
|
|
84
84
|
| 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. |
|
|
85
85
|
| 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. |
|
|
@@ -89,7 +89,7 @@ SDK/RPC/scripted-provider/package/compatibility coverage asserts exactly four to
|
|
|
89
89
|
| 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. |
|
|
90
90
|
| 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. |
|
|
91
91
|
| 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. |
|
|
92
|
-
| Pi/TypeBox compatibility | `peerDependencies`, packed bytes | yes | | | | | yes | | Unit pins typebox as
|
|
92
|
+
| Pi/TypeBox compatibility | `peerDependencies`, packed bytes | yes | | | | | yes | | Unit pins typebox and `@earendil-works/pi-ai` as host-provided `"*"` peers that are neither runtime nor bundled dependencies, 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. |
|
|
93
93
|
| 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. |
|
|
94
94
|
| 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. |
|
|
95
95
|
| 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. |
|
|
@@ -128,7 +128,7 @@ SDK/RPC/scripted-provider/package/compatibility coverage asserts exactly four to
|
|
|
128
128
|
| Process lifecycle/races | registry core | yes | yes | yes | | yes | | yes | Unit tests cover POSIX process-group fallback, Windows `taskkill /T` then `/T /F` tree termination, SIGKILL escalation, duplicate finalization/notification races, notification/metadata failures, pruning, malformed telemetry, split telemetry chunks, large telemetry records above the old 16KiB buffer, and wrapped-agent transcript/telemetry separation with split-chunk and trailing-partial flush; SDK/RPC cover runtime spawn/timeout/output-cap/shutdown; scripted provider covers wakeup integration. |
|
|
129
129
|
| Compiler/source safety guards | package TypeScript and file-URL path handling | | | | | | yes | | TypeScript compiler trees reject actual explicit `any` types, directly nested assertions through multiline/parenthesized/`satisfies` wrappers, production non-null expressions, and recognized `@ts-ignore`/`@ts-expect-error`/`@ts-nocheck` comments while allowing inert comment/string/template/regex text. Alias-separated assertions are intentionally outside the direct-syntax rule, preserving validated `unknown` boundaries without claiming whole-program soundness. The URL guard separates syntax-wide self-contained hazards from finite flow provenance, with distinct normal/break/continue/return/explicit-throw completions and `finally` processing. It uses WHATWG preprocessing/absolute-first classification for static strings and static-head templates; `./`/`../` heads use explicit bases, while proven absolute first values ignore them. It covers inline/variable/import-meta provenance, constructor/object/`pathToFileURL` aliases, exact lowercase `import.meta['url']` versus uppercase intrinsic `globalThis['URL']` keys, computed pathname keys, declaration/assignment/statically known `for...of` destructuring, and Windows drive/UNC fixtures; and retains any explicitly traced may-file branch. Unknown dynamic first values remain unknown but join an explicit base as a possible relative branch; known absolute HTTP(S), definite overwrites, and compiler-resolved shadowed `URL`/`globalThis` bindings are controls. Calls, implicit exceptions, interprocedural/later-closure effects, and heap/proxy mutation are documented limits; no source-file allowlist suppresses findings. |
|
|
130
130
|
| Package manifest | `package.json` | | | | | | yes | | Keywords, ordered `pi.extensions`, files/notices, registry-only production dependencies, local smoke/compat scripts, and direct-completion import bans. |
|
|
131
|
-
| Pack contents | `npm pack --dry-run`, `npm run test:pnpm-pack` | | | | | | yes | | Runtime files and third-party notice included; tests, fake child helpers, release-only scripts, artifacts, nested tarballs, and `node_modules` excluded. Default npm tarball coverage proves an empty cache fails loudly, then seeds a separate empty task-owned cache only from a loopback registry built from lifecycle-free portable archives of the real installed Turndown/Domino production closure. A task-owned executable `prepack`/`prepare`/`postpack` sentinel pins npm 10.9.3's unsafe `--ignore-scripts` prepare behavior and npm 11.13.0's denial, proves the fixture path executes zero hooks, preserves script-bearing manifest bytes, and leaves source hashes unchanged. Every npm subprocess uses explicit task-owned user/global config, an isolated project cwd/config, `GIT_ALLOW_PROTOCOL=file`, and absolute archive/pack/install operands; a config-only hostile scoped-registry control performs no request. The registry is closed before `--offline` install, and Turndown executes with its Domino transitive dependency. Linux CI separately installs the tarball with pnpm 11.18.0 and `blockExoticSubdeps: true`. |
|
|
131
|
+
| Pack contents | `npm pack --dry-run`, `npm run test:pnpm-pack` | | | | | | yes | | Runtime files and third-party notice included; tests, fake child helpers, release-only scripts, artifacts, nested tarballs, and `node_modules` excluded. An extracted-tarball fixture adds only physical copies of the production Turndown/Domino closure, proves every Pi host peer is absent package-locally, and drives both compiled entrypoints plus the public delegate, attested, Fusion, task-manager, and Fusion-selector lazy lanes to post-import semantic boundaries before exercising damaged deferred-module behavior. A source-graph guard pins alias-aware host imports on every internal dynamic-import issuer. Private `dist/src/**` chunks are not standalone Node entrypoints. Default npm tarball coverage proves an empty cache fails loudly, then seeds a separate empty task-owned cache only from a loopback registry built from lifecycle-free portable archives of the real installed Turndown/Domino production closure. A task-owned executable `prepack`/`prepare`/`postpack` sentinel pins npm 10.9.3's unsafe `--ignore-scripts` prepare behavior and npm 11.13.0's denial, proves the fixture path executes zero hooks, preserves script-bearing manifest bytes, and leaves source hashes unchanged. Every npm subprocess uses explicit task-owned user/global config, an isolated project cwd/config, `GIT_ALLOW_PROTOCOL=file`, and absolute archive/pack/install operands; a config-only hostile scoped-registry control performs no request. The registry is closed before `--offline` install, and Turndown executes with its Domino transitive dependency. Linux CI separately installs the tarball with pnpm 11.18.0 and `blockExoticSubdeps: true`. |
|
|
132
132
|
| Maintainer docs gateway | `BACKGROUND-TASKS-INSTRUCTIONS.md` | docs focused | | | | | | | Gateway requires INDEX → read-before-edit → owning-doc navigation, records current docs generate/verify/attestation workflow, preserves subscription-only frontier routing, parent/child tool separation, durability/integrity, and no self-certification. Logo reference is package-local (`docs/assets/logo.svg`) when the asset-owner file is present. |
|
|
133
133
|
| Shared architecture/API/runtime/operations docs | `docs/api`, `docs/concepts`, `docs/subsystems`, `docs/reference`, `docs/operations` | docs focused | | | | | | | The active docs engine validates frontmatter, package-local links/reference links, INDEX reachability, exact behavioral source ownership, generated runtime contracts, deterministic generated regions, and manifest freshness. Semantic receipt state is reported by default and enforced only by the optional strict attestation command. Unsupported extraction and legacy placeholders fail closed. |
|
|
134
134
|
| Package maintenance version/tag wording | `PUBLISHING.md`, `docs/operations/releasing.md` | docs focused | | | | | | | Release commands derive `VERSION` from `package.json`; observed git tags stop at `v0.6.0`, so git install/tag certification is separate from npm publishing and no nonexistent v1 tag is advertised. |
|
|
@@ -159,6 +159,7 @@ Lane A residual hardening is now covered by automated tests. No remaining harden
|
|
|
159
159
|
- [x] An incomplete response (`length`, non-`stop` stop reason) or a whitespace-only answer is never committed as a complete result, because a valid hash proves integrity but not completeness.
|
|
160
160
|
- [x] A real subscription-only run demonstrates the whole loop end to end, including that the child used both its read-only tools and the projected conversation.
|
|
161
161
|
- [x] `npm run test:full` validates baseline real TUI/PTY behavior.
|
|
162
|
+
- [x] A production-only extracted package with no private Pi SDK peers loads both compiled entrypoints, exercises every public lazy lane through Pi's real loader, and shuts down cleanly.
|
|
162
163
|
- [x] `npm run pack:dry-run` passes.
|
|
163
164
|
- [x] README claims and all plausible edge cases are exhaustively mapped in this test plan, including Fusion command/tool/model-selector public surfaces and the text-only image omission limitation.
|
|
164
165
|
- [x] Every listed edge case has automated coverage at the lowest reliable layer.
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import { anthropicMessagesApi } from '@earendil-works/pi-ai/compat';
|
|
2
|
+
import spawnAnthropicAttribution, {} from '../src/core/anthropic-attribution.js';
|
|
1
3
|
// Always-on safety entrypoint for package-owned isolated Anthropic children.
|
|
2
4
|
// Ambient parent capability selection must never disable this extension.
|
|
3
|
-
export
|
|
5
|
+
export default function childAnthropicAttribution(pi) {
|
|
6
|
+
spawnAnthropicAttribution(pi, { hostAnthropicMessagesApi: anthropicMessagesApi });
|
|
7
|
+
}
|
|
4
8
|
//# sourceMappingURL=anthropic-attribution-child.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic-attribution-child.js","sourceRoot":"","sources":["../../extensions/anthropic-attribution-child.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,yEAAyE;AACzE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"anthropic-attribution-child.js","sourceRoot":"","sources":["../../extensions/anthropic-attribution-child.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,yBAAyB,EAAE,EAEjC,MAAM,sCAAsC,CAAC;AAE9C,6EAA6E;AAC7E,yEAAyE;AACzE,MAAM,CAAC,OAAO,UAAU,yBAAyB,CAAC,EAAmB;IACnE,yBAAyB,CAAC,EAAE,EAAE,EAAE,wBAAwB,EAAE,oBAAoB,EAAE,CAAC,CAAC;AACpF,CAAC","sourcesContent":["import { anthropicMessagesApi } from '@earendil-works/pi-ai/compat';\nimport spawnAnthropicAttribution, {\n type PiExtensionHost,\n} from '../src/core/anthropic-attribution.js';\n\n// Always-on safety entrypoint for package-owned isolated Anthropic children.\n// Ambient parent capability selection must never disable this extension.\nexport default function childAnthropicAttribution(pi: PiExtensionHost): void {\n spawnAnthropicAttribution(pi, { hostAnthropicMessagesApi: anthropicMessagesApi });\n}\n"]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { anthropicMessagesApi } from '@earendil-works/pi-ai/compat';
|
|
1
2
|
import { parseBackgroundTasksConfig } from '../src/core/config.js';
|
|
2
3
|
const ANTHROPIC_PROVIDER = 'anthropic';
|
|
3
4
|
function captureProviderSnapshot(registry) {
|
|
@@ -55,7 +56,9 @@ function restoreProviderInstallation(installation) {
|
|
|
55
56
|
else {
|
|
56
57
|
// The exact package token and absence of a later native owner were proven above.
|
|
57
58
|
// Public unregister is therefore owner-conditional here: it removes only this
|
|
58
|
-
// still-current package layer and restores
|
|
59
|
+
// still-current package layer and restores dynamic-registration absence. The host
|
|
60
|
+
// may legitimately refresh its built-in provider while our overlay is installed,
|
|
61
|
+
// so that host-owned effective object is not required to retain stale identity.
|
|
59
62
|
registry.unregisterProvider(ANTHROPIC_PROVIDER);
|
|
60
63
|
}
|
|
61
64
|
const restoredConfig = registry.getRegisteredProviderConfig(ANTHROPIC_PROVIDER);
|
|
@@ -70,19 +73,11 @@ function restoreProviderInstallation(installation) {
|
|
|
70
73
|
(before.native !== undefined
|
|
71
74
|
? restoredNative === before.native
|
|
72
75
|
: restoredNative === undefined);
|
|
73
|
-
const streamIdentityRestored = before.legacy !== undefined
|
|
74
|
-
? restoredConfig?.streamSimple === before.legacy.streamSimple
|
|
75
|
-
: restoredEffective?.streamSimple === before.effective.streamSimple;
|
|
76
|
-
const effectiveIdentityRestored = before.legacy !== undefined || restoredEffective === before.effective;
|
|
77
76
|
const registeredIdRestored = before.legacy !== undefined || before.native !== undefined
|
|
78
77
|
? restoredIds.includes(ANTHROPIC_PROVIDER)
|
|
79
78
|
: !restoredIds.includes(ANTHROPIC_PROVIDER);
|
|
80
|
-
if (!registrationRestored ||
|
|
81
|
-
|
|
82
|
-
!streamIdentityRestored ||
|
|
83
|
-
!effectiveIdentityRestored ||
|
|
84
|
-
!registeredIdRestored) {
|
|
85
|
-
throw new Error('pi_anthropic_attribution_restore_failed: the preexisting host provider was not restored by identity');
|
|
79
|
+
if (!registrationRestored || restoredEffective === undefined || !registeredIdRestored) {
|
|
80
|
+
throw new Error('pi_anthropic_attribution_restore_failed: the preexisting public provider registration state was not restored');
|
|
86
81
|
}
|
|
87
82
|
}
|
|
88
83
|
export default async function ambientAnthropicAttribution(pi) {
|
|
@@ -93,7 +88,7 @@ export default async function ambientAnthropicAttribution(pi) {
|
|
|
93
88
|
pi.on('session_start', (_event, context) => {
|
|
94
89
|
const registry = context.modelRegistry;
|
|
95
90
|
const before = captureProviderSnapshot(registry);
|
|
96
|
-
spawnAnthropicAttribution(pi);
|
|
91
|
+
spawnAnthropicAttribution(pi, { hostAnthropicMessagesApi: anthropicMessagesApi });
|
|
97
92
|
installation = confirmProviderInstallation(registry, before) ?? installation;
|
|
98
93
|
});
|
|
99
94
|
pi.on('session_shutdown', () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic-attribution.js","sourceRoot":"","sources":["../../extensions/anthropic-attribution.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Provider } from '@earendil-works/pi-ai';\nimport type { ModelRegistry } from '@earendil-works/pi-coding-agent';\nimport type { PiContextLike, PiExtensionHost } from '../src/core/anthropic-attribution.js';\nimport { parseBackgroundTasksConfig } from '../src/core/config.js';\n\nconst ANTHROPIC_PROVIDER = 'anthropic';\n\ntype RegisteredProviderConfig = NonNullable<\n ReturnType<ModelRegistry['getRegisteredProviderConfig']>\n>;\n\ninterface AmbientAttributionContext extends PiContextLike {\n readonly modelRegistry: ModelRegistry;\n}\n\ninterface ProviderSnapshot {\n readonly effective: Provider;\n readonly legacy: RegisteredProviderConfig | undefined;\n readonly native: Provider | undefined;\n}\n\ninterface ProviderInstallation {\n readonly registry: ModelRegistry;\n readonly before: ProviderSnapshot;\n readonly token: RegisteredProviderConfig;\n}\n\nfunction captureProviderSnapshot(registry: ModelRegistry): ProviderSnapshot {\n const effective = registry.getProvider(ANTHROPIC_PROVIDER);\n if (effective === undefined) {\n throw new Error(\n 'pi_anthropic_attribution_unsupported: the host exposes no effective anthropic provider to preserve',\n );\n }\n return {\n effective,\n legacy: registry.getRegisteredProviderConfig(ANTHROPIC_PROVIDER),\n native: registry.getRegisteredNativeProvider(ANTHROPIC_PROVIDER),\n };\n}\n\nfunction confirmProviderInstallation(\n registry: ModelRegistry,\n before: ProviderSnapshot,\n): ProviderInstallation | undefined {\n const token = registry.getRegisteredProviderConfig(ANTHROPIC_PROVIDER);\n const native = registry.getRegisteredNativeProvider(ANTHROPIC_PROVIDER);\n const effective = registry.getProvider(ANTHROPIC_PROVIDER);\n\n // The accepted duplicate-owner protocol returned without registering anything.\n if (token === before.legacy && native === before.native && effective === before.effective) {\n return undefined;\n }\n\n if (\n token === undefined ||\n token === before.legacy ||\n native !== undefined ||\n effective === undefined ||\n effective === before.effective ||\n token.streamSimple === before.legacy?.streamSimple\n ) {\n throw new Error(\n 'pi_anthropic_attribution_install_failed: host provider registration did not install the package transport atomically',\n );\n }\n\n return { registry, before, token };\n}\n\nfunction sameConfigValues(\n left: RegisteredProviderConfig,\n right: RegisteredProviderConfig,\n): boolean {\n const leftKeys = Object.keys(left).sort();\n const rightKeys = Object.keys(right).sort();\n return (\n leftKeys.length === rightKeys.length &&\n leftKeys.every(\n (key, index) =>\n key === rightKeys[index] && Object.is(Reflect.get(left, key), Reflect.get(right, key)),\n )\n );\n}\n\nfunction restoreProviderInstallation(installation: ProviderInstallation): void {\n const { registry, before, token } = installation;\n if (\n registry.getRegisteredProviderConfig(ANTHROPIC_PROVIDER) !== token ||\n registry.getRegisteredNativeProvider(ANTHROPIC_PROVIDER) !== undefined\n ) {\n // A later owner replaced this exact installation. It owns teardown now.\n return;\n }\n\n if (before.native !== undefined) {\n registry.registerProvider(before.native);\n } else if (before.legacy !== undefined) {\n // Native replacement removes only the still-current package legacy layer. Reapplying\n // the prior legacy snapshot then starts from an empty legacy layer, so package-only\n // fields cannot leak through ModelRuntime's documented merge semantics.\n registry.registerProvider(before.effective);\n registry.registerProvider(ANTHROPIC_PROVIDER, before.legacy);\n } else {\n // The exact package token and absence of a later native owner were proven above.\n // Public unregister is therefore owner-conditional here: it removes only this\n // still-current package layer and restores the captured built-in source absence.\n registry.unregisterProvider(ANTHROPIC_PROVIDER);\n }\n\n const restoredConfig = registry.getRegisteredProviderConfig(ANTHROPIC_PROVIDER);\n const restoredNative = registry.getRegisteredNativeProvider(ANTHROPIC_PROVIDER);\n const restoredEffective = registry.getProvider(ANTHROPIC_PROVIDER);\n const restoredIds = registry.getRegisteredProviderIds();\n const registrationRestored =\n before.legacy !== undefined\n ? restoredNative === undefined &&\n restoredConfig !== undefined &&\n sameConfigValues(restoredConfig, before.legacy)\n : restoredConfig === undefined &&\n (before.native !== undefined\n ? restoredNative === before.native\n : restoredNative === undefined);\n const streamIdentityRestored =\n before.legacy !== undefined\n ? restoredConfig?.streamSimple === before.legacy.streamSimple\n : restoredEffective?.streamSimple === before.effective.streamSimple;\n const effectiveIdentityRestored =\n before.legacy !== undefined || restoredEffective === before.effective;\n const registeredIdRestored =\n before.legacy !== undefined || before.native !== undefined\n ? restoredIds.includes(ANTHROPIC_PROVIDER)\n : !restoredIds.includes(ANTHROPIC_PROVIDER);\n if (\n !registrationRestored ||\n restoredEffective === undefined ||\n !streamIdentityRestored ||\n !effectiveIdentityRestored ||\n !registeredIdRestored\n ) {\n throw new Error(\n 'pi_anthropic_attribution_restore_failed: the preexisting host provider was not restored by identity',\n );\n }\n}\n\nexport default async function ambientAnthropicAttribution(pi: PiExtensionHost): Promise<void> {\n const config = parseBackgroundTasksConfig();\n if (config.features.attribution) {\n const { default: spawnAnthropicAttribution } = await import(\n '../src/core/anthropic-attribution.js'\n );\n let installation: ProviderInstallation | undefined;\n pi.on('session_start', (_event, context) => {\n const registry = (context as AmbientAttributionContext).modelRegistry;\n const before = captureProviderSnapshot(registry);\n spawnAnthropicAttribution(pi);\n installation = confirmProviderInstallation(registry, before) ?? installation;\n });\n pi.on('session_shutdown', () => {\n const current = installation;\n installation = undefined;\n if (current !== undefined) restoreProviderInstallation(current);\n });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"anthropic-attribution.js","sourceRoot":"","sources":["../../extensions/anthropic-attribution.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Provider } from '@earendil-works/pi-ai';\nimport { anthropicMessagesApi } from '@earendil-works/pi-ai/compat';\nimport type { ModelRegistry } from '@earendil-works/pi-coding-agent';\nimport type { PiContextLike, PiExtensionHost } from '../src/core/anthropic-attribution.js';\nimport { parseBackgroundTasksConfig } from '../src/core/config.js';\n\nconst ANTHROPIC_PROVIDER = 'anthropic';\n\ntype RegisteredProviderConfig = NonNullable<\n ReturnType<ModelRegistry['getRegisteredProviderConfig']>\n>;\n\ninterface AmbientAttributionContext extends PiContextLike {\n readonly modelRegistry: ModelRegistry;\n}\n\ninterface ProviderSnapshot {\n readonly effective: Provider;\n readonly legacy: RegisteredProviderConfig | undefined;\n readonly native: Provider | undefined;\n}\n\ninterface ProviderInstallation {\n readonly registry: ModelRegistry;\n readonly before: ProviderSnapshot;\n readonly token: RegisteredProviderConfig;\n}\n\nfunction captureProviderSnapshot(registry: ModelRegistry): ProviderSnapshot {\n const effective = registry.getProvider(ANTHROPIC_PROVIDER);\n if (effective === undefined) {\n throw new Error(\n 'pi_anthropic_attribution_unsupported: the host exposes no effective anthropic provider to preserve',\n );\n }\n return {\n effective,\n legacy: registry.getRegisteredProviderConfig(ANTHROPIC_PROVIDER),\n native: registry.getRegisteredNativeProvider(ANTHROPIC_PROVIDER),\n };\n}\n\nfunction confirmProviderInstallation(\n registry: ModelRegistry,\n before: ProviderSnapshot,\n): ProviderInstallation | undefined {\n const token = registry.getRegisteredProviderConfig(ANTHROPIC_PROVIDER);\n const native = registry.getRegisteredNativeProvider(ANTHROPIC_PROVIDER);\n const effective = registry.getProvider(ANTHROPIC_PROVIDER);\n\n // The accepted duplicate-owner protocol returned without registering anything.\n if (token === before.legacy && native === before.native && effective === before.effective) {\n return undefined;\n }\n\n if (\n token === undefined ||\n token === before.legacy ||\n native !== undefined ||\n effective === undefined ||\n effective === before.effective ||\n token.streamSimple === before.legacy?.streamSimple\n ) {\n throw new Error(\n 'pi_anthropic_attribution_install_failed: host provider registration did not install the package transport atomically',\n );\n }\n\n return { registry, before, token };\n}\n\nfunction sameConfigValues(\n left: RegisteredProviderConfig,\n right: RegisteredProviderConfig,\n): boolean {\n const leftKeys = Object.keys(left).sort();\n const rightKeys = Object.keys(right).sort();\n return (\n leftKeys.length === rightKeys.length &&\n leftKeys.every(\n (key, index) =>\n key === rightKeys[index] && Object.is(Reflect.get(left, key), Reflect.get(right, key)),\n )\n );\n}\n\nfunction restoreProviderInstallation(installation: ProviderInstallation): void {\n const { registry, before, token } = installation;\n if (\n registry.getRegisteredProviderConfig(ANTHROPIC_PROVIDER) !== token ||\n registry.getRegisteredNativeProvider(ANTHROPIC_PROVIDER) !== undefined\n ) {\n // A later owner replaced this exact installation. It owns teardown now.\n return;\n }\n\n if (before.native !== undefined) {\n registry.registerProvider(before.native);\n } else if (before.legacy !== undefined) {\n // Native replacement removes only the still-current package legacy layer. Reapplying\n // the prior legacy snapshot then starts from an empty legacy layer, so package-only\n // fields cannot leak through ModelRuntime's documented merge semantics.\n registry.registerProvider(before.effective);\n registry.registerProvider(ANTHROPIC_PROVIDER, before.legacy);\n } else {\n // The exact package token and absence of a later native owner were proven above.\n // Public unregister is therefore owner-conditional here: it removes only this\n // still-current package layer and restores dynamic-registration absence. The host\n // may legitimately refresh its built-in provider while our overlay is installed,\n // so that host-owned effective object is not required to retain stale identity.\n registry.unregisterProvider(ANTHROPIC_PROVIDER);\n }\n\n const restoredConfig = registry.getRegisteredProviderConfig(ANTHROPIC_PROVIDER);\n const restoredNative = registry.getRegisteredNativeProvider(ANTHROPIC_PROVIDER);\n const restoredEffective = registry.getProvider(ANTHROPIC_PROVIDER);\n const restoredIds = registry.getRegisteredProviderIds();\n const registrationRestored =\n before.legacy !== undefined\n ? restoredNative === undefined &&\n restoredConfig !== undefined &&\n sameConfigValues(restoredConfig, before.legacy)\n : restoredConfig === undefined &&\n (before.native !== undefined\n ? restoredNative === before.native\n : restoredNative === undefined);\n const registeredIdRestored =\n before.legacy !== undefined || before.native !== undefined\n ? restoredIds.includes(ANTHROPIC_PROVIDER)\n : !restoredIds.includes(ANTHROPIC_PROVIDER);\n if (!registrationRestored || restoredEffective === undefined || !registeredIdRestored) {\n throw new Error(\n 'pi_anthropic_attribution_restore_failed: the preexisting public provider registration state was not restored',\n );\n }\n}\n\nexport default async function ambientAnthropicAttribution(pi: PiExtensionHost): Promise<void> {\n const config = parseBackgroundTasksConfig();\n if (config.features.attribution) {\n const { default: spawnAnthropicAttribution } = await import(\n '../src/core/anthropic-attribution.js'\n );\n let installation: ProviderInstallation | undefined;\n pi.on('session_start', (_event, context) => {\n const registry = (context as AmbientAttributionContext).modelRegistry;\n const before = captureProviderSnapshot(registry);\n spawnAnthropicAttribution(pi, { hostAnthropicMessagesApi: anthropicMessagesApi });\n installation = confirmProviderInstallation(registry, before) ?? installation;\n });\n pi.on('session_shutdown', () => {\n const current = installation;\n installation = undefined;\n if (current !== undefined) restoreProviderInstallation(current);\n });\n }\n}\n"]}
|
package/dist/package.json
CHANGED
|
@@ -2,7 +2,6 @@ import { createHash } from 'node:crypto';
|
|
|
2
2
|
import { appendFileSync, readFileSync } from 'node:fs';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
4
|
import { isAbsolute, join } from 'node:path';
|
|
5
|
-
import { anthropicMessagesApi } from '@earendil-works/pi-ai/compat';
|
|
6
5
|
export const CLAUDE_CODE_SESSION_HEADER = 'X-Claude-Code-Session-Id';
|
|
7
6
|
const CLAUDE_CODE_VERSION = '2.1.251';
|
|
8
7
|
const CLAUDE_CODE_ENTRYPOINT = 'sdk-cli';
|
|
@@ -1770,17 +1769,24 @@ function createOutput(model) {
|
|
|
1770
1769
|
timestamp: Date.now(),
|
|
1771
1770
|
};
|
|
1772
1771
|
}
|
|
1773
|
-
function forwardToBuiltInAnthropic(model, context, options) {
|
|
1774
|
-
//
|
|
1775
|
-
//
|
|
1776
|
-
//
|
|
1777
|
-
//
|
|
1778
|
-
const
|
|
1772
|
+
function forwardToBuiltInAnthropic(model, context, options, dependencies) {
|
|
1773
|
+
// The compiled gateway resolves this host-owned adapter through Pi's alias-aware
|
|
1774
|
+
// extension loader and injects it across the lazy native-import boundary. Keeping
|
|
1775
|
+
// the deferred core free of runtime Pi package imports prevents Node from trying to
|
|
1776
|
+
// resolve a private @earendil-works/pi-ai installation beside a managed package.
|
|
1777
|
+
const hostAnthropicMessagesApi = dependencies.hostAnthropicMessagesApi;
|
|
1778
|
+
if (hostAnthropicMessagesApi === undefined) {
|
|
1779
|
+
throw new Error("pi_anthropic_attribution_host_adapter_missing: the extension gateway did not inject Pi's anthropic-messages adapter");
|
|
1780
|
+
}
|
|
1781
|
+
// The host owns both its legacy Context or normalized TranscriptContext input and
|
|
1782
|
+
// the complete stream/event/result shape. These intersections only mark that
|
|
1783
|
+
// host-owned boundary; no request, callback, event, or result is reconstructed.
|
|
1784
|
+
const delegated = hostAnthropicMessagesApi().streamSimple(model, context, options);
|
|
1779
1785
|
return delegated;
|
|
1780
1786
|
}
|
|
1781
1787
|
export function streamAnthropicViaBetaMessages(model, context, options, dependencies = {}) {
|
|
1782
1788
|
if (model.provider !== 'anthropic') {
|
|
1783
|
-
return forwardToBuiltInAnthropic(model, context, options);
|
|
1789
|
+
return forwardToBuiltInAnthropic(model, context, options, dependencies);
|
|
1784
1790
|
}
|
|
1785
1791
|
const stream = createAssistantMessageEventStream();
|
|
1786
1792
|
const output = createOutput(model);
|