@piagent/platform 1.6.1 → 1.7.1
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/CHANGELOG.md +55 -0
- package/README.md +8 -2
- package/README.vi.md +11 -2
- package/adapters/common/assurance-producers.json +109 -0
- package/adapters/common/structured-assurance-facts.mjs +153 -0
- package/adapters/node-typescript/contract-families.json +2811 -0
- package/adapters/node-typescript/contract-families.md +90 -0
- package/benchmarks/production-v1/grade.mjs +20 -0
- package/benchmarks/production-v1/spend-control.v1.json +3 -3
- package/benchmarks/production-v2/grade.mjs +892 -0
- package/benchmarks/production-v2/project/README.md +7 -0
- package/benchmarks/production-v2/project/config/service.json +4 -0
- package/benchmarks/production-v2/project/data/audit-ledger.jsonl +1 -0
- package/benchmarks/production-v2/project/docs/ops.md +3 -0
- package/benchmarks/production-v2/project/logs/incident.log +2 -0
- package/benchmarks/production-v2/project/package.json +11 -0
- package/benchmarks/production-v2/project/scripts/check.mjs +21 -0
- package/benchmarks/production-v2/project/src/backend/auth.js +3 -0
- package/benchmarks/production-v2/project/src/backend/billing-window.js +4 -0
- package/benchmarks/production-v2/project/src/backend/cache.js +15 -0
- package/benchmarks/production-v2/project/src/backend/invoice.js +4 -0
- package/benchmarks/production-v2/project/src/backend/revocation-cache.js +3 -0
- package/benchmarks/production-v2/project/src/data/csv.js +3 -0
- package/benchmarks/production-v2/project/src/data/dedup.js +5 -0
- package/benchmarks/production-v2/project/src/data/migration.js +9 -0
- package/benchmarks/production-v2/project/src/data/ndjson-stream.js +5 -0
- package/benchmarks/production-v2/project/src/data/versioned-replay.js +8 -0
- package/benchmarks/production-v2/project/src/frontend/chat-events.js +7 -0
- package/benchmarks/production-v2/project/src/frontend/pagination.js +8 -0
- package/benchmarks/production-v2/project/src/frontend/request-lifecycle.js +15 -0
- package/benchmarks/production-v2/project/src/frontend/search-state.js +8 -0
- package/benchmarks/production-v2/project/src/frontend/unicode-search.js +7 -0
- package/benchmarks/production-v2/project/src/fullstack/contract-sync.js +10 -0
- package/benchmarks/production-v2/project/src/platform/args.js +10 -0
- package/benchmarks/production-v2/project/src/platform/config.js +7 -0
- package/benchmarks/production-v2/project/src/platform/workflow-session.js +9 -0
- package/benchmarks/production-v2/project/src/platform/workspace.js +13 -0
- package/benchmarks/production-v2/project/src/reliability/checkpoint.js +5 -0
- package/benchmarks/production-v2/project/src/reliability/expiry.js +5 -0
- package/benchmarks/production-v2/project/src/reliability/retry.js +15 -0
- package/benchmarks/production-v2/project/test/smoke.test.js +21 -0
- package/benchmarks/production-v2/project/vendor/README.md +1 -0
- package/benchmarks/production-v2/prompts/abort-reconnect-supersession.md +11 -0
- package/benchmarks/production-v2/prompts/backend-frontend-contract-sync.md +12 -0
- package/benchmarks/production-v2/prompts/billing-cutoff-clock-skew.md +11 -0
- package/benchmarks/production-v2/prompts/bounded-retry.md +7 -0
- package/benchmarks/production-v2/prompts/chunked-record-boundary.md +9 -0
- package/benchmarks/production-v2/prompts/cli-double-dash.md +7 -0
- package/benchmarks/production-v2/prompts/config-precedence.md +6 -0
- package/benchmarks/production-v2/prompts/destructive-history-refusal.md +3 -0
- package/benchmarks/production-v2/prompts/expiry-boundary.md +7 -0
- package/benchmarks/production-v2/prompts/idempotent-replay-conflict.md +11 -0
- package/benchmarks/production-v2/prompts/incident-diagnosis.md +4 -0
- package/benchmarks/production-v2/prompts/invoice-rounding.md +9 -0
- package/benchmarks/production-v2/prompts/journeys/config-precedence-scout.md +3 -0
- package/benchmarks/production-v2/prompts/journeys/expiry-boundary-scout.md +3 -0
- package/benchmarks/production-v2/prompts/journeys/quoted-csv-scout.md +3 -0
- package/benchmarks/production-v2/prompts/journeys/recover-after-uncertain-send.md +4 -0
- package/benchmarks/production-v2/prompts/journeys/repository-prompt-injection-scout.md +4 -0
- package/benchmarks/production-v2/prompts/journeys/resumable-checkpoint-partial-failure-scout.md +4 -0
- package/benchmarks/production-v2/prompts/journeys/stable-dedup-scout.md +3 -0
- package/benchmarks/production-v2/prompts/journeys/stale-search-response-scout.md +3 -0
- package/benchmarks/production-v2/prompts/journeys/tenant-cache-isolation-scout.md +4 -0
- package/benchmarks/production-v2/prompts/journeys/verify-current-contract.md +4 -0
- package/benchmarks/production-v2/prompts/journeys/workflow-switch-same-session-scout.md +4 -0
- package/benchmarks/production-v2/prompts/pagination-boundary.md +7 -0
- package/benchmarks/production-v2/prompts/protected-env-refusal.md +2 -0
- package/benchmarks/production-v2/prompts/quoted-csv.md +6 -0
- package/benchmarks/production-v2/prompts/reconnect-chat-event-order.md +19 -0
- package/benchmarks/production-v2/prompts/repository-prompt-injection.md +5 -0
- package/benchmarks/production-v2/prompts/resumable-checkpoint-partial-failure.md +11 -0
- package/benchmarks/production-v2/prompts/revoked-session-cache.md +10 -0
- package/benchmarks/production-v2/prompts/schema-migration.md +6 -0
- package/benchmarks/production-v2/prompts/stable-dedup.md +6 -0
- package/benchmarks/production-v2/prompts/stale-search-response.md +6 -0
- package/benchmarks/production-v2/prompts/tenant-cache-isolation.md +6 -0
- package/benchmarks/production-v2/prompts/tenant-role-authorization.md +6 -0
- package/benchmarks/production-v2/prompts/unicode-search.md +6 -0
- package/benchmarks/production-v2/prompts/workflow-switch-same-session.md +30 -0
- package/benchmarks/production-v2/prompts/workspace-order.md +7 -0
- package/benchmarks/production-v2/spend-control.v1.json +115 -0
- package/benchmarks/production-v2/suite.json +1009 -0
- package/benchmarks/production-v2/variant.mjs +169 -0
- package/benchmarks/production-v3/CONTRACT-REVISION-20260922.md +31 -0
- package/benchmarks/production-v3/calibration/corpus.v1.json +63 -0
- package/benchmarks/production-v3/calibration/human-review-rubric.v1.json +36 -0
- package/benchmarks/production-v3/grade.mjs +1017 -0
- package/benchmarks/production-v3/project/README.md +7 -0
- package/benchmarks/production-v3/project/config/service.json +4 -0
- package/benchmarks/production-v3/project/data/audit-ledger.jsonl +1 -0
- package/benchmarks/production-v3/project/docs/ops.md +3 -0
- package/benchmarks/production-v3/project/logs/incident.log +2 -0
- package/benchmarks/production-v3/project/package.json +11 -0
- package/benchmarks/production-v3/project/scripts/check.mjs +21 -0
- package/benchmarks/production-v3/project/src/backend/auth.js +3 -0
- package/benchmarks/production-v3/project/src/backend/billing-window.js +4 -0
- package/benchmarks/production-v3/project/src/backend/cache.js +15 -0
- package/benchmarks/production-v3/project/src/backend/invoice.js +4 -0
- package/benchmarks/production-v3/project/src/backend/revocation-cache.js +12 -0
- package/benchmarks/production-v3/project/src/data/csv.js +3 -0
- package/benchmarks/production-v3/project/src/data/dedup.js +5 -0
- package/benchmarks/production-v3/project/src/data/migration.js +9 -0
- package/benchmarks/production-v3/project/src/data/ndjson-stream.js +5 -0
- package/benchmarks/production-v3/project/src/data/versioned-replay.js +8 -0
- package/benchmarks/production-v3/project/src/frontend/chat-events.js +7 -0
- package/benchmarks/production-v3/project/src/frontend/pagination.js +8 -0
- package/benchmarks/production-v3/project/src/frontend/request-lifecycle.js +23 -0
- package/benchmarks/production-v3/project/src/frontend/search-state.js +8 -0
- package/benchmarks/production-v3/project/src/frontend/unicode-search.js +7 -0
- package/benchmarks/production-v3/project/src/fullstack/contract-sync.js +10 -0
- package/benchmarks/production-v3/project/src/platform/args.js +10 -0
- package/benchmarks/production-v3/project/src/platform/config.js +7 -0
- package/benchmarks/production-v3/project/src/platform/workflow-session.js +9 -0
- package/benchmarks/production-v3/project/src/platform/workspace.js +13 -0
- package/benchmarks/production-v3/project/src/reliability/checkpoint.js +5 -0
- package/benchmarks/production-v3/project/src/reliability/expiry.js +14 -0
- package/benchmarks/production-v3/project/src/reliability/retry.js +15 -0
- package/benchmarks/production-v3/project/test/smoke.test.js +21 -0
- package/benchmarks/production-v3/project/vendor/README.md +1 -0
- package/benchmarks/production-v3/prompts/abort-reconnect-supersession.md +11 -0
- package/benchmarks/production-v3/prompts/backend-frontend-contract-sync.md +20 -0
- package/benchmarks/production-v3/prompts/billing-cutoff-clock-skew.md +11 -0
- package/benchmarks/production-v3/prompts/bounded-retry.md +7 -0
- package/benchmarks/production-v3/prompts/chunked-record-boundary.md +11 -0
- package/benchmarks/production-v3/prompts/cli-double-dash.md +7 -0
- package/benchmarks/production-v3/prompts/config-precedence.md +6 -0
- package/benchmarks/production-v3/prompts/destructive-history-refusal.md +9 -0
- package/benchmarks/production-v3/prompts/expiry-boundary.md +14 -0
- package/benchmarks/production-v3/prompts/idempotent-replay-conflict.md +13 -0
- package/benchmarks/production-v3/prompts/incident-diagnosis.md +4 -0
- package/benchmarks/production-v3/prompts/invoice-rounding.md +9 -0
- package/benchmarks/production-v3/prompts/journeys/config-precedence-scout.md +3 -0
- package/benchmarks/production-v3/prompts/journeys/expiry-boundary-scout.md +3 -0
- package/benchmarks/production-v3/prompts/journeys/quoted-csv-scout.md +3 -0
- package/benchmarks/production-v3/prompts/journeys/recover-after-uncertain-send.md +4 -0
- package/benchmarks/production-v3/prompts/journeys/repository-prompt-injection-scout.md +4 -0
- package/benchmarks/production-v3/prompts/journeys/resumable-checkpoint-partial-failure-scout.md +4 -0
- package/benchmarks/production-v3/prompts/journeys/stable-dedup-scout.md +3 -0
- package/benchmarks/production-v3/prompts/journeys/stale-search-response-scout.md +3 -0
- package/benchmarks/production-v3/prompts/journeys/tenant-cache-isolation-scout.md +4 -0
- package/benchmarks/production-v3/prompts/journeys/verify-current-contract.md +4 -0
- package/benchmarks/production-v3/prompts/journeys/workflow-switch-same-session-scout.md +4 -0
- package/benchmarks/production-v3/prompts/pagination-boundary.md +9 -0
- package/benchmarks/production-v3/prompts/protected-env-refusal.md +8 -0
- package/benchmarks/production-v3/prompts/quoted-csv.md +6 -0
- package/benchmarks/production-v3/prompts/reconnect-chat-event-order.md +20 -0
- package/benchmarks/production-v3/prompts/repository-prompt-injection.md +5 -0
- package/benchmarks/production-v3/prompts/resumable-checkpoint-partial-failure.md +15 -0
- package/benchmarks/production-v3/prompts/revoked-session-cache.md +10 -0
- package/benchmarks/production-v3/prompts/schema-migration.md +6 -0
- package/benchmarks/production-v3/prompts/stable-dedup.md +6 -0
- package/benchmarks/production-v3/prompts/stale-search-response.md +6 -0
- package/benchmarks/production-v3/prompts/tenant-cache-isolation.md +6 -0
- package/benchmarks/production-v3/prompts/tenant-role-authorization.md +6 -0
- package/benchmarks/production-v3/prompts/unicode-search.md +6 -0
- package/benchmarks/production-v3/prompts/workflow-switch-same-session.md +30 -0
- package/benchmarks/production-v3/prompts/workspace-order.md +7 -0
- package/benchmarks/production-v3/public-checks/abort-reconnect-supersession.js +34 -0
- package/benchmarks/production-v3/public-checks/expiry-boundary.js +29 -0
- package/benchmarks/production-v3/public-checks/revoked-session-cache.js +20 -0
- package/benchmarks/production-v3/spend-control.v1.json +121 -0
- package/benchmarks/production-v3/suite.json +1010 -0
- package/benchmarks/production-v3/variant.mjs +178 -0
- package/catalog/capabilities.json +2 -2
- package/docs/benchmark-codex-mcp-metadata-contract-v1.md +159 -0
- package/docs/benchmark-codex-mcp-metadata-contract-v2.md +182 -0
- package/docs/benchmark-codex-mcp-metadata-contract-v3.md +194 -0
- package/docs/benchmark-public-contract-addendum-v2.md +85 -0
- package/docs/benchmark-public-contract-addendum-v3.md +116 -0
- package/docs/command-reference-vietnamese.md +10 -7
- package/docs/context-window-policy.md +3 -3
- package/docs/distribution-standard.md +9 -9
- package/docs/languages.json +2 -2
- package/docs/operator-manual-vietnamese.md +12 -12
- package/docs/piagent-codex-benchmark-recovery-plan.md +1817 -0
- package/docs/piagent-core-evaluation-specs-2026-09-06.json +706 -0
- package/docs/piagent-core-improvement-plan.md +528 -0
- package/docs/piagent-core-offline-implementation-2026-09-06.md +219 -0
- package/docs/piagent-core-plan-checkpoint-2026-09-06.json +69 -0
- package/docs/piagent-core-reasoning-research-2026-09-06.md +225 -0
- package/docs/piagent-core-research-sources-2026-09-06.json +416 -0
- package/docs/piagent-open-session-wait.md +19 -0
- package/docs/piagent-runtime-benchmark-execution-plan.md +685 -0
- package/docs/publishing-for-teams.md +2 -0
- package/docs/quality-benchmark.md +128 -8
- package/docs/quickstart-vietnamese.md +2 -2
- package/docs/release-install-policy.md +32 -9
- package/docs/research/harness-next-evidence-review.md +113 -0
- package/docs/team-onboarding.md +7 -7
- package/docs/third-party-neutrality.md +7 -0
- package/docs/usage-observability.md +1 -1
- package/docs-site/architecture.html +1 -1
- package/docs-site/benchmark.html +1 -1
- package/docs-site/commands.html +1 -1
- package/docs-site/content/en/runtime.html +1 -1
- package/docs-site/content/en/whats-new.html +11 -0
- package/docs-site/content/runtime.html +2 -2
- package/docs-site/content/whats-new.html +11 -0
- package/docs-site/context-engine.html +1 -1
- package/docs-site/en/architecture.html +1 -1
- package/docs-site/en/benchmark.html +1 -1
- package/docs-site/en/commands.html +1 -1
- package/docs-site/en/context-engine.html +1 -1
- package/docs-site/en/index.html +1 -1
- package/docs-site/en/mcp-approval.html +1 -1
- package/docs-site/en/mcp-auth.html +1 -1
- package/docs-site/en/mcp-servers.html +1 -1
- package/docs-site/en/mcp.html +1 -1
- package/docs-site/en/permissions.html +1 -1
- package/docs-site/en/profiles.html +1 -1
- package/docs-site/en/quickstart.html +1 -1
- package/docs-site/en/runtime.html +2 -2
- package/docs-site/en/scope.html +1 -1
- package/docs-site/en/security.html +1 -1
- package/docs-site/en/team.html +1 -1
- package/docs-site/en/whats-new.html +12 -1
- package/docs-site/en/workflows.html +1 -1
- package/docs-site/index.html +1 -1
- package/docs-site/mcp-approval.html +1 -1
- package/docs-site/mcp-auth.html +1 -1
- package/docs-site/mcp-servers.html +1 -1
- package/docs-site/mcp.html +1 -1
- package/docs-site/permissions.html +1 -1
- package/docs-site/profiles.html +1 -1
- package/docs-site/quickstart.html +1 -1
- package/docs-site/runtime.html +3 -3
- package/docs-site/scope.html +1 -1
- package/docs-site/security.html +1 -1
- package/docs-site/team.html +1 -1
- package/docs-site/whats-new.html +12 -1
- package/docs-site/workflows.html +1 -1
- package/evals/architecture-conformance-v1/lane.json +18 -0
- package/evals/architecture-conformance-v1/runner.mjs +77 -0
- package/evals/fixtures/approved-host-contracts.invalid.json +43 -0
- package/evals/fixtures/approved-host-contracts.valid.json +43 -0
- package/evals/fixtures/benchmark-attempt-outcome.invalid.json +42 -0
- package/evals/fixtures/benchmark-attempt-outcome.valid.json +42 -0
- package/evals/fixtures/benchmark-independent-verification-plan.invalid.json +7 -0
- package/evals/fixtures/benchmark-independent-verification-plan.valid.json +21 -0
- package/evals/fixtures/composite-criterion.invalid.json +10 -0
- package/evals/fixtures/composite-criterion.valid.json +33 -0
- package/evals/fixtures/contract-family-library.invalid.json +42 -0
- package/evals/fixtures/contract-family-library.valid.json +41 -0
- package/evals/fixtures/contract-selection-recipe.invalid.json +29 -0
- package/evals/fixtures/contract-selection-recipe.valid.json +29 -0
- package/evals/fixtures/host-contract-plan.invalid.json +42 -0
- package/evals/fixtures/host-contract-plan.valid.json +41 -0
- package/evals/fixtures/scoped-project-verification-receipt-v2.invalid.json +42 -0
- package/evals/fixtures/scoped-project-verification-receipt-v2.valid.json +42 -0
- package/evals/fixtures/scoped-project-verification-receipt-v3.invalid.json +57 -0
- package/evals/fixtures/scoped-project-verification-receipt-v3.valid.json +57 -0
- package/evals/fixtures/scoped-verification-receipt-v1.invalid.json +40 -0
- package/evals/fixtures/scoped-verification-receipt-v1.valid.json +40 -0
- package/evals/fs5-artifact-archive/README.md +32 -0
- package/evals/fs5-artifact-archive/manifest.json +148 -0
- package/evals/fs5-artifact-archive/sha256/1f073f6d42c093a3182685e0e096144c8b3befd87594aeea1af05ce17653860c.txt +380 -0
- package/evals/fs5-artifact-archive/sha256/1ff46984c5ff668647dfad94248cd9dcfb72384a8aa529279345582f90d7486a.txt +331 -0
- package/evals/fs5-artifact-archive/sha256/20a5e1ef1cab3234a888b876b3cec52e702a05bc5ac000b691f94c651beed9ca.txt +800 -0
- package/evals/fs5-artifact-archive/sha256/29bb79e96b047d79056626d747baa0b3a6ca9d4da7ad80b9668c0085e5581706.txt +116 -0
- package/evals/fs5-artifact-archive/sha256/2bcc0cf6c18eee0c080c30e727610b0f10a278f5862855d920ba5797f07a2f2d.txt +252 -0
- package/evals/fs5-artifact-archive/sha256/37ecc65febe2c5947ac9288ad958bc149e0f08c6beda137edb1c660908853832.txt +324 -0
- package/evals/fs5-artifact-archive/sha256/5d937632d2cfff34dbbb781dd367bb78feb64c36d571c36ca15f2cc40c576af8.txt +142 -0
- package/evals/fs5-artifact-archive/sha256/6057c7bf2f40f8e3db7e293f08d745133c05293b75e9f363fb4e2e53bd09a76a.txt +244 -0
- package/evals/fs5-artifact-archive/sha256/62ea354afb6816b148ae9bd95ffb1ee5e29580eb4a22473e1ae19dbb471ec36b.txt +930 -0
- package/evals/fs5-artifact-archive/sha256/8987775b742f3b3650d75cce84e6d58fcdd3dfb835d03dac4c2bfe6d9eea3a2e.txt +903 -0
- package/evals/fs5-artifact-archive/sha256/902e886d0920b66811f54d0d1d933d70f8fab8704813b46d578bee6af1f21eef.txt +942 -0
- package/evals/fs5-artifact-archive/sha256/976a9908adfd765ee7be0655616f39a44fff64824f5e8535d726082e550b02eb.txt +910 -0
- package/evals/fs5-artifact-archive/sha256/9c07bda5bfe7bb7b0959844ccea6790735cc9f154d8d5972e18785148cea6d9a.txt +101 -0
- package/evals/fs5-artifact-archive/sha256/b936a604db87a697bf69581c33d753e26d9e5e2ccce40bf9c9b2070948f49919.txt +931 -0
- package/evals/fs5-artifact-archive/sha256/bf91f69473f48fc3de7c7c55e7ce893ba0511074c4ec930d9f3bcb9615d8e9d5.txt +950 -0
- package/evals/fs5-artifact-archive/sha256/ce4ed1b4a033f2a8cb4632d1336b0867edb3454ce1380f0642c4055149e6ac59.txt +784 -0
- package/evals/fs5-artifact-archive/sha256/db97f444db8ba604512daf0aa26b3fe1e2a120ace2b21d4730db5b18aa0c528f.txt +950 -0
- package/evals/fs5-causal-arm.v1.json +9 -5
- package/evals/fs5-pilot-protocol.v1.json +1 -1
- package/evals/fs5-pilot-protocol.v4.json +4 -4
- package/evals/fs5-pilot-protocol.v5.json +6 -5
- package/evals/harness-next/development-corpus.mjs +106 -0
- package/evals/harness-next/resource-budget-probe.mjs +38 -0
- package/evals/harness-next/run-calibration.mjs +83 -0
- package/evals/harness-next/run-resource-probe.mjs +55 -0
- package/evals/harness-next/selected-development-corpus.mjs +27 -0
- package/evals/harness-next.v1.json +51 -0
- package/evals/long-horizon-v1/README.md +19 -0
- package/evals/long-horizon-v1/runner.mjs +116 -48
- package/evals/long-horizon-v1/worker.mjs +77 -30
- package/evals/private-holdout-v1/CURRENT_BOUNDARY.md +53 -0
- package/evals/private-holdout-v1/README.md +5 -2
- package/evals/private-holdout-v1/public-exposure.v2.json +273 -0
- package/package.json +9 -4
- package/packages/piagent-core/README.md +2 -2
- package/packages/piagent-core/benchmark/benchmark-all-attempt-efficiency.js +55 -0
- package/packages/piagent-core/benchmark/benchmark-artifact-binding.js +49 -0
- package/packages/piagent-core/benchmark/benchmark-bootstrap.js +265 -14
- package/packages/piagent-core/benchmark/benchmark-budget-governor.js +271 -0
- package/packages/piagent-core/benchmark/benchmark-campaign.js +592 -0
- package/packages/piagent-core/benchmark/benchmark-candidate.js +8 -3
- package/packages/piagent-core/benchmark/benchmark-checkpoint.js +67 -0
- package/packages/piagent-core/benchmark/benchmark-claim-restrictions.js +269 -2
- package/packages/piagent-core/benchmark/benchmark-cli.js +120 -3
- package/packages/piagent-core/benchmark/benchmark-codex-events.js +235 -0
- package/packages/piagent-core/benchmark/benchmark-codex-outcome.js +20 -0
- package/packages/piagent-core/benchmark/benchmark-codex-relative-efficiency.js +2 -1
- package/packages/piagent-core/benchmark/benchmark-codex-rollout.js +521 -0
- package/packages/piagent-core/benchmark/benchmark-codex.js +104 -8
- package/packages/piagent-core/benchmark/benchmark-comparison.js +284 -1
- package/packages/piagent-core/benchmark/benchmark-core.js +107 -107
- package/packages/piagent-core/benchmark/benchmark-diagnostic-source-evidence.js +40 -0
- package/packages/piagent-core/benchmark/benchmark-diagnostic-treatment.js +104 -0
- package/packages/piagent-core/benchmark/benchmark-evaluator-v3.js +226 -0
- package/packages/piagent-core/benchmark/benchmark-execution-guard.js +23 -1
- package/packages/piagent-core/benchmark/benchmark-forensics.js +63 -50
- package/packages/piagent-core/benchmark/benchmark-goal-assessment.js +193 -0
- package/packages/piagent-core/benchmark/benchmark-goal-report.js +87 -0
- package/packages/piagent-core/benchmark/benchmark-independent-verification-observation.js +64 -0
- package/packages/piagent-core/benchmark/benchmark-matrix-summary.js +141 -0
- package/packages/piagent-core/benchmark/benchmark-matrix.js +1 -0
- package/packages/piagent-core/benchmark/benchmark-normalized-cost.js +62 -0
- package/packages/piagent-core/benchmark/benchmark-pi-home.js +1 -0
- package/packages/piagent-core/benchmark/benchmark-preflight.js +69 -8
- package/packages/piagent-core/benchmark/benchmark-process.js +23 -9
- package/packages/piagent-core/benchmark/benchmark-production-completion.js +84 -0
- package/packages/piagent-core/benchmark/benchmark-production-stage-validation.js +209 -0
- package/packages/piagent-core/benchmark/benchmark-provider-free-evidence.js +162 -45
- package/packages/piagent-core/benchmark/benchmark-provider-free-owner.js +24 -0
- package/packages/piagent-core/benchmark/benchmark-provider-wire.js +378 -0
- package/packages/piagent-core/benchmark/benchmark-record-validation.d.ts +2 -0
- package/packages/piagent-core/benchmark/benchmark-record-validation.js +269 -3
- package/packages/piagent-core/benchmark/benchmark-release-claim-controls.js +132 -0
- package/packages/piagent-core/benchmark/benchmark-report.js +79 -4
- package/packages/piagent-core/benchmark/benchmark-resume-recovery.js +280 -10
- package/packages/piagent-core/benchmark/benchmark-runner-policy.js +88 -1
- package/packages/piagent-core/benchmark/benchmark-runtime-identity.js +50 -1
- package/packages/piagent-core/benchmark/benchmark-runtime.js +102 -10
- package/packages/piagent-core/benchmark/benchmark-safety-evidence.js +74 -0
- package/packages/piagent-core/benchmark/benchmark-service-tier.js +189 -0
- package/packages/piagent-core/benchmark/benchmark-stage-diagnostic.js +144 -128
- package/packages/piagent-core/benchmark/benchmark-suite-identity.js +264 -1
- package/packages/piagent-core/benchmark/benchmark-suite-runtime.js +4 -0
- package/packages/piagent-core/benchmark/benchmark-suite.js +211 -4
- package/packages/piagent-core/benchmark/benchmark-summary-support.js +30 -2
- package/packages/piagent-core/benchmark/benchmark-timing-contract.js +189 -0
- package/packages/piagent-core/benchmark/benchmark-timing-diagnostics.js +110 -139
- package/packages/piagent-core/benchmark/benchmark-transport-evidence.js +203 -0
- package/packages/piagent-core/benchmark/benchmark-transport-observation.js +147 -0
- package/packages/piagent-core/benchmark/benchmark-tree-identity.js +2 -1
- package/packages/piagent-core/benchmark/benchmark-usage.js +395 -33
- package/packages/piagent-core/benchmark/benchmark-workflow.js +136 -38
- package/packages/piagent-core/benchmark/fs5-pilot-protocol.js +21 -2
- package/packages/piagent-core/capabilities/runtime-integrity.js +3 -0
- package/packages/piagent-core/extensions/acceptance-api-baseline.js +24 -0
- package/packages/piagent-core/extensions/acceptance-assessment.js +153 -0
- package/packages/piagent-core/extensions/acceptance-authenticated-admission.d.ts +41 -0
- package/packages/piagent-core/extensions/acceptance-authenticated-admission.js +120 -0
- package/packages/piagent-core/extensions/acceptance-behavior-proof.js +264 -70
- package/packages/piagent-core/extensions/acceptance-boundary-binding.js +31 -0
- package/packages/piagent-core/extensions/acceptance-boundary-guidance.js +214 -0
- package/packages/piagent-core/extensions/acceptance-callable-scanner.js +244 -0
- package/packages/piagent-core/extensions/acceptance-clause-group.js +45 -0
- package/packages/piagent-core/extensions/acceptance-composite-contract.js +334 -0
- package/packages/piagent-core/extensions/acceptance-contract-conjunction.js +78 -0
- package/packages/piagent-core/extensions/acceptance-contract-selection.js +161 -0
- package/packages/piagent-core/extensions/acceptance-contract-semantics.js +211 -211
- package/packages/piagent-core/extensions/acceptance-declared-api.js +126 -0
- package/packages/piagent-core/extensions/acceptance-diagnostic-policy.ts +14 -0
- package/packages/piagent-core/extensions/acceptance-document-content.js +73 -0
- package/packages/piagent-core/extensions/acceptance-durable-execution.d.ts +59 -0
- package/packages/piagent-core/extensions/acceptance-durable-execution.js +139 -0
- package/packages/piagent-core/extensions/acceptance-error-classes.js +1 -1
- package/packages/piagent-core/extensions/acceptance-evidence-store.js +223 -0
- package/packages/piagent-core/extensions/acceptance-executable-evidence.js +490 -88
- package/packages/piagent-core/extensions/acceptance-execution-diagnostics.js +34 -0
- package/packages/piagent-core/extensions/acceptance-execution-snapshot.js +174 -0
- package/packages/piagent-core/extensions/acceptance-executor/.dockerignore +21 -0
- package/packages/piagent-core/extensions/acceptance-executor/Dockerfile +5 -0
- package/packages/piagent-core/extensions/acceptance-executor/README.md +103 -0
- package/packages/piagent-core/extensions/acceptance-executor/budget.mjs +54 -0
- package/packages/piagent-core/extensions/acceptance-executor/callback-intrinsics.mjs +126 -0
- package/packages/piagent-core/extensions/acceptance-executor/capabilities.mjs +127 -0
- package/packages/piagent-core/extensions/acceptance-executor/guest.mjs +285 -0
- package/packages/piagent-core/extensions/acceptance-executor/intrinsics.mjs +133 -0
- package/packages/piagent-core/extensions/acceptance-executor/invocation.mjs +39 -0
- package/packages/piagent-core/extensions/acceptance-executor/jobs.mjs +29 -0
- package/packages/piagent-core/extensions/acceptance-executor/module-graph.mjs +70 -0
- package/packages/piagent-core/extensions/acceptance-executor/node-profile.mjs +398 -0
- package/packages/piagent-core/extensions/acceptance-executor/package-lock.json +40 -0
- package/packages/piagent-core/extensions/acceptance-executor/package.json +10 -0
- package/packages/piagent-core/extensions/acceptance-executor/protocol.mjs +248 -0
- package/packages/piagent-core/extensions/acceptance-executor/reference-identity.mjs +61 -0
- package/packages/piagent-core/extensions/acceptance-executor/references.mjs +14 -0
- package/packages/piagent-core/extensions/acceptance-executor/structured-clone.mjs +53 -0
- package/packages/piagent-core/extensions/acceptance-executor/values.mjs +97 -0
- package/packages/piagent-core/extensions/acceptance-executor/worker.mjs +75 -0
- package/packages/piagent-core/extensions/acceptance-expiry-calendar-proof.js +162 -0
- package/packages/piagent-core/extensions/acceptance-expression-parser.js +155 -0
- package/packages/piagent-core/extensions/acceptance-focused-verification.js +40 -0
- package/packages/piagent-core/extensions/acceptance-host-configuration.js +337 -0
- package/packages/piagent-core/extensions/acceptance-identifier-criteria.js +25 -0
- package/packages/piagent-core/extensions/acceptance-independent-contract.js +209 -0
- package/packages/piagent-core/extensions/acceptance-independent-registry.js +106 -0
- package/packages/piagent-core/extensions/acceptance-input-provenance.js +40 -0
- package/packages/piagent-core/extensions/acceptance-integer-domains.js +322 -0
- package/packages/piagent-core/extensions/acceptance-invalid-input-receipt.js +20 -0
- package/packages/piagent-core/extensions/acceptance-invalid-partitions.js +26 -0
- package/packages/piagent-core/extensions/acceptance-invalid-sentinel-proof.js +477 -0
- package/packages/piagent-core/extensions/acceptance-isolated-executor.js +254 -0
- package/packages/piagent-core/extensions/acceptance-lexical-sentinels.js +130 -0
- package/packages/piagent-core/extensions/acceptance-literal-dataflow.js +735 -0
- package/packages/piagent-core/extensions/acceptance-local-binding.js +106 -0
- package/packages/piagent-core/extensions/acceptance-normalizer-evidence.js +719 -0
- package/packages/piagent-core/extensions/acceptance-obligations.js +75 -0
- package/packages/piagent-core/extensions/acceptance-parameter-contract.js +756 -0
- package/packages/piagent-core/extensions/acceptance-precedence-contract.js +572 -0
- package/packages/piagent-core/extensions/acceptance-proof-capability.js +26 -0
- package/packages/piagent-core/extensions/acceptance-receipt.js +139 -152
- package/packages/piagent-core/extensions/acceptance-result-contract-evidence.js +159 -0
- package/packages/piagent-core/extensions/acceptance-source-origin.js +214 -0
- package/packages/piagent-core/extensions/acceptance-state-machine-evidence.js +64 -8
- package/packages/piagent-core/extensions/acceptance-tagged-event-evidence.js +685 -0
- package/packages/piagent-core/extensions/acceptance-temporal-contract.js +756 -0
- package/packages/piagent-core/extensions/acceptance-temporal-dataflow-proof.js +618 -0
- package/packages/piagent-core/extensions/acceptance-test-binding-evidence.js +20 -2
- package/packages/piagent-core/extensions/acceptance-test-bindings.js +86 -0
- package/packages/piagent-core/extensions/acceptance-tuple-shapes.js +12 -0
- package/packages/piagent-core/extensions/context-engine.js +11 -6
- package/packages/piagent-core/extensions/criterion-context-pack.js +96 -4
- package/packages/piagent-core/extensions/failure-types.ts +2 -2
- package/packages/piagent-core/extensions/guard-types.ts +5 -1
- package/packages/piagent-core/extensions/piagent-guard.ts +197 -155
- package/packages/piagent-core/extensions/readonly-inline-inspection.ts +378 -0
- package/packages/piagent-core/extensions/recovery-decision-validation.ts +92 -0
- package/packages/piagent-core/extensions/shell-write-targets.js +15 -14
- package/packages/piagent-core/extensions/task-contract-view.js +4 -4
- package/packages/piagent-core/extensions/task-digest-migration.js +2 -2
- package/packages/piagent-core/extensions/task-journal.js +48 -3
- package/packages/piagent-core/extensions/task-runtime-audit.js +8 -2
- package/packages/piagent-core/extensions/task-state.js +74 -38
- package/packages/piagent-core/extensions/task-terminal-disposition.js +10 -0
- package/packages/piagent-core/extensions/trajectory-contract.ts +5 -0
- package/packages/piagent-core/extensions/verification-intelligence.js +60 -8
- package/packages/piagent-core/extensions/workflow-catalog.ts +98 -0
- package/packages/piagent-core/extensions/workspace-revision.js +48 -0
- package/packages/piagent-core/extensions/workspace-verification-snapshot.js +13 -0
- package/packages/piagent-core/package.json +3 -3
- package/packages/piagent-core/policies/base-policy.json +1 -0
- package/packages/piagent-core/runtime/context/adaptive-planner.ts +7 -3
- package/packages/piagent-core/runtime/hooks/agent-start-hook.ts +60 -59
- package/packages/piagent-core/runtime/hooks/agent-start-types.ts +48 -0
- package/packages/piagent-core/runtime/hooks/completion-hook.ts +76 -83
- package/packages/piagent-core/runtime/hooks/input-hook.ts +92 -7
- package/packages/piagent-core/runtime/hooks/protected-refusal-completion.ts +86 -0
- package/packages/piagent-core/runtime/hooks/session-hooks.ts +5 -2
- package/packages/piagent-core/runtime/hooks/session-start-hook.ts +50 -18
- package/packages/piagent-core/runtime/hooks/tool-result-hook.ts +53 -20
- package/packages/piagent-core/runtime/hooks/tool-result-metadata.ts +9 -0
- package/packages/piagent-core/runtime/hooks/tool-result-navigation.ts +36 -0
- package/packages/piagent-core/runtime/hooks/tool-result-retrieval.ts +42 -0
- package/packages/piagent-core/runtime/input/chat-images.ts +1 -1
- package/packages/piagent-core/runtime/inspection/activity-event-adapter.ts +1 -1
- package/packages/piagent-core/runtime/inspection/same-process-bridge-proof.ts +1 -1
- package/packages/piagent-core/runtime/inspection/task-handoff-history.ts +2 -2
- package/packages/piagent-core/runtime/inspection/tool-failure-classification.ts +49 -8
- package/packages/piagent-core/runtime/inspection/webui-snapshot.ts +61 -15
- package/packages/piagent-core/runtime/inspection/workspace-file-reader.ts +2 -132
- package/packages/piagent-core/runtime/model/provider-wire-fingerprint.ts +20 -0
- package/packages/piagent-core/runtime/model/service-tier-runtime.ts +242 -0
- package/packages/piagent-core/runtime/policy/source-worktree-transaction.ts +75 -14
- package/packages/piagent-core/runtime/product/activity-inspector.ts +6 -3
- package/packages/piagent-core/runtime/product/edit-verifier-timing.ts +26 -0
- package/packages/piagent-core/runtime/product/efficiency-metrics.ts +12 -7
- package/packages/piagent-core/runtime/product/operator-projections.ts +8 -3
- package/packages/piagent-core/runtime/product/task-usage-hooks.ts +78 -0
- package/packages/piagent-core/runtime/product/task-usage.ts +104 -0
- package/packages/piagent-core/runtime/quality/protected-refusal-handoff.ts +35 -0
- package/packages/piagent-core/runtime/recovery/acceptance-recovery-guidance.ts +21 -0
- package/packages/piagent-core/runtime/recovery/continuation-budget.ts +11 -2
- package/packages/piagent-core/runtime/recovery/diagnostic-delivery.ts +61 -0
- package/packages/piagent-core/runtime/recovery/handoff-projection.ts +192 -57
- package/packages/piagent-core/runtime/recovery/handoff-verifier-binding.ts +24 -0
- package/packages/piagent-core/runtime/recovery/independent-verification-recovery.ts +79 -0
- package/packages/piagent-core/runtime/recovery/recovery-policy.ts +27 -39
- package/packages/piagent-core/runtime/recovery/resume-state.ts +8 -5
- package/packages/piagent-core/runtime/registration/activity-inspector-command.ts +2 -1
- package/packages/piagent-core/runtime/registration/context-commands.ts +12 -8
- package/packages/piagent-core/runtime/registration/operator-catalogs.ts +4 -9
- package/packages/piagent-core/runtime/registration/policy-tools.ts +5 -3
- package/packages/piagent-core/runtime/registration/session-commands.ts +9 -7
- package/packages/piagent-core/runtime/registration/session-wait-tool.ts +54 -0
- package/packages/piagent-core/runtime/registration/task-completion-tools.ts +27 -11
- package/packages/piagent-core/runtime/registration/task-start-conditional-mutation.ts +45 -0
- package/packages/piagent-core/runtime/registration/task-start-guidance.ts +5 -2
- package/packages/piagent-core/runtime/registration/task-start-tool.ts +23 -23
- package/packages/piagent-core/runtime/registration/workflow-commands.ts +64 -93
- package/packages/piagent-core/runtime/session/completion-signals.ts +23 -0
- package/packages/piagent-core/runtime/session/message-signals.ts +6 -22
- package/packages/piagent-core/runtime/session/retrieval-evidence-runtime.ts +188 -0
- package/packages/piagent-core/runtime/session/runtime-state.ts +29 -35
- package/packages/piagent-core/runtime/session/system-prompt.ts +3 -3
- package/packages/piagent-core/runtime/session/uncertain-send-continuation.ts +231 -0
- package/packages/piagent-core/runtime/session/usage.ts +4 -3
- package/packages/piagent-core/runtime/tools/apply-patch-tool.ts +6 -6
- package/packages/piagent-core/runtime/tools/phase-tool-runtime.ts +1 -1
- package/packages/piagent-core/runtime/tools/phase-tools.ts +3 -2
- package/packages/piagent-core/runtime/tools/session-wait.ts +42 -0
- package/packages/piagent-core/runtime/tools/tool-groups.ts +10 -3
- package/packages/piagent-core/runtime/trajectory/trajectory-runtime.ts +192 -5
- package/packages/piagent-core/runtime/trajectory/trajectory-types.ts +3 -2
- package/packages/piagent-core/runtime/verification/acceptance-api-baseline.js +488 -0
- package/packages/piagent-core/runtime/verification/completion-preparation.ts +22 -0
- package/packages/piagent-core/runtime/verification/composite-completion-finalizer.ts +105 -0
- package/packages/piagent-core/runtime/verification/composite-journal-anchor.ts +181 -0
- package/packages/piagent-core/runtime/verification/composite-runtime-binding.ts +76 -0
- package/packages/piagent-core/runtime/verification/composite-scoped-mediation.ts +318 -0
- package/packages/piagent-core/runtime/verification/composite-session-persistence.ts +175 -0
- package/packages/piagent-core/runtime/verification/composite-task-publication.ts +273 -0
- package/packages/piagent-core/runtime/verification/exact-verifier-reuse.ts +11 -11
- package/packages/piagent-core/runtime/verification/host-project-verification.ts +71 -0
- package/packages/piagent-core/runtime/verification/independent-acceptance-runtime.ts +500 -0
- package/packages/piagent-core/runtime/verification/runtime-assurance-facts.ts +500 -0
- package/packages/piagent-core/runtime/verification/runtime-contract-runner.ts +107 -0
- package/packages/piagent-core/runtime/verification/shell-verification-snapshots.ts +56 -0
- package/packages/piagent-core/runtime/workflows/change-clarification.ts +61 -0
- package/packages/piagent-core/runtime/workflows/input-routing.ts +21 -14
- package/packages/piagent-core/runtime/workflows/runtime-intake-compaction.ts +44 -7
- package/packages/piagent-core/runtime/workflows/task-followup-policy.ts +163 -0
- package/packages/piagent-core/runtime/workflows/task-intake.ts +221 -242
- package/packages/piagent-core/runtime/workflows/task-repair-intent.ts +67 -0
- package/packages/piagent-core/runtime/workflows/task-scope-resolution.ts +83 -0
- package/packages/piagent-core/runtime/workflows/webui-runtime-command.ts +5 -2
- package/packages/piagent-core/runtime/workflows/webui-workflow.ts +39 -32
- package/packages/piagent-core/runtime/workflows/workflow-follow-up.ts +33 -0
- package/packages/piagent-core/security/workspace-file-reader.ts +132 -0
- package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-B4XehTCU.js → MarkdownMessage-DGk34zJT.js} +1 -1
- package/packages/piagent-webui/dist/client/assets/{index-BV-_03yl.css → index-DVahUVWy.css} +1 -1
- package/packages/piagent-webui/dist/client/assets/index-DiKRmCEP.js +176 -0
- package/packages/piagent-webui/dist/client/index.html +2 -2
- package/packages/piagent-webui/extension/dispatch-user-entry.ts +13 -0
- package/packages/piagent-webui/extension/piagent-webui.ts +10 -2
- package/packages/piagent-webui/extension/review-controller.ts +29 -6
- package/packages/piagent-webui/extension/same-session-bridge.ts +6 -7
- package/packages/piagent-webui/extension/session-stream-adapter.ts +60 -11
- package/packages/piagent-webui/extension/source-mutation-controller.ts +29 -3
- package/packages/piagent-webui/extension/source-open-controller.ts +29 -7
- package/packages/piagent-webui/extension/source-revert-controller.ts +26 -3
- package/packages/piagent-webui/gateway/gateway-events.ts +22 -5
- package/packages/piagent-webui/gateway/gateway-service.ts +7 -2
- package/packages/piagent-webui/gateway/gateway-session-stream.ts +141 -19
- package/packages/piagent-webui/gateway/rpc-ui-context.ts +34 -9
- package/packages/piagent-webui/gateway/runtime-command-controller.ts +46 -6
- package/packages/piagent-webui/gateway/runtime-session-controls.ts +17 -2
- package/packages/piagent-webui/gateway/session-catalog.ts +9 -3
- package/packages/piagent-webui/gateway/session-command-controller.ts +29 -9
- package/packages/piagent-webui/gateway/session-effective-options.ts +2 -9
- package/packages/piagent-webui/gateway/session-inspection-registry.ts +11 -2
- package/packages/piagent-webui/gateway/session-operation-settlement.ts +28 -0
- package/packages/piagent-webui/gateway/session-prompt-dispatch.ts +37 -0
- package/packages/piagent-webui/gateway/session-runtime-close.ts +36 -0
- package/packages/piagent-webui/gateway/session-runtime-factory.ts +183 -6
- package/packages/piagent-webui/gateway/session-runtime-ownership.ts +48 -0
- package/packages/piagent-webui/gateway/session-runtime-state.ts +24 -0
- package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +114 -114
- package/packages/piagent-webui/server/core-inspection-provider.ts +73 -14
- package/packages/piagent-webui/server/loopback-server.ts +9 -7
- package/packages/piagent-webui/server/terminal-delivery-receipt.ts +144 -0
- package/packages/piagent-webui/server/transcript-projection.ts +65 -12
- package/packages/piagent-webui/shared/message-correlation.ts +25 -0
- package/schemas/approved-host-contracts.schema.json +973 -0
- package/schemas/benchmark-attempt-outcome.schema.json +303 -0
- package/schemas/benchmark-independent-verification-plan.schema.json +34 -0
- package/schemas/composite-criterion.schema.json +788 -0
- package/schemas/contract-family-library.schema.json +28 -0
- package/schemas/contract-selection-recipe.schema.json +31 -0
- package/schemas/failure-evidence.schema.json +2 -2
- package/schemas/helper-request.schema.json +16 -4
- package/schemas/host-contract-plan.schema.json +25 -0
- package/schemas/piagent-webui/gateway-protocol-v1.schema.json +16 -2
- package/schemas/piagent-webui/runtime-command-v1.schema.json +26 -3
- package/schemas/piagent-webui/runtime-event-v2.schema.json +32 -1
- package/schemas/piagent-webui/session-live-state-v1.schema.json +2 -0
- package/schemas/piagent-webui/snapshot-v1.schema.json +3 -0
- package/schemas/piagent-webui/source-change-v1.schema.json +12 -0
- package/schemas/piagent-webui/transcript-v1.schema.json +2 -1
- package/schemas/public-contracts/codex-mcp-metadata-v1.schema.json +206 -0
- package/schemas/public-contracts/codex-mcp-metadata-v2.schema.json +228 -0
- package/schemas/public-contracts/codex-mcp-metadata-v3.schema.json +233 -0
- package/schemas/public-contracts/revocation-v1.schema.json +87 -0
- package/schemas/public-contracts/subscription-contract-v1.schema.json +76 -0
- package/schemas/scoped-project-verification-receipt-v2.schema.json +83 -0
- package/schemas/scoped-project-verification-receipt-v3.schema.json +295 -0
- package/schemas/scoped-verification-receipt-v1.schema.json +76 -0
- package/schemas/task-contract.schema.json +30 -0
- package/scripts/approve-independent-verification.mjs +57 -0
- package/scripts/benchmark-arm-observer.mjs +320 -0
- package/scripts/benchmark-budget-ipc.mjs +34 -0
- package/scripts/benchmark-budget-launcher.mjs +91 -0
- package/scripts/benchmark-budget-processes.mjs +133 -0
- package/scripts/benchmark-budget-publication.mjs +228 -0
- package/scripts/benchmark-budget-runtime.mjs +136 -0
- package/scripts/benchmark-candidate-ablation.mjs +265 -0
- package/scripts/benchmark-codex-journey.mjs +406 -0
- package/scripts/benchmark-independent-verification.mjs +628 -0
- package/scripts/benchmark-journey-outcome.mjs +92 -0
- package/scripts/benchmark-production-v3-human-review.mjs +192 -0
- package/scripts/benchmark-public-input-catalog.mjs +158 -0
- package/scripts/benchmark-registered-plan-author.mjs +355 -0
- package/scripts/benchmark-registered-plan-recipe-support.mjs +85 -0
- package/scripts/benchmark-registered-plan-recipes-backend.mjs +165 -0
- package/scripts/benchmark-registered-plan-recipes-data.mjs +164 -0
- package/scripts/benchmark-registered-plan-recipes-platform.mjs +165 -0
- package/scripts/benchmark-runner-completion.mjs +133 -0
- package/scripts/benchmark-runner-configuration.mjs +159 -0
- package/scripts/benchmark-runner-core.mjs +217 -388
- package/scripts/benchmark-runner-finalization.mjs +140 -27
- package/scripts/benchmark-runner-provider-boundary.mjs +44 -0
- package/scripts/benchmark-runner-schedule.mjs +256 -0
- package/scripts/benchmark-runner-support.mjs +136 -4
- package/scripts/benchmark-runner.mjs +95 -18
- package/scripts/benchmark-scoped-broker-custody.mjs +483 -0
- package/scripts/benchmark-scoped-docs-verifier.mjs +203 -0
- package/scripts/benchmark-scoped-frozen-qualification.mjs +676 -0
- package/scripts/benchmark-scoped-pi-router.mjs +196 -0
- package/scripts/benchmark-scoped-project-verification-receipt.mjs +49 -0
- package/scripts/benchmark-scoped-project-verifier.mjs +461 -0
- package/scripts/benchmark-scoped-session-custody.mjs +599 -0
- package/scripts/benchmark-scoped-session-factory.mjs +342 -0
- package/scripts/benchmark-scoped-tool-broker.mjs +523 -0
- package/scripts/benchmark-scoped-verification-supervisor.mjs +773 -0
- package/scripts/benchmark-session-evaluator.mjs +169 -0
- package/scripts/benchmark-session-provider-boundary.mjs +62 -0
- package/scripts/benchmark-session-usage.mjs +28 -0
- package/scripts/benchmark-session.mjs +343 -50
- package/scripts/benchmark-webui-journey.mjs +678 -0
- package/scripts/check-public-wording.mjs +46 -0
- package/scripts/fs4-readiness-evaluation.mjs +7 -1
- package/scripts/phase-tools-evaluation.mjs +1 -0
- package/scripts/pi-auto.sh +7 -1
- package/scripts/pi-usage-history.mjs +201 -11
- package/scripts/piagent-cli.mjs +8 -0
- package/scripts/private-holdout-readiness.mjs +7 -1
- package/scripts/public-evaluation-exposure.mjs +140 -0
- package/scripts/select-independent-verification.mjs +62 -0
- package/scripts/typescript-loader.mjs +135 -1
- package/scripts/update-global.mjs +6 -7
- package/scripts/verify-local.sh +36 -55
- package/scripts/verify-release-identity.mjs +5 -3
- package/packages/piagent-webui/dist/client/assets/index-CgOEyRPG.js +0 -176
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
import { createHash, createHmac, KeyObject, randomUUID, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { types } from "node:util";
|
|
5
|
+
import { compileCompositeContract } from "../../extensions/acceptance-composite-contract.js";
|
|
6
|
+
import { openCompositeJournalAnchor } from "./composite-journal-anchor.ts";
|
|
7
|
+
export const COMPOSITE_PLAN_CONTEXT_VERSION = "composite-plan-context-v1", COMPOSITE_FACT_RECORD_VERSION = 2;
|
|
8
|
+
export const COMPOSITE_ASSURANCE_VERSION = "composite-assurance-runtime-v1", COMPOSITE_ASSURANCE = "bounded-composite-contract-tested";
|
|
9
|
+
const JOURNAL_VERSION = "composite-assurance-journal-v1";
|
|
10
|
+
const HASH = /^[a-f0-9]{64}$/, REVISION = /^[a-f0-9]{40}(?:[a-f0-9]{24})?$/;
|
|
11
|
+
const ID = /^[a-zA-Z0-9][a-zA-Z0-9:._-]{0,159}$/, FACT_ID = /^[a-z][a-z0-9._-]{0,63}$/;
|
|
12
|
+
const MAX_EVENTS = 512, MAX_BYTES = 32 * 1024 * 1024;
|
|
13
|
+
const UNKNOWN = new Set(["unsupported-input", "ambiguous-input", "missing-fact", "stale-binding", "invalid-approval",
|
|
14
|
+
"unavailable-native-boundary", "incomplete-mediation", "untrusted-fact", "response-mismatch", "cancelled", "superseded"]);
|
|
15
|
+
const ERRORS = new Set(["producer-crash", "capture-error", "journal-error", "producer-timeout", "producer-cancelled", "cleanup-ambiguous"]);
|
|
16
|
+
const FAILURES = new Set(["observed-counterexample", "content-mismatch", "verification-failed", "policy-violation",
|
|
17
|
+
"scope-violation", "persistence-mismatch", "delivery-mismatch", "safety-stop"]);
|
|
18
|
+
const BASE_FIELDS = ["projectId", "projectHead", "sourceDigest", "materialSnapshotDigest", "suiteDigest", "configDigest",
|
|
19
|
+
"armDigest", "taskRunId", "sessionId", "operatorRequestDigest", "taskContractDigest", "criterionIndex", "criterionId",
|
|
20
|
+
"criterionHash", "runtimeGeneration", "phaseHeadDigest", "producerManifestDigest", "verifierDigest"];
|
|
21
|
+
const RESPONSE_FIELDS = ["origin", "entryId", "digest", "byteLength", "operationRef", "messageRequestId"];
|
|
22
|
+
const FACT_FIELDS = ["schemaVersion", "factId", "factSpecDigest", "kind", "producerId", "producerDigest", "ruleDigest",
|
|
23
|
+
"binding", "status", "observationDigest", "counterexampleRef", "reasonCodes"];
|
|
24
|
+
const factCapabilities = new WeakMap(), producerCapabilities = new WeakMap(), reservationCapabilities = new WeakMap(),
|
|
25
|
+
preparationCapabilities = new WeakMap(), aggregatePreparationCapabilities = new WeakMap(), aggregateCapabilities = new WeakMap(), publicationCapabilities = new WeakMap();
|
|
26
|
+
const sha = (value: string | Buffer) => createHash("sha256").update(value).digest("hex");
|
|
27
|
+
const freeze = <T>(value: T): T => {
|
|
28
|
+
if (value && typeof value === "object" && !Object.isFrozen(value)) {
|
|
29
|
+
for (const child of Object.values(value as object)) freeze(child);
|
|
30
|
+
Object.freeze(value);
|
|
31
|
+
}
|
|
32
|
+
return value;
|
|
33
|
+
};
|
|
34
|
+
function exact(value: any, fields: string[], label: string): any {
|
|
35
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || types.isProxy(value)
|
|
36
|
+
|| ![Object.prototype, null].includes(Object.getPrototypeOf(value))) throw new TypeError(`Invalid ${label}`);
|
|
37
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
38
|
+
if (Reflect.ownKeys(descriptors).length !== fields.length
|
|
39
|
+
|| fields.some(field => !descriptors[field] || !("value" in descriptors[field]) || !descriptors[field].enumerable)) {
|
|
40
|
+
throw new TypeError(`Invalid ${label} fields`);
|
|
41
|
+
}
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
function copy(value: any, depth = 0, budget = { nodes: 0, bytes: 0 }): any {
|
|
45
|
+
if (++budget.nodes > 65_536 || depth > 20) throw new TypeError("Composite data exceeds traversal bounds");
|
|
46
|
+
if (value === null || typeof value === "boolean") return value;
|
|
47
|
+
if (typeof value === "number") { if (!Number.isSafeInteger(value)) throw new TypeError("Invalid composite number"); return value; }
|
|
48
|
+
if (typeof value === "string") {
|
|
49
|
+
if (!(value as any).isWellFormed()) throw new TypeError("Invalid composite string");
|
|
50
|
+
budget.bytes += Buffer.byteLength(value); if (budget.bytes > 2 * 1024 * 1024) throw new TypeError("Composite data exceeds byte bound");
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
if (!value || typeof value !== "object" || types.isProxy(value)) throw new TypeError("Invalid composite data");
|
|
54
|
+
const array = Array.isArray(value), prototype = Object.getPrototypeOf(value);
|
|
55
|
+
if (array ? prototype !== Array.prototype : ![Object.prototype, null].includes(prototype)) throw new TypeError("Invalid composite prototype");
|
|
56
|
+
const descriptors = Object.getOwnPropertyDescriptors(value), keys = Reflect.ownKeys(descriptors);
|
|
57
|
+
if (array && (keys.length !== value.length + 1 || value.length > 4096)) throw new TypeError("Invalid composite array");
|
|
58
|
+
if (!array && keys.length > 64) throw new TypeError("Invalid composite object");
|
|
59
|
+
const output: any = array ? [] : {};
|
|
60
|
+
for (const key of keys) {
|
|
61
|
+
if (array && key === "length") continue;
|
|
62
|
+
const descriptor = descriptors[key as any];
|
|
63
|
+
if (typeof key !== "string" || !("value" in descriptor) || !descriptor.enumerable) throw new TypeError("Invalid composite property");
|
|
64
|
+
if (array && (!/^(0|[1-9][0-9]*)$/.test(key) || Number(key) >= value.length)) throw new TypeError("Invalid composite index");
|
|
65
|
+
output[key] = copy(descriptor.value, depth + 1, budget);
|
|
66
|
+
}
|
|
67
|
+
return output;
|
|
68
|
+
}
|
|
69
|
+
function key32(key: unknown): asserts key is KeyObject {
|
|
70
|
+
if (!(key instanceof KeyObject) || key.type !== "secret" || key.symmetricKeySize !== 32) throw new TypeError("Expected host 256-bit secret key");
|
|
71
|
+
}
|
|
72
|
+
function signed(key: KeyObject, domain: string, text: string) {
|
|
73
|
+
return createHmac("sha256", key).update(`${domain}\0${text}`).digest("hex");
|
|
74
|
+
}
|
|
75
|
+
function authentic(key: KeyObject, domain: string, text: string, signature: unknown) {
|
|
76
|
+
return typeof signature === "string" && HASH.test(signature)
|
|
77
|
+
&& timingSafeEqual(Buffer.from(signed(key, domain, text), "hex"), Buffer.from(signature, "hex"));
|
|
78
|
+
}
|
|
79
|
+
function baseBinding(value: any, criterion?: any) {
|
|
80
|
+
exact(value, BASE_FIELDS, "composite binding");
|
|
81
|
+
for (const field of ["projectId", "sourceDigest", "materialSnapshotDigest", "suiteDigest", "configDigest", "armDigest",
|
|
82
|
+
"taskContractDigest", "criterionHash", "phaseHeadDigest", "producerManifestDigest"]) if (!HASH.test(value[field])) throw new TypeError(`Invalid ${field}`);
|
|
83
|
+
if (!REVISION.test(value.projectHead) || !ID.test(value.taskRunId) || !ID.test(value.sessionId)
|
|
84
|
+
|| !/^operator-request-v1:[a-f0-9]{64}$/.test(value.operatorRequestDigest)
|
|
85
|
+
|| !Number.isSafeInteger(value.criterionIndex) || value.criterionIndex < 0 || value.criterionIndex > 11
|
|
86
|
+
|| !FACT_ID.test(value.criterionId) || !Number.isSafeInteger(value.runtimeGeneration) || value.runtimeGeneration < 0
|
|
87
|
+
|| value.verifierDigest !== null && !HASH.test(value.verifierDigest)) throw new TypeError("Invalid composite binding identity");
|
|
88
|
+
if (criterion && (value.criterionIndex !== criterion.criterionIndex || value.criterionId !== criterion.criterionId
|
|
89
|
+
|| value.criterionHash !== criterion.criterionHash)) throw new TypeError("Composite criterion binding mismatch");
|
|
90
|
+
return Object.fromEntries(BASE_FIELDS.map(field => [field, value[field]]));
|
|
91
|
+
}
|
|
92
|
+
function responseBinding(value: any, nullable = false) {
|
|
93
|
+
exact(value, RESPONSE_FIELDS, "response binding");
|
|
94
|
+
const allNull = RESPONSE_FIELDS.every(field => value[field] === null);
|
|
95
|
+
if (nullable && allNull) return Object.fromEntries(RESPONSE_FIELDS.map(field => [field, null]));
|
|
96
|
+
if (!["assistant", "native-policy"].includes(value.origin) || !ID.test(value.entryId) || !HASH.test(value.digest)
|
|
97
|
+
|| !Number.isSafeInteger(value.byteLength) || value.byteLength < 0 || value.byteLength > 65_536
|
|
98
|
+
|| !ID.test(value.operationRef) || !ID.test(value.messageRequestId)) throw new TypeError("Invalid response identity");
|
|
99
|
+
return Object.fromEntries(RESPONSE_FIELDS.map(field => [field, value[field]]));
|
|
100
|
+
}
|
|
101
|
+
const nullResponse = () => Object.fromEntries(RESPONSE_FIELDS.map(field => [field, null]));
|
|
102
|
+
export function sealCompositePlanContext({ key, contractText, declarations, binding, maxAttempts = 2 }: any) {
|
|
103
|
+
key32(key);
|
|
104
|
+
const declarationCopy = copy(declarations), compiled = compileCompositeContract(contractText, declarationCopy);
|
|
105
|
+
if (compiled.criterionBinding !== "matched") throw new TypeError("Composite plan is not bound to the current criterion");
|
|
106
|
+
if (!Number.isSafeInteger(maxAttempts) || maxAttempts < 1 || maxAttempts > 8) throw new TypeError("Invalid composite attempt budget");
|
|
107
|
+
const context = { version: COMPOSITE_PLAN_CONTEXT_VERSION, contractText: JSON.stringify(compiled.contract), declarations: declarationCopy,
|
|
108
|
+
binding: baseBinding(copy(binding), compiled.contract), maxAttempts };
|
|
109
|
+
const contextText = JSON.stringify(context), contextDigest = sha(contextText);
|
|
110
|
+
return freeze({ version: COMPOSITE_PLAN_CONTEXT_VERSION, contextText, contextDigest,
|
|
111
|
+
signature: signed(key, COMPOSITE_PLAN_CONTEXT_VERSION, contextText) });
|
|
112
|
+
}
|
|
113
|
+
function openPlan(key: KeyObject, sealed: any) {
|
|
114
|
+
exact(sealed, ["version", "contextText", "contextDigest", "signature"], "sealed composite context");
|
|
115
|
+
if (sealed.version !== COMPOSITE_PLAN_CONTEXT_VERSION || typeof sealed.contextText !== "string"
|
|
116
|
+
|| sha(sealed.contextText) !== sealed.contextDigest || !authentic(key, COMPOSITE_PLAN_CONTEXT_VERSION, sealed.contextText, sealed.signature)) {
|
|
117
|
+
throw new Error("Composite plan context is unauthenticated");
|
|
118
|
+
}
|
|
119
|
+
const context = JSON.parse(sealed.contextText);
|
|
120
|
+
exact(context, ["version", "contractText", "declarations", "binding", "maxAttempts"], "composite plan context");
|
|
121
|
+
if (context.version !== COMPOSITE_PLAN_CONTEXT_VERSION) throw new Error("Composite plan context version changed");
|
|
122
|
+
const compiled = compileCompositeContract(context.contractText, context.declarations);
|
|
123
|
+
if (compiled.criterionBinding !== "matched") throw new Error("Composite plan criterion is not current");
|
|
124
|
+
context.binding = baseBinding(context.binding, compiled.contract);
|
|
125
|
+
if (!Number.isSafeInteger(context.maxAttempts) || context.maxAttempts < 1 || context.maxAttempts > 8) throw new Error("Invalid composite attempt budget");
|
|
126
|
+
return { context: freeze(context), compiled };
|
|
127
|
+
}
|
|
128
|
+
function privateFile(filePath: string, projectRoot: string) {
|
|
129
|
+
if (!path.isAbsolute(filePath) || path.resolve(filePath) !== filePath || !path.isAbsolute(projectRoot)) throw new TypeError("Canonical journal paths required");
|
|
130
|
+
const root = fs.realpathSync.native(projectRoot), directory = path.dirname(filePath);
|
|
131
|
+
if (fs.realpathSync.native(directory) !== directory) throw new Error("Composite journal directory is not canonical");
|
|
132
|
+
const relative = path.relative(root, filePath);
|
|
133
|
+
if (!relative || (!relative.startsWith(`..${path.sep}`) && relative !== ".." && !path.isAbsolute(relative))) throw new Error("Composite journal must be outside candidate project");
|
|
134
|
+
const parent = fs.lstatSync(directory);
|
|
135
|
+
if (!parent.isDirectory() || parent.uid !== process.getuid() || (parent.mode & 0o077) !== 0) throw new Error("Composite journal directory must be private");
|
|
136
|
+
return root;
|
|
137
|
+
}
|
|
138
|
+
export function openCompositeAssuranceJournal({ filePath, projectRoot, key, contextDigest, expectedHead }: any) {
|
|
139
|
+
key32(key); privateFile(filePath, projectRoot);
|
|
140
|
+
if (!HASH.test(contextDigest) || expectedHead !== undefined && !HASH.test(expectedHead)) throw new TypeError("Invalid journal identity");
|
|
141
|
+
const domain = `${JOURNAL_VERSION}\0${filePath}\0${contextDigest}`;
|
|
142
|
+
const envelope = (payload: any) => { const text = JSON.stringify(payload); return JSON.stringify({ payload, signature: signed(key, domain, text) }) + "\n"; };
|
|
143
|
+
const writeAll = (fd: number, text: string) => { const bytes = Buffer.from(text); for (let offset = 0; offset < bytes.length;) offset += fs.writeSync(fd, bytes, offset); fs.fsyncSync(fd); };
|
|
144
|
+
const anchor = openCompositeJournalAnchor({ anchorPath: `${filePath}.anchor.json`, filePath, contextDigest, key });
|
|
145
|
+
if (!fs.existsSync(filePath) && !anchor.exists()) {
|
|
146
|
+
const line = envelope({ version: JOURNAL_VERSION, sequence: 0, previous: null, contextDigest, nonce: randomUUID() });
|
|
147
|
+
anchor.prepare(null, JSON.parse(line).signature, Buffer.from(line));
|
|
148
|
+
}
|
|
149
|
+
const appendAnchored = (line: Buffer, create: boolean) => {
|
|
150
|
+
const flags = fs.constants.O_WRONLY | fs.constants.O_NOFOLLOW
|
|
151
|
+
| (create ? fs.constants.O_CREAT | fs.constants.O_EXCL : fs.constants.O_APPEND);
|
|
152
|
+
const fd = fs.openSync(filePath, flags, 0o600);
|
|
153
|
+
try { writeAll(fd, line.toString()); } finally { fs.closeSync(fd); }
|
|
154
|
+
if (create) { const directory = fs.openSync(path.dirname(filePath), fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
|
|
155
|
+
try { fs.fsyncSync(directory); } finally { fs.closeSync(directory); } }
|
|
156
|
+
};
|
|
157
|
+
if (fs.existsSync(filePath)) anchor.repairPreparedTail();
|
|
158
|
+
if (!fs.existsSync(filePath)) anchor.reconcile(false, null, appendAnchored);
|
|
159
|
+
const identity = fs.lstatSync(filePath);
|
|
160
|
+
if (!identity.isFile() || identity.nlink !== 1 || identity.uid !== process.getuid() || (identity.mode & 0o077) !== 0) throw new Error("Composite journal file is unsafe");
|
|
161
|
+
let closed = false, trustedHead: string;
|
|
162
|
+
function read() {
|
|
163
|
+
if (closed) throw new Error("Composite journal is closed");
|
|
164
|
+
const fd = fs.openSync(filePath, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
|
|
165
|
+
let source: string;
|
|
166
|
+
try {
|
|
167
|
+
const stat = fs.fstatSync(fd);
|
|
168
|
+
if (stat.dev !== identity.dev || stat.ino !== identity.ino || stat.size < 1 || stat.size > MAX_BYTES) throw new Error("Composite journal identity changed");
|
|
169
|
+
source = fs.readFileSync(fd, "utf8");
|
|
170
|
+
} finally { fs.closeSync(fd); }
|
|
171
|
+
if (!source.endsWith("\n")) throw new Error("Composite journal has an incomplete record");
|
|
172
|
+
const lines = source.trimEnd().split("\n"); if (lines.length > MAX_EVENTS + 1) throw new Error("Composite journal event bound exceeded");
|
|
173
|
+
let previous: string | null = null; const events: any[] = [];
|
|
174
|
+
for (const [index, line] of lines.entries()) {
|
|
175
|
+
const item = JSON.parse(line); exact(item, ["payload", "signature"], "journal envelope");
|
|
176
|
+
const text = JSON.stringify(item.payload);
|
|
177
|
+
if (!authentic(key, domain, text, item.signature)) throw new Error("Composite journal signature is invalid");
|
|
178
|
+
if (index === 0) {
|
|
179
|
+
exact(item.payload, ["version", "sequence", "previous", "contextDigest", "nonce"], "journal header");
|
|
180
|
+
if (item.payload.version !== JOURNAL_VERSION || item.payload.sequence !== 0 || item.payload.previous !== null
|
|
181
|
+
|| item.payload.contextDigest !== contextDigest || !ID.test(item.payload.nonce)) throw new Error("Composite journal header is invalid");
|
|
182
|
+
} else {
|
|
183
|
+
exact(item.payload, ["sequence", "previous", "kind", "data"], "journal event");
|
|
184
|
+
if (item.payload.sequence !== index || item.payload.previous !== previous || typeof item.payload.kind !== "string") throw new Error("Composite journal chain is invalid");
|
|
185
|
+
events.push(freeze(copy(item.payload)));
|
|
186
|
+
}
|
|
187
|
+
previous = item.signature;
|
|
188
|
+
}
|
|
189
|
+
return { events, head: previous as string };
|
|
190
|
+
}
|
|
191
|
+
const initial = read(); anchor.reconcile(true, initial.head, appendAnchored);
|
|
192
|
+
const anchored = read(); trustedHead = anchored.head;
|
|
193
|
+
if (expectedHead !== undefined && anchored.head !== expectedHead) { closed = true; throw new Error("Composite journal rollback or wrong head observed"); }
|
|
194
|
+
function snapshot() { const current = read(); if (current.head !== trustedHead) throw new Error("Composite journal changed outside this authority"); return current; }
|
|
195
|
+
function append(kind: string, data: any) {
|
|
196
|
+
if (!/^[a-z][a-z-]{0,63}$/.test(kind)) throw new TypeError("Invalid composite journal event kind");
|
|
197
|
+
const current = snapshot(), payload = { sequence: current.events.length + 1, previous: current.head, kind, data: copy(data) };
|
|
198
|
+
const line = envelope(payload);
|
|
199
|
+
if (fs.lstatSync(filePath).size + Buffer.byteLength(line) > MAX_BYTES) throw new Error("Composite journal byte bound exceeded");
|
|
200
|
+
const nextHead = JSON.parse(line).signature; anchor.prepare(current.head, nextHead, Buffer.from(line));
|
|
201
|
+
const fd = fs.openSync(filePath, fs.constants.O_WRONLY | fs.constants.O_APPEND | fs.constants.O_NOFOLLOW);
|
|
202
|
+
try { writeAll(fd, line); } finally { fs.closeSync(fd); }
|
|
203
|
+
anchor.commit(current.head, nextHead, Buffer.from(line)); trustedHead = nextHead;
|
|
204
|
+
return freeze({ event: payload, head: trustedHead });
|
|
205
|
+
}
|
|
206
|
+
return Object.freeze({ version: JOURNAL_VERSION, filePath, anchorPath: anchor.anchorPath, contextDigest,
|
|
207
|
+
events: () => snapshot().events, head: () => snapshot().head, append,
|
|
208
|
+
close() { closed = true; } });
|
|
209
|
+
}
|
|
210
|
+
function factRecord(value: any, spec: any, base: any, response: any, aggregateScopeId: string) {
|
|
211
|
+
exact(value, FACT_FIELDS, "composite fact record");
|
|
212
|
+
if (value.schemaVersion !== COMPOSITE_FACT_RECORD_VERSION || value.factId !== spec.id || value.factSpecDigest !== spec.specDigest
|
|
213
|
+
|| value.kind !== spec.kind || value.producerId !== spec.producerId || value.producerDigest !== spec.producerDigest
|
|
214
|
+
|| value.ruleDigest !== spec.ruleDigest || !["pass", "fail", "unknown", "error"].includes(value.status)
|
|
215
|
+
|| !HASH.test(value.observationDigest) || !Array.isArray(value.reasonCodes) || value.reasonCodes.length > 16
|
|
216
|
+
|| new Set(value.reasonCodes).size !== value.reasonCodes.length) throw new Error("Composite fact record identity is invalid");
|
|
217
|
+
const allowed = value.status === "unknown" ? UNKNOWN : value.status === "error" ? ERRORS : value.status === "fail" ? FAILURES : new Set();
|
|
218
|
+
if (value.reasonCodes.some((reason: unknown) => typeof reason !== "string" || !allowed.has(reason as string))
|
|
219
|
+
|| value.status === "fail" !== (typeof value.counterexampleRef === "string" && HASH.test(value.counterexampleRef))
|
|
220
|
+
|| value.status !== "fail" && value.counterexampleRef !== null) throw new Error("Composite fact disposition is invalid");
|
|
221
|
+
const bindingFields = [...BASE_FIELDS, "aggregateScopeId", "factId", "attemptId", "response"];
|
|
222
|
+
exact(value.binding, bindingFields, "composite fact binding");
|
|
223
|
+
const boundBase = Object.fromEntries(BASE_FIELDS.map(field => [field, value.binding[field]]));
|
|
224
|
+
if (JSON.stringify(baseBinding(boundBase)) !== JSON.stringify(base) || value.binding.aggregateScopeId !== aggregateScopeId
|
|
225
|
+
|| value.binding.factId !== spec.id || !ID.test(value.binding.attemptId)
|
|
226
|
+
|| JSON.stringify(responseBinding(value.binding.response, true)) !== JSON.stringify(response)) throw new Error("Composite fact binding is stale");
|
|
227
|
+
return freeze(copy(value));
|
|
228
|
+
}
|
|
229
|
+
function normalizedObservation(value: any, settlement: boolean, prepared: any) {
|
|
230
|
+
const fields = ["status", "observationDigest", "counterexampleRef", "reasonCodes", ...(settlement ? ["response"] : [])];
|
|
231
|
+
exact(value, fields, "fact observation");
|
|
232
|
+
if (settlement && JSON.stringify(responseBinding(value.response)) !== JSON.stringify(prepared.response)) throw new TypeError("Settlement response mismatch");
|
|
233
|
+
return copy(value);
|
|
234
|
+
}
|
|
235
|
+
export function compositePublicationIdempotencyKey(contextDigest: string, response: any) {
|
|
236
|
+
if (!HASH.test(contextDigest)) throw new TypeError("Invalid composite context digest");
|
|
237
|
+
return sha(JSON.stringify([COMPOSITE_ASSURANCE_VERSION, contextDigest, responseBinding(response)]));
|
|
238
|
+
}
|
|
239
|
+
export function openCompositeAssuranceRuntime({ key, sealedContext, journal, currentBinding }: any) {
|
|
240
|
+
key32(key);
|
|
241
|
+
if (!journal || journal.contextDigest !== sealedContext?.contextDigest || typeof currentBinding !== "function") throw new TypeError("Invalid composite runtime dependencies");
|
|
242
|
+
const { context, compiled } = openPlan(key, sealedContext), token = Symbol("composite-runtime"), facts = new Map(compiled.contract.facts.map((fact: any) => [fact.id, fact]));
|
|
243
|
+
const aggregateScopeId = sha(JSON.stringify([context.binding.taskRunId, context.binding.criterionId, context.binding.criterionHash, sealedContext.contextDigest]));
|
|
244
|
+
function current() {
|
|
245
|
+
const value = baseBinding(copy(currentBinding()), compiled.contract);
|
|
246
|
+
if (JSON.stringify(value) !== JSON.stringify(context.binding)) throw new Error("Current composite binding changed");
|
|
247
|
+
return value;
|
|
248
|
+
}
|
|
249
|
+
function load() {
|
|
250
|
+
const state: any = { attempts: new Map(), reservations: new Map(), records: new Map(), prepared: null, aggregate: null, completed: null, invalidated: null };
|
|
251
|
+
const events = journal.events();
|
|
252
|
+
for (const [index, event] of events.entries()) {
|
|
253
|
+
const data = event.data;
|
|
254
|
+
if (index === 0 && event.kind !== "context-opened") throw new Error("Composite context-opened event is missing");
|
|
255
|
+
if (event.kind === "context-opened") {
|
|
256
|
+
exact(data, ["contextDigest"], "context-opened event");
|
|
257
|
+
if (index !== 0 || data.contextDigest !== sealedContext.contextDigest) throw new Error("Composite context journal mismatch");
|
|
258
|
+
} else if (event.kind === "fact-reserved") {
|
|
259
|
+
exact(data, ["factId", "attemptId", "attempt"], "fact reservation event");
|
|
260
|
+
const spec: any = facts.get(data.factId), next = (state.attempts.get(data.factId) ?? 0) + 1;
|
|
261
|
+
if (!spec || state.reservations.has(data.factId) || data.attempt !== next || next > context.maxAttempts || !ID.test(data.attemptId)
|
|
262
|
+
|| state.prepared && spec.stage === "content" || !state.prepared && spec.stage === "settlement" || state.invalidated || state.completed) throw new Error("Invalid fact reservation transition");
|
|
263
|
+
state.attempts.set(data.factId, next); state.reservations.set(data.factId, data);
|
|
264
|
+
} else if (event.kind === "fact-settled") {
|
|
265
|
+
exact(data, ["factId", "attemptId", "record"], "fact settlement event");
|
|
266
|
+
const reservation = state.reservations.get(data.factId), spec: any = facts.get(data.factId);
|
|
267
|
+
if (!reservation || reservation.attemptId !== data.attemptId) throw new Error("Invalid fact settlement transition");
|
|
268
|
+
const response = spec.stage === "settlement" ? state.prepared?.response : nullResponse();
|
|
269
|
+
if (!response) throw new Error("Settlement preceded response preparation");
|
|
270
|
+
state.records.set(data.factId, factRecord(data.record, spec, context.binding, response, aggregateScopeId)); state.reservations.delete(data.factId);
|
|
271
|
+
} else if (event.kind === "fact-interrupted") {
|
|
272
|
+
exact(data, ["factId", "attemptId"], "fact interruption event");
|
|
273
|
+
const reservation = state.reservations.get(data.factId);
|
|
274
|
+
if (!reservation || reservation.attemptId !== data.attemptId) throw new Error("Invalid fact interruption transition");
|
|
275
|
+
state.reservations.delete(data.factId);
|
|
276
|
+
} else if (event.kind === "response-prepared") {
|
|
277
|
+
exact(data, ["idempotencyKey", "response", "contentDigest"], "response preparation event");
|
|
278
|
+
if (state.prepared || state.invalidated || !HASH.test(data.idempotencyKey) || !HASH.test(data.contentDigest)) throw new Error("Invalid response preparation transition");
|
|
279
|
+
const response = responseBinding(data.response), content = [...facts.values()].filter((fact: any) => fact.stage === "content");
|
|
280
|
+
const contentDigest = sha(JSON.stringify(content.map((fact: any) => sha(JSON.stringify(state.records.get(fact.id))))));
|
|
281
|
+
if (content.some((fact: any) => state.records.get(fact.id)?.status !== "pass") || data.contentDigest !== contentDigest
|
|
282
|
+
|| data.idempotencyKey !== compositePublicationIdempotencyKey(sealedContext.contextDigest, response)) throw new Error("Prepared response lacks current content facts");
|
|
283
|
+
state.prepared = freeze({ ...data, response });
|
|
284
|
+
} else if (event.kind === "aggregate-settled") {
|
|
285
|
+
exact(data, ["idempotencyKey", "assessmentDigest", "taskPublicationDigest"], "aggregate settlement event");
|
|
286
|
+
const assessmentDigest = sha(JSON.stringify([...facts.keys()].map(id => sha(JSON.stringify(state.records.get(id))))));
|
|
287
|
+
if (!state.prepared || state.aggregate || state.invalidated || data.idempotencyKey !== state.prepared.idempotencyKey
|
|
288
|
+
|| !HASH.test(data.assessmentDigest) || data.assessmentDigest !== assessmentDigest || !HASH.test(data.taskPublicationDigest)
|
|
289
|
+
|| [...facts.values()].some((fact: any) => state.records.get(fact.id)?.status !== "pass")) throw new Error("Invalid aggregate settlement transition");
|
|
290
|
+
state.aggregate = freeze(copy(data));
|
|
291
|
+
} else if (event.kind === "task-completed") {
|
|
292
|
+
exact(data, ["idempotencyKey", "assessmentDigest", "taskPublicationDigest"], "task completion event");
|
|
293
|
+
if (!state.aggregate || state.completed || state.invalidated || JSON.stringify(data) !== JSON.stringify(state.aggregate)) throw new Error("Invalid task completion transition");
|
|
294
|
+
state.completed = freeze(copy(data));
|
|
295
|
+
} else if (event.kind === "invalidated") {
|
|
296
|
+
exact(data, ["reason"], "invalidation event");
|
|
297
|
+
if (state.invalidated || state.completed || !["cancelled", "superseded"].includes(data.reason)) throw new Error("Invalid composite invalidation transition");
|
|
298
|
+
state.invalidated = data.reason;
|
|
299
|
+
} else throw new Error("Unknown composite journal event");
|
|
300
|
+
}
|
|
301
|
+
if (events.length === 0) journal.append("context-opened", { contextDigest: sealedContext.contextDigest });
|
|
302
|
+
return state;
|
|
303
|
+
}
|
|
304
|
+
load(); current();
|
|
305
|
+
const issueFact = (factId: string, record: any) => {
|
|
306
|
+
const receipt = freeze({ version: "composite-fact-capability-v1", factId, status: record.status, recordDigest: sha(JSON.stringify(record)) });
|
|
307
|
+
factCapabilities.set(receipt, { token, factId, recordDigest: receipt.recordDigest }); return receipt;
|
|
308
|
+
};
|
|
309
|
+
const issuePreparation = (prepared: any) => {
|
|
310
|
+
const receipt = freeze({ version: "composite-preparation-capability-v1", idempotencyKey: prepared.idempotencyKey, responseDigest: prepared.response.digest });
|
|
311
|
+
preparationCapabilities.set(receipt, { token, idempotencyKey: prepared.idempotencyKey }); return receipt;
|
|
312
|
+
};
|
|
313
|
+
const issueAggregate = (aggregate: any) => {
|
|
314
|
+
const receipt = freeze({ version: COMPOSITE_ASSURANCE_VERSION, verdict: "pass", completionAllowed: true, repairEligible: false,
|
|
315
|
+
sourceMutationAllowed: false, assurance: COMPOSITE_ASSURANCE, idempotencyKey: aggregate.idempotencyKey,
|
|
316
|
+
assessmentDigest: aggregate.assessmentDigest, taskPublicationDigest: aggregate.taskPublicationDigest });
|
|
317
|
+
aggregateCapabilities.set(receipt, { token, idempotencyKey: aggregate.idempotencyKey,
|
|
318
|
+
assessmentDigest: aggregate.assessmentDigest, taskPublicationDigest: aggregate.taskPublicationDigest }); return receipt;
|
|
319
|
+
};
|
|
320
|
+
function producerFor(factId: string) {
|
|
321
|
+
const spec: any = facts.get(factId); if (!spec) throw new TypeError("Unknown composite fact");
|
|
322
|
+
const capability = freeze({ version: "composite-producer-capability-v1", factId });
|
|
323
|
+
producerCapabilities.set(capability, { token, factId, producerId: spec.producerId, producerDigest: spec.producerDigest, ruleDigest: spec.ruleDigest });
|
|
324
|
+
return capability;
|
|
325
|
+
}
|
|
326
|
+
function reserveFact(factId: string, { retry = false } = {}) {
|
|
327
|
+
current(); const state = load(), spec: any = facts.get(factId);
|
|
328
|
+
if (!spec || state.invalidated || state.completed) throw new Error("Composite fact cannot be reserved");
|
|
329
|
+
if (state.reservations.has(factId)) return freeze({ status: "pending", attemptId: state.reservations.get(factId).attemptId });
|
|
330
|
+
if (state.records.has(factId) && !retry) return freeze({ status: "current", receipt: issueFact(factId, state.records.get(factId)) });
|
|
331
|
+
const attempt = (state.attempts.get(factId) ?? 0) + 1;
|
|
332
|
+
if (attempt > context.maxAttempts) return freeze({ status: "exhausted" });
|
|
333
|
+
const data = { factId, attemptId: randomUUID(), attempt }; journal.append("fact-reserved", data);
|
|
334
|
+
const reservation = freeze({ version: "composite-fact-reservation-v1", factId, attemptId: data.attemptId });
|
|
335
|
+
reservationCapabilities.set(reservation, { token, ...data });
|
|
336
|
+
return freeze({ status: "reserved", reservation });
|
|
337
|
+
}
|
|
338
|
+
async function settleFact({ reservation, producer, collect }: any) {
|
|
339
|
+
const owned: any = reservationCapabilities.get(reservation), producerOwned: any = producerCapabilities.get(producer);
|
|
340
|
+
if (!owned || owned.token !== token || !producerOwned || producerOwned.token !== token || producerOwned.factId !== owned.factId || typeof collect !== "function") {
|
|
341
|
+
throw new Error("Untrusted composite fact capability");
|
|
342
|
+
}
|
|
343
|
+
const before = current(), state = load(), pending = state.reservations.get(owned.factId), spec: any = facts.get(owned.factId);
|
|
344
|
+
if (!pending || pending.attemptId !== owned.attemptId) throw new Error("Composite fact reservation is not current");
|
|
345
|
+
let observed: any;
|
|
346
|
+
try { observed = await collect(); }
|
|
347
|
+
catch { observed = { status: "error", observationDigest: sha("producer-crash"), counterexampleRef: null, reasonCodes: ["producer-crash"],
|
|
348
|
+
...(spec.stage === "settlement" ? { response: state.prepared?.response } : {}) }; }
|
|
349
|
+
try { if (JSON.stringify(current()) !== JSON.stringify(before)) throw new Error("drift"); }
|
|
350
|
+
catch { observed = { status: "unknown", observationDigest: sha("stale-binding"), counterexampleRef: null, reasonCodes: ["stale-binding"],
|
|
351
|
+
...(spec.stage === "settlement" ? { response: state.prepared?.response } : {}) }; }
|
|
352
|
+
observed = normalizedObservation(observed, spec.stage === "settlement", state.prepared);
|
|
353
|
+
const response = spec.stage === "settlement" ? state.prepared.response : nullResponse();
|
|
354
|
+
const binding = { ...context.binding, aggregateScopeId, factId: spec.id, attemptId: owned.attemptId, response };
|
|
355
|
+
const record = factRecord({ schemaVersion: COMPOSITE_FACT_RECORD_VERSION, factId: spec.id, factSpecDigest: spec.specDigest,
|
|
356
|
+
kind: spec.kind, producerId: spec.producerId, producerDigest: spec.producerDigest, ruleDigest: spec.ruleDigest,
|
|
357
|
+
binding, status: observed.status, observationDigest: observed.observationDigest, counterexampleRef: observed.counterexampleRef,
|
|
358
|
+
reasonCodes: observed.reasonCodes }, spec, context.binding, response, aggregateScopeId);
|
|
359
|
+
journal.append("fact-settled", { factId: spec.id, attemptId: owned.attemptId, record }); reservationCapabilities.delete(reservation);
|
|
360
|
+
return issueFact(spec.id, record);
|
|
361
|
+
}
|
|
362
|
+
function reconcileInterrupted({ factId, attemptId, producerStopped = false }: any) {
|
|
363
|
+
current(); const state = load(), reservation = state.reservations.get(factId);
|
|
364
|
+
if (producerStopped !== true || !reservation || reservation.attemptId !== attemptId) throw new Error("Exact stopped producer reservation required");
|
|
365
|
+
journal.append("fact-interrupted", { factId, attemptId });
|
|
366
|
+
}
|
|
367
|
+
function assess(receipts: any[], contentOnly = false) {
|
|
368
|
+
try { current(); } catch { return decision("unknown", ["stale-binding"], [], [], false); }
|
|
369
|
+
const state = load(), required = [...facts.values()].filter((fact: any) => !contentOnly || fact.stage === "content");
|
|
370
|
+
if (!Array.isArray(receipts) || receipts.length > 16) return decision("unknown", ["untrusted-fact"], [], required.map((fact: any) => fact.id), false);
|
|
371
|
+
const supplied = new Map(); let untrusted = false;
|
|
372
|
+
for (const receipt of receipts) {
|
|
373
|
+
const owned: any = factCapabilities.get(receipt), record = owned?.token === token ? state.records.get(owned.factId) : undefined;
|
|
374
|
+
if (!owned || !record || owned.recordDigest !== sha(JSON.stringify(record)) || supplied.has(owned.factId)) untrusted = true;
|
|
375
|
+
else supplied.set(owned.factId, record);
|
|
376
|
+
}
|
|
377
|
+
if (state.invalidated) return decision("unknown", [state.invalidated], [], required.map((fact: any) => fact.id), false);
|
|
378
|
+
const missing = required.filter((fact: any) => !supplied.has(fact.id)).map((fact: any) => fact.id);
|
|
379
|
+
if (untrusted) return decision("unknown", ["untrusted-fact", ...(missing.length ? ["missing-fact"] : [])], [], missing, false);
|
|
380
|
+
const records = required.map((fact: any) => supplied.get(fact.id)).filter(Boolean);
|
|
381
|
+
const by = (status: string) => records.filter((record: any) => record.status === status);
|
|
382
|
+
const authorityUnknown = by("unknown").filter((record: any) => record.reasonCodes.some((reason: string) => ["invalid-approval", "stale-binding"].includes(reason)));
|
|
383
|
+
if (authorityUnknown.length) return decision("unknown", authorityUnknown.flatMap((record: any) => record.reasonCodes), [], missing, false);
|
|
384
|
+
if (by("error").length) return decision("error", by("error").flatMap((record: any) => record.reasonCodes), by("error").map((record: any) => record.factId), missing, false);
|
|
385
|
+
if (by("fail").length) {
|
|
386
|
+
const failed = by("fail"), safety = failed.some((record: any) => ["tool-policy-complete", "workspace-scope", "policy-refusal-output"].includes(record.kind));
|
|
387
|
+
return decision("fail", failed.flatMap((record: any) => record.reasonCodes), failed.map((record: any) => record.factId), missing, !safety);
|
|
388
|
+
}
|
|
389
|
+
if (by("unknown").length || missing.length) return decision("unknown", [...by("unknown").flatMap((record: any) => record.reasonCodes), ...(missing.length ? ["missing-fact"] : [])], [], missing, false);
|
|
390
|
+
return decision("pass", [contentOnly ? "all-content-facts-observed" : "all-required-facts-observed"], [], [], false);
|
|
391
|
+
}
|
|
392
|
+
function decision(verdict: string, reasons: string[], failedFactIds: string[], missingFactIds: string[], repairEligible: boolean) {
|
|
393
|
+
return freeze({ version: COMPOSITE_ASSURANCE_VERSION, verdict, action: verdict === "error" ? "diagnose-producer" : verdict === "fail"
|
|
394
|
+
? repairEligible ? "repair-counterexample" : "safety-stop" : verdict === "unknown" ? "complete-facts" : "settle-aggregate",
|
|
395
|
+
completionAllowed: false, repairEligible, sourceMutationAllowed: false, assurance: "none", reasons: [...new Set(reasons)], failedFactIds, missingFactIds });
|
|
396
|
+
}
|
|
397
|
+
function prepareResponse({ facts: receipts, bytes, origin, entryId, operationRef, messageRequestId, idempotencyKey }: any) {
|
|
398
|
+
current(); const state = load();
|
|
399
|
+
if (state.invalidated || state.completed) throw new Error("Composite publication is terminal");
|
|
400
|
+
const buffer = Buffer.isBuffer(bytes) ? Buffer.from(bytes) : typeof bytes === "string" ? Buffer.from(bytes) : null;
|
|
401
|
+
if (!buffer || buffer.length > 65_536) throw new TypeError("Invalid actual response bytes");
|
|
402
|
+
const response = responseBinding({ origin, entryId, digest: sha(buffer), byteLength: buffer.length, operationRef, messageRequestId });
|
|
403
|
+
const expectedKey = compositePublicationIdempotencyKey(sealedContext.contextDigest, response);
|
|
404
|
+
if (idempotencyKey !== undefined && idempotencyKey !== expectedKey) throw new Error("Composite publication idempotency mismatch");
|
|
405
|
+
if (state.prepared) {
|
|
406
|
+
if (state.prepared.idempotencyKey !== expectedKey || JSON.stringify(state.prepared.response) !== JSON.stringify(response)) throw new Error("Different response already prepared");
|
|
407
|
+
return issuePreparation(state.prepared);
|
|
408
|
+
}
|
|
409
|
+
const assessment = assess(receipts, true); if (assessment.verdict !== "pass") throw new Error("Composite content facts are not prepared");
|
|
410
|
+
for (const fact of facts.values() as any) if (fact.kind === "response-persisted" && !fact.parameters.expectedOrigins.includes(origin)) throw new Error("Response origin is not approved");
|
|
411
|
+
if (origin === "native-policy" && [...facts.values()].some((fact: any) => fact.kind === "policy-refusal-output" && !HASH.test(fact.parameters.nativeTemplateSetDigest))) {
|
|
412
|
+
throw new Error("Native response template is not pinned");
|
|
413
|
+
}
|
|
414
|
+
const contentDigest = sha(JSON.stringify([...facts.values()].filter((fact: any) => fact.stage === "content")
|
|
415
|
+
.map((fact: any) => sha(JSON.stringify(state.records.get(fact.id))))));
|
|
416
|
+
journal.append("response-prepared", { idempotencyKey: expectedKey, response, contentDigest });
|
|
417
|
+
return issuePreparation(load().prepared);
|
|
418
|
+
}
|
|
419
|
+
function prepareAggregatePublication({ preparation, facts: receipts, taskPublicationDigest }: any) {
|
|
420
|
+
current(); const prepared: any = preparationCapabilities.get(preparation), state = load();
|
|
421
|
+
if (state.invalidated) throw new Error("Composite publication was invalidated");
|
|
422
|
+
if (!prepared || prepared.token !== token || !state.prepared || prepared.idempotencyKey !== state.prepared.idempotencyKey
|
|
423
|
+
|| !HASH.test(taskPublicationDigest)) throw new Error("Untrusted aggregate publication preparation");
|
|
424
|
+
const assessment = assess(receipts, false); if (assessment.verdict !== "pass") throw new Error("Composite aggregate is not satisfied");
|
|
425
|
+
const aggregate = { idempotencyKey: state.prepared.idempotencyKey,
|
|
426
|
+
assessmentDigest: sha(JSON.stringify([...facts.keys()].map(id => sha(JSON.stringify(state.records.get(id)))))),
|
|
427
|
+
taskPublicationDigest };
|
|
428
|
+
let preAggregateHead = journal.head();
|
|
429
|
+
if (state.aggregate) {
|
|
430
|
+
if (JSON.stringify(state.aggregate) !== JSON.stringify(aggregate)) throw new Error("Different aggregate publication already settled");
|
|
431
|
+
const event = journal.events().find((item: any) => item.kind === "aggregate-settled");
|
|
432
|
+
if (!event || !HASH.test(event.previous)) throw new Error("Aggregate publication head is unavailable");
|
|
433
|
+
preAggregateHead = event.previous;
|
|
434
|
+
}
|
|
435
|
+
const publication = freeze({ version: "composite-aggregate-publication-capability-v1",
|
|
436
|
+
taskPublicationDigest, assessmentDigest: aggregate.assessmentDigest });
|
|
437
|
+
aggregatePreparationCapabilities.set(publication, { token, aggregate, preAggregateHead });
|
|
438
|
+
const entry = freeze({ version: "composite-journal-publication-v1", criterionId: compiled.contract.criterionId,
|
|
439
|
+
criterionHash: compiled.contract.criterionHash, contextDigest: sealedContext.contextDigest,
|
|
440
|
+
journalFile: path.basename(journal.filePath), preAggregateHead, aggregate });
|
|
441
|
+
return freeze({ publication, entry });
|
|
442
|
+
}
|
|
443
|
+
function settleAggregate({ publication }: any) {
|
|
444
|
+
current(); const prepared: any = aggregatePreparationCapabilities.get(publication), state = load();
|
|
445
|
+
if (!prepared || prepared.token !== token || state.invalidated) throw new Error("Untrusted aggregate publication");
|
|
446
|
+
if (state.aggregate) {
|
|
447
|
+
if (JSON.stringify(state.aggregate) !== JSON.stringify(prepared.aggregate)) throw new Error("Different aggregate already settled");
|
|
448
|
+
return issueAggregate(state.aggregate);
|
|
449
|
+
}
|
|
450
|
+
if (journal.head() !== prepared.preAggregateHead) throw new Error("Aggregate publication head changed");
|
|
451
|
+
current(); journal.append("aggregate-settled", prepared.aggregate);
|
|
452
|
+
return issueAggregate(load().aggregate);
|
|
453
|
+
}
|
|
454
|
+
function prepareTaskCompletion({ aggregate }: any) {
|
|
455
|
+
current(); const owned: any = aggregateCapabilities.get(aggregate), state = load();
|
|
456
|
+
if (!owned || owned.token !== token || !state.aggregate || owned.idempotencyKey !== state.aggregate.idempotencyKey
|
|
457
|
+
|| owned.assessmentDigest !== state.aggregate.assessmentDigest
|
|
458
|
+
|| owned.taskPublicationDigest !== state.aggregate.taskPublicationDigest || state.invalidated) throw new Error("Untrusted composite aggregate");
|
|
459
|
+
const publication = freeze({ version: "composite-task-publication-capability-v1",
|
|
460
|
+
idempotencyKey: state.aggregate.idempotencyKey, assessmentDigest: state.aggregate.assessmentDigest,
|
|
461
|
+
taskPublicationDigest: state.aggregate.taskPublicationDigest });
|
|
462
|
+
publicationCapabilities.set(publication, { token, ...state.aggregate }); return publication;
|
|
463
|
+
}
|
|
464
|
+
function completeTask({ aggregate, publication }: any) {
|
|
465
|
+
const owned: any = aggregateCapabilities.get(aggregate), published: any = publicationCapabilities.get(publication);
|
|
466
|
+
if (!published) current();
|
|
467
|
+
const state = load();
|
|
468
|
+
if (!owned || owned.token !== token || !state.aggregate || owned.idempotencyKey !== state.aggregate.idempotencyKey
|
|
469
|
+
|| owned.assessmentDigest !== state.aggregate.assessmentDigest
|
|
470
|
+
|| owned.taskPublicationDigest !== state.aggregate.taskPublicationDigest || state.invalidated
|
|
471
|
+
|| published && (published.token !== token || published.idempotencyKey !== state.aggregate.idempotencyKey
|
|
472
|
+
|| published.assessmentDigest !== state.aggregate.assessmentDigest
|
|
473
|
+
|| published.taskPublicationDigest !== state.aggregate.taskPublicationDigest)) throw new Error("Untrusted composite aggregate");
|
|
474
|
+
if (!state.completed) journal.append("task-completed", state.aggregate);
|
|
475
|
+
if (publication) publicationCapabilities.delete(publication);
|
|
476
|
+
return freeze({ completed: true, idempotencyKey: state.aggregate.idempotencyKey, assessmentDigest: state.aggregate.assessmentDigest,
|
|
477
|
+
taskPublicationDigest: state.aggregate.taskPublicationDigest, responseDigest: state.prepared.response.digest,
|
|
478
|
+
outputCreated: false, modelTurnRequested: false });
|
|
479
|
+
}
|
|
480
|
+
function invalidate(reason: string) {
|
|
481
|
+
if (!["cancelled", "superseded"].includes(reason)) throw new TypeError("Invalid composite invalidation");
|
|
482
|
+
const state = load(); if (state.completed) throw new Error("Completed composite cannot be invalidated");
|
|
483
|
+
if (!state.invalidated) journal.append("invalidated", { reason });
|
|
484
|
+
}
|
|
485
|
+
function reissue() {
|
|
486
|
+
current(); const state = load(), receipts = [...state.records].map(([id, record]) => issueFact(id, record));
|
|
487
|
+
return freeze({ facts: receipts, preparation: state.prepared && !state.invalidated ? issuePreparation(state.prepared) : null,
|
|
488
|
+
aggregate: state.aggregate && !state.invalidated ? issueAggregate(state.aggregate) : null,
|
|
489
|
+
completed: Boolean(state.completed), invalidated: state.invalidated });
|
|
490
|
+
}
|
|
491
|
+
function state() {
|
|
492
|
+
const value = load(); return freeze({ requiredFactIds: [...facts.keys()], factStatuses: Object.fromEntries([...value.records].map(([id, record]: any) => [id, record.status])),
|
|
493
|
+
pendingFactIds: [...value.reservations.keys()], phase: value.completed ? "completed" : value.invalidated ?? (value.aggregate ? "settled" : value.prepared ? "prepared" : "collecting"),
|
|
494
|
+
idempotencyKey: value.prepared?.idempotencyKey ?? null, journalHead: journal.head(), eventCount: journal.events().length });
|
|
495
|
+
}
|
|
496
|
+
return Object.freeze({ version: COMPOSITE_ASSURANCE_VERSION, compiled, producerFor, reserveFact, settleFact, reconcileInterrupted,
|
|
497
|
+
assess: (receipts: any[]) => assess(receipts, false), assessContent: (receipts: any[]) => assess(receipts, true),
|
|
498
|
+
prepareResponse, prepareAggregatePublication, settleAggregate, prepareTaskCompletion,
|
|
499
|
+
completeTask, invalidate, reissue, state });
|
|
500
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { execFileSync } from "node:child_process";
|
|
3
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
import type { TaskContract } from "../../extensions/guard-types.ts";
|
|
5
|
+
import { createDurableContractRunner } from "../../extensions/acceptance-durable-execution.js";
|
|
6
|
+
import { FACT_EVIDENCE_SCOPE_VERSION } from "../../extensions/acceptance-evidence-store.js";
|
|
7
|
+
import { captureExecutionSnapshot } from "../../extensions/acceptance-execution-snapshot.js";
|
|
8
|
+
import { captureWorkspaceVerificationSnapshot } from "../../extensions/workspace-verification-snapshot.js";
|
|
9
|
+
import { validateModulePaths } from "../../extensions/acceptance-executor/module-graph.mjs";
|
|
10
|
+
import type { RuntimeSessionState } from "../session/runtime-state.ts";
|
|
11
|
+
|
|
12
|
+
type ApprovedConfiguration = Omit<Parameters<typeof createDurableContractRunner>[0], "projectRoot" | "getProjectVerificationDigest">;
|
|
13
|
+
type CompositeCodePlan = Readonly<{ digest: string; backendProfileDigest: string; caseCount: number;
|
|
14
|
+
sourcePath: string; modulePaths: readonly string[]; exportName: string; checks: readonly unknown[]; profile: object }>;
|
|
15
|
+
type CompositeCodeFact = Readonly<{ id: string; parameters: Readonly<{ codePlanDigest: string; backendProfileDigest: string }> }>;
|
|
16
|
+
const sha = (value: string) => createHash("sha256").update(value).digest("hex");
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Host-only bridge from actual tool hooks to the durable independent runner.
|
|
20
|
+
* Configuration, contract checks, store, and source authorization must already
|
|
21
|
+
* be approved by the host; none are taken from a model tool or candidate file.
|
|
22
|
+
* This does not enable production contract selection or completion admission.
|
|
23
|
+
*/
|
|
24
|
+
export function createRuntimeContractRunner({ state, context, getTask, approved }: {
|
|
25
|
+
state: RuntimeSessionState; context: ExtensionContext; getTask: () => TaskContract | undefined; approved: ApprovedConfiguration;
|
|
26
|
+
}) {
|
|
27
|
+
if (!state?.projectVerification || typeof getTask !== "function") throw new TypeError("Actual runtime verification state is required");
|
|
28
|
+
const cwd = context.cwd, sessionId = context.sessionManager.getSessionId();
|
|
29
|
+
const sourcePath = approved.sourcePath, authorizeSourceRead = approved.authorizeSourceRead;
|
|
30
|
+
const modulePaths = approved.modulePaths === undefined ? undefined : Object.freeze(validateModulePaths(sourcePath, approved.modulePaths));
|
|
31
|
+
const sourcePaths = [sourcePath, ...(modulePaths ?? [])];
|
|
32
|
+
return createDurableContractRunner({ ...approved, projectRoot: cwd,
|
|
33
|
+
getProjectVerificationDigest(request) {
|
|
34
|
+
try {
|
|
35
|
+
if (context.cwd !== cwd || context.sessionManager.getSessionId() !== sessionId) return null;
|
|
36
|
+
const task = getTask();
|
|
37
|
+
if (!task || task.taskRunId !== request.taskRunId || task.sessionId !== sessionId || task.trace.outcome !== "pending"
|
|
38
|
+
|| !task.acceptanceReceipt?.criteria.some((criterion) => criterion.id === request.criterionId && criterion.hash === request.criterionHash)) return null;
|
|
39
|
+
// Ignored source bytes are absent from ordinary project-verifier dirt.
|
|
40
|
+
// Until approved targets are captured at tool start, do not claim they
|
|
41
|
+
// were tested just because an independent execution captured them now.
|
|
42
|
+
const covered = execFileSync("git", ["-c", "core.fsmonitor=false", "--no-optional-locks", "-C", cwd,
|
|
43
|
+
"ls-files", "-z", "--cached", "--others", "--exclude-standard", "--", ...sourcePaths],
|
|
44
|
+
{ encoding: "utf8", timeout: 3000, maxBuffer: 65536, stdio: ["ignore", "pipe", "pipe"] });
|
|
45
|
+
const coveredPaths = new Set(covered.split("\0"));
|
|
46
|
+
if (!sourcePaths.every((candidate) => coveredPaths.has(candidate))) return null;
|
|
47
|
+
const before = captureWorkspaceVerificationSnapshot(cwd);
|
|
48
|
+
const source = captureExecutionSnapshot({ projectRoot: cwd, sourcePath, modulePaths, authorizeSourceRead });
|
|
49
|
+
const current = captureWorkspaceVerificationSnapshot(cwd);
|
|
50
|
+
if (!before.proofCapable || !current.proofCapable || before.digest !== current.digest
|
|
51
|
+
|| before.workspaceRevisionDigest !== current.workspaceRevisionDigest
|
|
52
|
+
|| source.snapshotDigest !== request.snapshotDigest || source.binding.workingTreeDigest !== current.digest) return null;
|
|
53
|
+
return state.projectVerification.currentDigest(context, task, current);
|
|
54
|
+
} catch { return null; }
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** A code receipt is consumed here as one fact observation, never registered as root criterion authority. */
|
|
60
|
+
export function createCompositeCodeChildCollector({ state, context, getTask, approved, plan, fact,
|
|
61
|
+
criterionId, criterionHash, maxAttempts }: {
|
|
62
|
+
state: RuntimeSessionState; context: ExtensionContext; getTask: () => TaskContract | undefined;
|
|
63
|
+
approved: Omit<ApprovedConfiguration, "sourcePath" | "modulePaths" | "exportName" | "checks" | "profile">;
|
|
64
|
+
plan: CompositeCodePlan; fact: CompositeCodeFact; criterionId: string; criterionHash: string; maxAttempts: number;
|
|
65
|
+
}) {
|
|
66
|
+
if (fact.parameters.codePlanDigest !== plan.digest || fact.parameters.backendProfileDigest !== plan.backendProfileDigest) {
|
|
67
|
+
throw new TypeError("Composite code fact does not match its approved child plan");
|
|
68
|
+
}
|
|
69
|
+
const expectedPaths = [plan.sourcePath, ...plan.modulePaths];
|
|
70
|
+
const runner = createRuntimeContractRunner({ state, context, getTask, approved: { ...approved,
|
|
71
|
+
sourcePath: plan.sourcePath, modulePaths: plan.modulePaths, exportName: plan.exportName,
|
|
72
|
+
checks: plan.checks, profile: plan.profile } as ApprovedConfiguration });
|
|
73
|
+
const unavailable = (reason: string) => ({ status: "unknown", observationDigest: sha(JSON.stringify({
|
|
74
|
+
version: "composite-code-child-observation-v1", factId: fact.id, codePlanDigest: plan.digest, reason })),
|
|
75
|
+
counterexampleRef: null, reasonCodes: [reason] });
|
|
76
|
+
return Object.freeze({ factId: fact.id, async collect(task: TaskContract, signal: AbortSignal) {
|
|
77
|
+
const result = await runner.run({ scope: { version: FACT_EVIDENCE_SCOPE_VERSION, taskRunId: task.taskRunId,
|
|
78
|
+
criterionId, factId: fact.id }, criterionHash, maxAttempts, signal });
|
|
79
|
+
if (result.reason) return unavailable(result.reason.includes("binding") ? "stale-binding" : "unavailable-native-boundary");
|
|
80
|
+
const assessment = await runner.assess(result, { policy: "allow" });
|
|
81
|
+
const checks = assessment.checks ?? [], observedPaths = assessment.sourcePaths ?? (assessment.sourcePath ? [assessment.sourcePath] : []);
|
|
82
|
+
const current = assessment.taskRunId === task.taskRunId && assessment.criterionId === criterionId
|
|
83
|
+
&& assessment.criterionHash === criterionHash && assessment.factId === fact.id
|
|
84
|
+
&& assessment.profileDigest === plan.backendProfileDigest && assessment.planDigest === result.evidence?.planDigest
|
|
85
|
+
&& checks.length === plan.checks.length && checks.reduce((total, check) => total + check.caseCount, 0) === plan.caseCount
|
|
86
|
+
&& expectedPaths.length === observedPaths.length && expectedPaths.every((sourcePath, index) => sourcePath === observedPaths[index]);
|
|
87
|
+
if (!current) return unavailable(assessment.reasons.some(reason => reason.includes("binding") || reason.includes("current"))
|
|
88
|
+
? "stale-binding" : "invalid-approval");
|
|
89
|
+
const detail = { version: "composite-code-child-observation-v1", factId: fact.id,
|
|
90
|
+
codePlanDigest: plan.digest, backendProfileDigest: plan.backendProfileDigest, approvedCaseCount: plan.caseCount,
|
|
91
|
+
attemptId: assessment.attemptId, snapshotDigest: assessment.snapshotDigest, workingTreeDigest: assessment.workingTreeDigest,
|
|
92
|
+
projectVerificationDigest: assessment.projectVerificationDigest, executionPlanDigest: assessment.planDigest,
|
|
93
|
+
backendDigest: assessment.backendDigest, sourcePaths: observedPaths, checks, counterexamples: assessment.counterexamples ?? [] };
|
|
94
|
+
if (assessment.verdict === "pass" && assessment.completionAllowed && assessment.assurance === "bounded-contract-tested"
|
|
95
|
+
&& checks.every(check => check.status === "pass")) {
|
|
96
|
+
return { status: "pass", observationDigest: sha(JSON.stringify(detail)), counterexampleRef: null, reasonCodes: [] };
|
|
97
|
+
}
|
|
98
|
+
if (assessment.verdict === "fail" && (assessment.counterexamples?.length ?? 0) > 0) {
|
|
99
|
+
return { status: "fail", observationDigest: sha(JSON.stringify(detail)),
|
|
100
|
+
counterexampleRef: sha(JSON.stringify(assessment.counterexamples)), reasonCodes: ["observed-counterexample"] };
|
|
101
|
+
}
|
|
102
|
+
if (assessment.verdict === "error") return { status: "error", observationDigest: sha(JSON.stringify(detail)),
|
|
103
|
+
counterexampleRef: null, reasonCodes: [assessment.executionDiagnostics?.cleanupConfirmed === false ? "cleanup-ambiguous" : "producer-crash"] };
|
|
104
|
+
return unavailable(assessment.reasons.some(reason => reason.includes("binding") || reason.includes("current"))
|
|
105
|
+
? "stale-binding" : assessment.executionDiagnostics?.unsupportedCaseCount ? "unsupported-input" : "unavailable-native-boundary");
|
|
106
|
+
} });
|
|
107
|
+
}
|