@piagent/platform 1.5.5 → 1.6.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 +166 -0
- package/README.md +25 -21
- package/README.vi.md +34 -5
- package/adapters/web-frontend/profile.json +2 -4
- package/architecture/layers.json +18 -3
- package/benchmarks/deep-logic-v1/SPECIALIST_PROTOCOL.vi.md +122 -0
- package/benchmarks/deep-logic-v1/grade.mjs +272 -10
- package/benchmarks/deep-logic-v1/project/apps/admin/src/billing-summary.js +3 -0
- package/benchmarks/deep-logic-v1/project/benchmark-contract/public-smoke.test.js +166 -0
- package/benchmarks/deep-logic-v1/project/package.json +1 -1
- package/benchmarks/deep-logic-v1/project/packages/billing/src/close-period.js +3 -0
- package/benchmarks/deep-logic-v1/project/packages/billing/src/plan-timeline.js +3 -0
- package/benchmarks/deep-logic-v1/project/test/smoke.test.js +7 -1
- package/benchmarks/deep-logic-v1/prompts/budgeted-context-graph.md +2 -0
- package/benchmarks/deep-logic-v1/prompts/fair-dependency-scheduler.md +2 -0
- package/benchmarks/deep-logic-v1/prompts/layered-policy-resolution.md +2 -0
- package/benchmarks/deep-logic-v1/prompts/resumable-stream-assembly.md +3 -1
- package/benchmarks/deep-logic-v1/prompts/revision-event-reconciliation.md +2 -0
- package/benchmarks/deep-logic-v1/prompts/temporal-usage-billing-close.md +123 -0
- package/benchmarks/deep-logic-v1/prompts/transactional-config-merge.md +2 -0
- package/benchmarks/deep-logic-v1/suite.json +51 -3
- package/benchmarks/deep-logic-v1/variant.mjs +22 -1
- package/benchmarks/production-v1/spend-control.v1.json +92 -0
- package/benchmarks/production-v1/suite.json +46 -3
- package/catalog/capabilities.json +18 -18
- package/docs/authority-manifest.md +1 -1
- package/docs/auto-delegation-policy.md +38 -73
- package/docs/command-reference-vietnamese.md +27 -35
- package/docs/context-engine.md +12 -4
- package/docs/context-window-policy.md +37 -1
- package/docs/distribution-standard.md +7 -7
- package/docs/en/architecture.md +27 -13
- package/docs/languages.json +4 -4
- package/docs/model-options.md +8 -5
- package/docs/operator-manual-vietnamese.md +27 -45
- package/docs/project-adapters.md +2 -2
- package/docs/publishing-for-teams.md +10 -0
- package/docs/quality-benchmark.md +427 -62
- package/docs/release-install-policy.md +10 -9
- package/docs/runtime-harness-standard.md +3 -3
- package/docs/runtime-policy-design.md +4 -4
- package/docs/runtime-quality-baseline.md +13 -6
- package/docs/security-threat-model.md +2 -2
- package/docs/subagent-orchestration-capabilities.md +19 -51
- package/docs/subagents-and-multiagent.md +21 -47
- package/docs/task-implementation-contract.md +3 -3
- package/docs/task-lifecycle-vietnamese.md +3 -3
- package/docs/team-onboarding.md +2 -2
- package/docs/usage-observability.md +7 -2
- package/docs/vi/architecture.md +27 -13
- package/docs-site/architecture.html +1 -1
- package/docs-site/benchmark.html +89 -95
- package/docs-site/commands.html +1 -1
- package/docs-site/content/benchmark.html +88 -94
- package/docs-site/content/en/benchmark.html +134 -90
- package/docs-site/content/en/index.html +19 -19
- package/docs-site/content/en/runtime.html +4 -4
- package/docs-site/content/en/whats-new.html +12 -1
- package/docs-site/content/en/workflows.html +2 -2
- package/docs-site/content/index.html +19 -19
- package/docs-site/content/runtime.html +4 -4
- package/docs-site/content/whats-new.html +12 -1
- package/docs-site/content/workflows.html +2 -2
- package/docs-site/context-engine.html +1 -1
- package/docs-site/en/architecture.html +1 -1
- package/docs-site/en/benchmark.html +135 -91
- package/docs-site/en/commands.html +1 -1
- package/docs-site/en/context-engine.html +1 -1
- package/docs-site/en/index.html +20 -20
- 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 +5 -5
- 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 +13 -2
- package/docs-site/en/workflows.html +3 -3
- package/docs-site/index.html +20 -20
- 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 +5 -5
- 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 +13 -2
- package/docs-site/workflows.html +3 -3
- package/evals/fixtures/benchmark-assurance-evidence.invalid.json +1 -1
- package/evals/fixtures/benchmark-assurance-evidence.valid.json +1 -1
- package/evals/fixtures/codex-relative-efficiency-v1.invalid.json +4 -0
- package/evals/fixtures/codex-relative-efficiency-v1.valid.json +94 -0
- package/evals/fixtures/helper-request.invalid.json +1 -1
- package/evals/fixtures/helper-request.valid.json +1 -1
- package/evals/fixtures/piagent-webui/session-live-state-v1.invalid.json +18 -0
- package/evals/fixtures/piagent-webui/session-live-state-v1.valid.json +27 -0
- package/evals/fixtures/piagent-webui/subagent-tree-v1.valid.json +7 -0
- package/evals/fixtures/task-authority-snapshot.valid.json +4 -4
- package/evals/fs4-readiness-matrix.v1.json +2 -2
- package/evals/fs5-causal-arm.v1.json +3 -3
- package/evals/fs5-pilot-protocol.v1.json +3 -3
- package/evals/fs5-pilot-protocol.v2.json +8 -8
- package/evals/fs5-pilot-protocol.v3.json +8 -8
- package/evals/fs5-pilot-protocol.v4.json +7 -6
- package/evals/fs5-pilot-protocol.v5.json +9 -7
- package/evals/private-holdout-v1/public-exposure.v1.json +1 -1
- package/evals/runtime-conformance-v1/README.md +43 -0
- package/evals/runtime-conformance-v1/evaluate-paid-canary.mjs +198 -0
- package/evals/runtime-conformance-v1/lane.json +57 -0
- package/evals/runtime-conformance-v1/paid-canary.json +31 -0
- package/evals/runtime-conformance-v1/runner.mjs +456 -0
- package/package.json +3 -1
- package/packages/piagent-core/README.md +13 -4
- package/packages/piagent-core/benchmark/benchmark-assurance.js +4 -1
- package/packages/piagent-core/benchmark/benchmark-bootstrap.js +91 -9
- package/packages/piagent-core/benchmark/benchmark-claim-restrictions.js +38 -7
- package/packages/piagent-core/benchmark/benchmark-cli.js +7 -7
- package/packages/piagent-core/benchmark/benchmark-codex-relative-efficiency.js +698 -0
- package/packages/piagent-core/benchmark/benchmark-codex.js +3 -1
- package/packages/piagent-core/benchmark/benchmark-comparison.js +271 -2
- package/packages/piagent-core/benchmark/benchmark-core.js +736 -664
- package/packages/piagent-core/benchmark/benchmark-execution-guard.js +7 -5
- package/packages/piagent-core/benchmark/benchmark-forensics.js +441 -4
- package/packages/piagent-core/benchmark/benchmark-host-readiness.js +639 -0
- package/packages/piagent-core/benchmark/benchmark-matrix.js +22 -4
- package/packages/piagent-core/benchmark/benchmark-normalized-cost.js +317 -0
- package/packages/piagent-core/benchmark/benchmark-preflight.js +5 -1
- package/packages/piagent-core/benchmark/benchmark-process.js +6 -5
- package/packages/piagent-core/benchmark/benchmark-provider-free-evidence.js +345 -0
- package/packages/piagent-core/benchmark/benchmark-provider-wire.js +126 -0
- package/packages/piagent-core/benchmark/benchmark-record-validation.js +252 -0
- package/packages/piagent-core/benchmark/benchmark-report.js +130 -12
- package/packages/piagent-core/benchmark/benchmark-resume-recovery.js +14 -0
- package/packages/piagent-core/benchmark/benchmark-runner-policy.js +26 -0
- package/packages/piagent-core/benchmark/benchmark-runtime-causal.js +244 -0
- package/packages/piagent-core/benchmark/benchmark-stage-diagnostic.js +979 -0
- package/packages/piagent-core/benchmark/benchmark-statistics.js +64 -0
- package/packages/piagent-core/benchmark/benchmark-stop-policy.js +8 -5
- package/packages/piagent-core/benchmark/benchmark-suite-identity.js +31 -0
- package/packages/piagent-core/benchmark/benchmark-suite-runtime.js +13 -9
- package/packages/piagent-core/benchmark/benchmark-suite.js +149 -2
- package/packages/piagent-core/benchmark/benchmark-summary-support.js +380 -0
- package/packages/piagent-core/benchmark/benchmark-timing-diagnostics.js +971 -0
- package/packages/piagent-core/benchmark/benchmark-usage.js +322 -0
- package/packages/piagent-core/benchmark/benchmark-workflow.js +166 -0
- package/packages/piagent-core/capabilities/runtime-integrity.js +3 -0
- package/packages/piagent-core/docs/context-efficiency-improvement-plan.md +1080 -0
- package/packages/piagent-core/extensions/acceptance-behavior-proof.js +806 -0
- package/packages/piagent-core/extensions/acceptance-contract-semantics.js +83 -69
- package/packages/piagent-core/extensions/acceptance-error-classes.js +160 -0
- package/packages/piagent-core/extensions/acceptance-executable-evidence.js +12 -9
- package/packages/piagent-core/extensions/acceptance-receipt.js +123 -59
- package/packages/piagent-core/extensions/acceptance-state-machine-evidence.js +518 -0
- package/packages/piagent-core/extensions/acceptance-test-binding-evidence.js +60 -0
- package/packages/piagent-core/extensions/context-efficiency-metrics.js +302 -0
- package/packages/piagent-core/extensions/context-efficiency-report.js +167 -0
- package/packages/piagent-core/extensions/context-engine.js +149 -156
- package/packages/piagent-core/extensions/context-evidence.js +48 -0
- package/packages/piagent-core/extensions/context-import-links.js +296 -0
- package/packages/piagent-core/extensions/context-index-policy.js +4 -0
- package/packages/piagent-core/extensions/context-query-paths.js +57 -0
- package/packages/piagent-core/extensions/context-telemetry-integrity.js +65 -0
- package/packages/piagent-core/extensions/criterion-context-pack.js +435 -7
- package/packages/piagent-core/extensions/criterion-graph.js +82 -9
- package/packages/piagent-core/extensions/fallback-base-policy.ts +101 -0
- package/packages/piagent-core/extensions/guard-types.ts +6 -0
- package/packages/piagent-core/extensions/javascript-regex-evidence.js +32 -0
- package/packages/piagent-core/extensions/piagent-guard.ts +420 -260
- package/packages/piagent-core/extensions/task-contract-view.js +17 -3
- package/packages/piagent-core/extensions/task-lifecycle.js +39 -2
- package/packages/piagent-core/extensions/task-state.js +109 -142
- package/packages/piagent-core/extensions/verification-intelligence.js +52 -13
- package/packages/piagent-core/extensions/workspace-evidence-roots.js +193 -0
- package/packages/piagent-core/package.json +1 -1
- package/packages/piagent-core/policies/base-policy.json +44 -2
- package/packages/piagent-core/policy/authority-manifest.v1.json +3 -3
- package/packages/piagent-core/prompts/be-to-fe.md +2 -8
- package/packages/piagent-core/prompts/plan.md +1 -1
- package/packages/piagent-core/prompts/platform-improve.md +2 -6
- package/packages/piagent-core/prompts/review.md +2 -7
- package/packages/piagent-core/prompts/scout.md +1 -1
- package/packages/piagent-core/prompts/task.md +4 -3
- package/packages/piagent-core/runtime/context/context-delivery.ts +103 -0
- package/packages/piagent-core/runtime/context/context-delta-shadow.ts +53 -0
- package/packages/piagent-core/runtime/context/context-efficiency-format.ts +47 -0
- package/packages/piagent-core/runtime/context/context-evidence-qualification.ts +32 -0
- package/packages/piagent-core/runtime/context/prefix-telemetry.ts +47 -0
- package/packages/piagent-core/runtime/context/repository-memory-hints.ts +27 -0
- package/packages/piagent-core/runtime/hooks/agent-prompt-telemetry.ts +56 -0
- package/packages/piagent-core/runtime/hooks/agent-start-hook.ts +162 -102
- package/packages/piagent-core/runtime/hooks/completion-hook.ts +12 -12
- package/packages/piagent-core/runtime/hooks/input-hook.ts +17 -1
- package/packages/piagent-core/runtime/hooks/session-hooks.ts +22 -2
- package/packages/piagent-core/runtime/hooks/session-start-hook.ts +107 -3
- package/packages/piagent-core/runtime/hooks/tool-result-content-guards.ts +95 -0
- package/packages/piagent-core/runtime/hooks/tool-result-hook.ts +134 -166
- package/packages/piagent-core/runtime/hooks/tool-result-value-helpers.ts +66 -0
- package/packages/piagent-core/runtime/inspection/activity-event-adapter.ts +4 -2
- package/packages/piagent-core/runtime/inspection/activity-recovery.ts +104 -0
- package/packages/piagent-core/runtime/inspection/bounded-emission-guard.ts +286 -0
- package/packages/piagent-core/runtime/inspection/runtime-event-store.ts +37 -7
- package/packages/piagent-core/runtime/inspection/task-compaction-history.ts +1 -1
- package/packages/piagent-core/runtime/inspection/task-handoff-history.ts +1 -1
- package/packages/piagent-core/runtime/inspection/task-subagent-tree.ts +27 -4
- package/packages/piagent-core/runtime/inspection/tool-failure-classification.ts +70 -0
- package/packages/piagent-core/runtime/inspection/webui-snapshot.ts +73 -9
- package/packages/piagent-core/runtime/model/authenticated-catalog.ts +17 -9
- package/packages/piagent-core/runtime/model/openai-codex-reasoning.ts +83 -0
- package/packages/piagent-core/runtime/model/provider-wire-fingerprint.ts +238 -0
- package/packages/piagent-core/runtime/orchestration/helper-lifecycle.ts +16 -12
- package/packages/piagent-core/runtime/orchestration/minimal-delegation-policy.ts +71 -0
- package/packages/piagent-core/runtime/orchestration/owned-work-budget.ts +28 -20
- package/packages/piagent-core/runtime/orchestration/role-policy.ts +27 -9
- package/packages/piagent-core/runtime/orchestration/subagent-tool-policy.ts +329 -0
- package/packages/piagent-core/runtime/policy/block-remedy.ts +0 -4
- package/packages/piagent-core/runtime/product/activity-inspector.ts +7 -2
- package/packages/piagent-core/runtime/product/efficiency-metrics.ts +7 -2
- package/packages/piagent-core/runtime/quality/edit-freshness-guard.ts +102 -0
- package/packages/piagent-core/runtime/quality/exact-output-contract.ts +22 -11
- package/packages/piagent-core/runtime/quality/performance-assurance-signals.ts +23 -0
- package/packages/piagent-core/runtime/quality/performance-assurance.ts +9 -5
- package/packages/piagent-core/runtime/recovery/continuation-budget.ts +1 -1
- package/packages/piagent-core/runtime/recovery/edit-recovery-context.ts +113 -0
- package/packages/piagent-core/runtime/recovery/edit-recovery-delivery.ts +42 -0
- package/packages/piagent-core/runtime/recovery/handoff-projection.ts +4 -1
- package/packages/piagent-core/runtime/recovery/pristine-task-policy-refresh.ts +91 -0
- package/packages/piagent-core/runtime/recovery/recovery-policy.ts +7 -4
- package/packages/piagent-core/runtime/recovery/resume-state.ts +112 -14
- package/packages/piagent-core/runtime/recovery/semantic-repair-handshake.ts +60 -13
- package/packages/piagent-core/runtime/recovery/semantic-repair-runtime.ts +55 -2
- package/packages/piagent-core/runtime/recovery/task-evidence-coverage-refresh.ts +217 -0
- package/packages/piagent-core/runtime/registration/context-commands.ts +82 -7
- package/packages/piagent-core/runtime/registration/knowledge-tools.ts +9 -5
- package/packages/piagent-core/runtime/registration/operator-request-intake.ts +49 -0
- package/packages/piagent-core/runtime/registration/policy-tools.ts +50 -15
- package/packages/piagent-core/runtime/registration/session-commands.ts +1 -1
- package/packages/piagent-core/runtime/registration/task-completion-tools.ts +61 -25
- package/packages/piagent-core/runtime/registration/task-evidence-tools.ts +21 -12
- package/packages/piagent-core/runtime/registration/task-start-guidance.ts +169 -0
- package/packages/piagent-core/runtime/registration/task-start-manifest.ts +54 -0
- package/packages/piagent-core/runtime/registration/task-start-retry-helpers.ts +37 -0
- package/packages/piagent-core/runtime/registration/task-start-tool.ts +108 -103
- package/packages/piagent-core/runtime/runtime-limits.ts +16 -2
- package/packages/piagent-core/runtime/session/adaptive-context-analysis.ts +461 -0
- package/packages/piagent-core/runtime/session/adaptive-context-governor.ts +464 -0
- package/packages/piagent-core/runtime/session/adaptive-context-ledger.ts +265 -0
- package/packages/piagent-core/runtime/session/message-signals.ts +7 -3
- package/packages/piagent-core/runtime/session/operator-request-carry.ts +44 -0
- package/packages/piagent-core/runtime/session/runtime-state.ts +161 -5
- package/packages/piagent-core/runtime/session/source-checkout-read-grants.ts +35 -0
- package/packages/piagent-core/runtime/session/system-prompt.ts +76 -14
- package/packages/piagent-core/runtime/session/task-contract-presentation.ts +39 -0
- package/packages/piagent-core/runtime/tools/apply-patch-tool.ts +500 -0
- package/packages/piagent-core/runtime/tools/phase-tools.ts +8 -8
- package/packages/piagent-core/runtime/tools/tool-groups.ts +12 -2
- package/packages/piagent-core/runtime/trajectory/trajectory-runtime.ts +2 -1
- package/packages/piagent-core/runtime/verification/exact-verifier-reuse.ts +101 -0
- package/packages/piagent-core/runtime/workflows/runtime-intake-compaction.ts +164 -0
- package/packages/piagent-core/runtime/workflows/task-intake.ts +207 -38
- package/packages/piagent-core/security/sensitive-data.js +13 -218
- package/packages/piagent-core/security/sensitive-project-file.js +42 -0
- package/packages/piagent-core/security/sensitive-source-expression.js +330 -0
- package/packages/piagent-core/security/sensitive-source-formats.js +394 -0
- package/packages/piagent-core/security/sensitive-text.js +234 -0
- package/packages/piagent-core/subagents/piagent-oracle.md +5 -3
- package/packages/piagent-core/subagents/piagent-planner.md +6 -4
- package/packages/piagent-core/subagents/piagent-reviewer.md +5 -3
- package/packages/piagent-core/subagents/piagent-scout.md +4 -2
- package/packages/piagent-core/subagents/piagent-worker.md +7 -5
- package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-rD9WJKxs.js → MarkdownMessage-B4XehTCU.js} +1 -1
- package/packages/piagent-webui/dist/client/assets/index-CgOEyRPG.js +176 -0
- package/packages/piagent-webui/dist/client/index.html +1 -1
- package/packages/piagent-webui/extension/piagent-webui.ts +3 -1
- package/packages/piagent-webui/extension/same-session-bridge.ts +1 -1
- package/packages/piagent-webui/extension/session-stream-adapter.ts +33 -10
- package/packages/piagent-webui/gateway/gateway-events.ts +109 -5
- package/packages/piagent-webui/gateway/gateway-service.ts +3 -0
- package/packages/piagent-webui/gateway/gateway-session-stream.ts +122 -20
- package/packages/piagent-webui/gateway/session-command-controller.ts +18 -3
- package/packages/piagent-webui/gateway/session-effective-options.ts +47 -0
- package/packages/piagent-webui/gateway/session-live-state.ts +21 -0
- package/packages/piagent-webui/gateway/session-operation-lifecycle.ts +237 -0
- package/packages/piagent-webui/gateway/session-operation-retry-control.ts +38 -0
- package/packages/piagent-webui/gateway/session-operation-start.ts +5 -1
- package/packages/piagent-webui/gateway/session-operation-watchdog.ts +165 -0
- package/packages/piagent-webui/gateway/session-runtime-factory.ts +45 -0
- package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +141 -125
- package/packages/piagent-webui/ownership/session-lease-store.ts +5 -2
- package/packages/piagent-webui/server/core-inspection-provider.ts +2 -1
- package/packages/piagent-webui/server/loopback-server.ts +8 -0
- package/packages/piagent-webui/server/transcript-projection.ts +113 -9
- package/packages/piagent-webui/shared/text-visibility.ts +10 -0
- package/schemas/codex-relative-efficiency-v1.schema.json +155 -0
- package/schemas/piagent-webui/catalog-v1.json +9 -0
- package/schemas/piagent-webui/gateway-protocol-v1.schema.json +53 -3
- package/schemas/piagent-webui/runtime-event-v2.schema.json +15 -5
- package/schemas/piagent-webui/session-live-state-v1.schema.json +76 -0
- package/schemas/piagent-webui/subagent-tree-v1.schema.json +11 -1
- package/schemas/project-profile.schema.json +1 -1
- package/schemas/task-contract.schema.json +27 -1
- package/scripts/benchmark-runner-core.mjs +367 -363
- package/scripts/benchmark-runner-finalization.mjs +313 -0
- package/scripts/benchmark-runner-host-readiness.mjs +94 -0
- package/scripts/benchmark-runner-support.mjs +200 -0
- package/scripts/benchmark-session.mjs +95 -41
- package/scripts/configure-model-scope.sh +4 -3
- package/scripts/configure-subagents.sh +42 -34
- package/scripts/helper-evaluation.mjs +2 -2
- package/scripts/install-global.sh +2 -2
- package/scripts/pi-usage-history.mjs +37 -10
- package/scripts/setup.sh +2 -2
- package/scripts/team-doctor.sh +1 -1
- package/scripts/verify-local.sh +10 -1
- package/templates/global/settings.json +2 -1
- package/packages/piagent-webui/dist/client/assets/index-BZRFw1u6.js +0 -174
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { durableContextEvidenceEntries, isRuntimeOwnedContextEvidenceEntry } from "../../extensions/context-evidence.js";
|
|
3
|
+
import { mergeObservedTaskContext } from "../../extensions/task-contract-view.js";
|
|
2
4
|
import { buildHandoffProjection, writeHandoffProjection } from "../recovery/handoff-projection.ts";
|
|
3
5
|
import { buildCompletionReceiptView } from "../product/operator-projections.ts";
|
|
4
6
|
import { semanticRepairProvenance } from "../recovery/semantic-repair-handshake.ts";
|
|
@@ -7,6 +9,9 @@ import { readTrajectoryStore } from "../trajectory/trajectory-store.ts";
|
|
|
7
9
|
|
|
8
10
|
type TaskContract = any;
|
|
9
11
|
|
|
12
|
+
function taskErrorDetails(task: TaskContract, reasonCode: string): Record<string, unknown> {
|
|
13
|
+
return { reasonCode, taskId: task.taskId, taskRunId: task.taskRunId, outcome: task.trace?.outcome, attempt: task.attempt };
|
|
14
|
+
}
|
|
10
15
|
|
|
11
16
|
export function registerTaskCompletionTools(pi: ExtensionAPI, deps: Record<string, any>): void {
|
|
12
17
|
const {
|
|
@@ -39,15 +44,52 @@ export function registerTaskCompletionTools(pi: ExtensionAPI, deps: Record<strin
|
|
|
39
44
|
return { content: [{ type: "text", text: `Task not found: ${params.taskId}` }], isError: true };
|
|
40
45
|
}
|
|
41
46
|
if (task.trace.outcome !== "pending") {
|
|
42
|
-
return { content: [{ type: "text", text: `Task ${task.taskId} is immutable after ${task.trace.outcome}; context evidence was not changed.` }], details: task, isError: true };
|
|
47
|
+
return { content: [{ type: "text", text: `Task ${task.taskId} is immutable after ${task.trace.outcome}; context evidence was not changed.` }], details: taskErrorDetails(task, "task-immutable"), isError: true };
|
|
48
|
+
}
|
|
49
|
+
const activeIdentity = runtimeState.taskIdentity(ctx);
|
|
50
|
+
if (!activeIdentity || activeIdentity.taskId !== task.taskId || activeIdentity.taskRunId !== task.taskRunId) {
|
|
51
|
+
return {
|
|
52
|
+
content: [{ type: "text", text: `Context evidence rejected for ${task.taskId}: the active runtime task identity does not match this session task.` }],
|
|
53
|
+
details: { reasonCode: "context-evidence-task-identity-mismatch" },
|
|
54
|
+
isError: true
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
const observedPaths = new Set(runtimeState.observedContext(ctx)
|
|
58
|
+
.map((entry: { path: string }) => normalizeRelative(ctx.cwd, entry.path))
|
|
59
|
+
.filter((filePath: string | undefined): filePath is string => Boolean(filePath)));
|
|
60
|
+
const qualifiedByPath = new Map<string, { path: string; reason: string }>();
|
|
61
|
+
for (const entry of runtimeState.qualifiedContextEvidence(ctx, task.taskRunId)) {
|
|
62
|
+
const filePath = normalizeRelative(ctx.cwd, entry.path);
|
|
63
|
+
if (filePath && isRuntimeOwnedContextEvidenceEntry(entry)) qualifiedByPath.set(filePath, entry);
|
|
64
|
+
}
|
|
65
|
+
const qualifiedFiles = new Map<string, { path: string; reason: string }>();
|
|
66
|
+
const unprovenPaths: string[] = [];
|
|
67
|
+
for (const file of params.files) {
|
|
68
|
+
const filePath = normalizeRelative(ctx.cwd, file.path);
|
|
69
|
+
const evidence = filePath ? qualifiedByPath.get(filePath) : undefined;
|
|
70
|
+
if (!filePath || !observedPaths.has(filePath) || !evidence) {
|
|
71
|
+
unprovenPaths.push(redactText(file.path));
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
qualifiedFiles.set(filePath, { path: filePath, reason: evidence.reason });
|
|
75
|
+
}
|
|
76
|
+
if (unprovenPaths.length > 0) {
|
|
77
|
+
return {
|
|
78
|
+
content: [{ type: "text", text: `Context evidence rejected for ${task.taskId}: no runtime-observed read or host-confirmed delivery exists in this task/session for ${unprovenPaths.join(", ")}. Read the exact file successfully in this task or rely on automatic delivery confirmation.` }],
|
|
79
|
+
details: { reasonCode: "context-evidence-unproven", paths: unprovenPaths },
|
|
80
|
+
isError: true
|
|
81
|
+
};
|
|
43
82
|
}
|
|
44
|
-
|
|
83
|
+
const safeFiles = [...qualifiedFiles.values()];
|
|
84
|
+
const evidencePaths = new Set(durableContextEvidenceEntries(task).map((item) => item.path));
|
|
85
|
+
const projectedManifestFiles = evidencePaths.size + new Set(safeFiles.map((item) => item.path).filter((filePath) => !evidencePaths.has(filePath))).size;
|
|
86
|
+
if (runtime.contextBudget !== "off" && projectedManifestFiles > budget.maxManifestFiles) {
|
|
45
87
|
return {
|
|
46
|
-
content: [{ type: "text", text: `Context manifest budget exceeded: ${
|
|
88
|
+
content: [{ type: "text", text: `Context manifest budget exceeded: ${projectedManifestFiles} files > ${budget.maxManifestFiles}` }],
|
|
47
89
|
isError: true
|
|
48
90
|
};
|
|
49
91
|
}
|
|
50
|
-
const fileBudget =
|
|
92
|
+
const fileBudget = safeFiles.map((file) => candidateFileBudget(ctx.cwd, file.path, budget));
|
|
51
93
|
const overLimit = fileBudget.filter((item) => item.overLimit);
|
|
52
94
|
if (runtime.contextBudget === "enforce" && overLimit.length > 0) {
|
|
53
95
|
return {
|
|
@@ -56,17 +98,8 @@ export function registerTaskCompletionTools(pi: ExtensionAPI, deps: Record<strin
|
|
|
56
98
|
isError: true
|
|
57
99
|
};
|
|
58
100
|
}
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
path: file.path,
|
|
62
|
-
reason: redactText(file.reason)
|
|
63
|
-
}));
|
|
64
|
-
const seen = new Set(task.contextManifest.map((item) => `${item.path}\u0000${item.reason}`));
|
|
65
|
-
for (const file of safeFiles) {
|
|
66
|
-
const key = `${file.path}\u0000${file.reason}`;
|
|
67
|
-
if (!seen.has(key)) task.contextManifest.push(file);
|
|
68
|
-
}
|
|
69
|
-
const lifecycle = task.changeMode === "read-only"
|
|
101
|
+
mergeObservedTaskContext(task, safeFiles, budget.maxManifestFiles, (value) => value);
|
|
102
|
+
const lifecycle = task.changeMode === "read-only" || task.mutationPolicy === "forbidden"
|
|
70
103
|
? applyRuntimeLifecycleObservation(task, "context-complete", nowIso())
|
|
71
104
|
: { changed: false, mode: runtimeLifecycleMode(task) };
|
|
72
105
|
const written = writeTask(ctx.cwd, task);
|
|
@@ -74,7 +107,7 @@ export function registerTaskCompletionTools(pi: ExtensionAPI, deps: Record<strin
|
|
|
74
107
|
appendSessionTrace(pi, { taskId: task.taskId, taskRunId: task.taskRunId, sessionId: task.sessionId, event: "context_record", files: safeFiles, lifecycleMode: lifecycle.mode, lifecycleAdvanced: lifecycle.changed });
|
|
75
108
|
|
|
76
109
|
return {
|
|
77
|
-
content: [{ type: "text", text: `Context recorded for ${task.taskId}: ${
|
|
110
|
+
content: [{ type: "text", text: `Context recorded for ${task.taskId}: ${safeFiles.length} runtime-qualified file(s)` }],
|
|
78
111
|
details: compactTaskDetails(written)
|
|
79
112
|
};
|
|
80
113
|
}
|
|
@@ -97,7 +130,7 @@ export function registerTaskCompletionTools(pi: ExtensionAPI, deps: Record<strin
|
|
|
97
130
|
return { content: [{ type: "text", text: `Task not found: ${params.taskId}` }], isError: true };
|
|
98
131
|
}
|
|
99
132
|
if (task.trace.outcome !== "pending") {
|
|
100
|
-
return { content: [{ type: "text", text: `Task ${task.taskId} is immutable after ${task.trace.outcome}; verify evidence was not changed.` }], details: task, isError: true };
|
|
133
|
+
return { content: [{ type: "text", text: `Task ${task.taskId} is immutable after ${task.trace.outcome}; verify evidence was not changed.` }], details: taskErrorDetails(task, "task-immutable"), isError: true };
|
|
101
134
|
}
|
|
102
135
|
|
|
103
136
|
const observedEntries = [
|
|
@@ -209,7 +242,7 @@ export function registerTaskCompletionTools(pi: ExtensionAPI, deps: Record<strin
|
|
|
209
242
|
return { content: [{ type: "text", text: `Task not found: ${params.taskId}` }], isError: true };
|
|
210
243
|
}
|
|
211
244
|
if (task.trace.outcome !== "pending") {
|
|
212
|
-
return { content: [{ type: "text", text: `Task ${task.taskId} is immutable after ${task.trace.outcome}; memory evidence was not changed.` }], details: task, isError: true };
|
|
245
|
+
return { content: [{ type: "text", text: `Task ${task.taskId} is immutable after ${task.trace.outcome}; memory evidence was not changed.` }], details: taskErrorDetails(task, "task-immutable"), isError: true };
|
|
213
246
|
}
|
|
214
247
|
|
|
215
248
|
const safeFiles = params.files.map((file) => ({
|
|
@@ -227,7 +260,7 @@ export function registerTaskCompletionTools(pi: ExtensionAPI, deps: Record<strin
|
|
|
227
260
|
|
|
228
261
|
return {
|
|
229
262
|
content: [{ type: "text", text: `Memory citations recorded for ${task.taskId}: ${params.files.length} file(s)` }],
|
|
230
|
-
details: task
|
|
263
|
+
details: compactTaskDetails(task)
|
|
231
264
|
};
|
|
232
265
|
}
|
|
233
266
|
});
|
|
@@ -254,13 +287,13 @@ export function registerTaskCompletionTools(pi: ExtensionAPI, deps: Record<strin
|
|
|
254
287
|
return { content: [{ type: "text", text: `Task not found: ${params.taskId}` }], isError: true };
|
|
255
288
|
}
|
|
256
289
|
if (task.trace.outcome !== "pending") {
|
|
257
|
-
return { content: [{ type: "text", text: `Task ${task.taskId} is immutable after ${task.trace.outcome}; its final trace was not replaced.` }], details: task, isError: true };
|
|
290
|
+
return { content: [{ type: "text", text: `Task ${task.taskId} is immutable after ${task.trace.outcome}; its final trace was not replaced.` }], details: taskErrorDetails(task, "task-immutable"), isError: true };
|
|
258
291
|
}
|
|
259
292
|
if (params.outcome !== "completed" && !params.friction?.trim() && !task.failureReason?.trim()) {
|
|
260
|
-
return { content: [{ type: "text", text: `Trace ${params.outcome} requires a concrete friction/reason so the next attempt does not repeat the same work.` }], details: task, isError: true };
|
|
293
|
+
return { content: [{ type: "text", text: `Trace ${params.outcome} requires a concrete friction/reason so the next attempt does not repeat the same work.` }], details: taskErrorDetails(task, "trace-friction-required"), isError: true };
|
|
261
294
|
}
|
|
262
295
|
if (params.outcome === "failed" && !params.failedAt && !task.failedAt) {
|
|
263
|
-
return { content: [{ type: "text", text: "A failed trace requires failedAt to identify the lifecycle phase." }], details: task, isError: true };
|
|
296
|
+
return { content: [{ type: "text", text: "A failed trace requires failedAt to identify the lifecycle phase." }], details: taskErrorDetails(task, "trace-failure-phase-required"), isError: true };
|
|
264
297
|
}
|
|
265
298
|
|
|
266
299
|
const rawChangedFiles = params.changedFiles ?? task.changedFiles;
|
|
@@ -296,7 +329,10 @@ export function registerTaskCompletionTools(pi: ExtensionAPI, deps: Record<strin
|
|
|
296
329
|
currentDigests: finalFileDigests,
|
|
297
330
|
currentWorkingTreeDigest: workingTreeEvidenceDigest(finalFileDigests)
|
|
298
331
|
});
|
|
299
|
-
const semanticBlock = taskAuthorityDecision(nextTask, "CAP-13", "block").allowed
|
|
332
|
+
const semanticBlock = (taskAuthorityDecision(nextTask, "CAP-13", "block").allowed
|
|
333
|
+
|| taskAuthorityDecision(nextTask, "CAP-12", "mutate").allowed)
|
|
334
|
+
? semanticRepairCompletionBlock?.(ctx.cwd, nextTask.taskRunId)
|
|
335
|
+
: undefined;
|
|
300
336
|
if (semanticBlock) gate = { ...gate, decision: "fail", missing: [...new Set([...gate.missing, semanticBlock])] };
|
|
301
337
|
if (params.outcome === "completed" && (semanticBlock || (runtime.finalGate === "enforce" && gate.decision === "fail"))) {
|
|
302
338
|
const blockedTrace = {
|
|
@@ -330,7 +366,7 @@ export function registerTaskCompletionTools(pi: ExtensionAPI, deps: Record<strin
|
|
|
330
366
|
...verifierCommandInstructions(gate.missingVerifyCommands)
|
|
331
367
|
].join("\n")
|
|
332
368
|
}],
|
|
333
|
-
details: { gate, task: nextTask },
|
|
369
|
+
details: { gate, task: taskErrorDetails(nextTask, "completion-gate-blocked") },
|
|
334
370
|
isError: true
|
|
335
371
|
};
|
|
336
372
|
}
|
|
@@ -393,7 +429,7 @@ export function registerTaskCompletionTools(pi: ExtensionAPI, deps: Record<strin
|
|
|
393
429
|
|
|
394
430
|
return {
|
|
395
431
|
content: [{ type: "text", text: `Trace recorded for ${nextTask.taskId}: ${params.outcome}${gate.decision === "fail" ? ` (gate warning: missing ${gate.missing.join(", ")})` : ""}` }],
|
|
396
|
-
details: { task: written, gate, completionReceipt: buildCompletionReceiptView(written, { cwd: ctx.cwd, gate }) }
|
|
432
|
+
details: { task: compactTaskDetails(written), gate, completionReceipt: buildCompletionReceiptView(written, { cwd: ctx.cwd, gate }) }
|
|
397
433
|
};
|
|
398
434
|
}
|
|
399
435
|
});
|
|
@@ -2,6 +2,9 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
|
2
2
|
|
|
3
3
|
type WorkPlanStep = any;
|
|
4
4
|
|
|
5
|
+
function taskErrorDetails(task: any, reasonCode: string): Record<string, unknown> {
|
|
6
|
+
return { reasonCode, taskId: task.taskId, taskRunId: task.taskRunId, outcome: task.trace?.outcome, attempt: task.attempt };
|
|
7
|
+
}
|
|
5
8
|
|
|
6
9
|
export function registerTaskEvidenceTools(pi: ExtensionAPI, deps: Record<string, any>): void {
|
|
7
10
|
const {
|
|
@@ -9,7 +12,7 @@ export function registerTaskEvidenceTools(pi: ExtensionAPI, deps: Record<string,
|
|
|
9
12
|
compactTaskDetails, effectiveProtectedPaths, extensionDir, extractDocument, loadProfileFromContext,
|
|
10
13
|
matchesAnyPath, matchesProtectedPath, nowIso, path, permissionOverrideFromContext,
|
|
11
14
|
policy, readTask, recordTaskProgressCheckpoints, redactText, registerPiagentTool,
|
|
12
|
-
resolveDocumentPath, resolveDocumentRoots, resolvePermissionProfile, safeTaskId, verifyProjectCapabilityState,
|
|
15
|
+
resolveDocumentPath, resolveDocumentRoots, resolvePermissionProfile, runtimeState, safeTaskId, verifyProjectCapabilityState,
|
|
13
16
|
writeTask
|
|
14
17
|
} = deps;
|
|
15
18
|
registerPiagentTool(pi, {
|
|
@@ -37,24 +40,26 @@ export function registerTaskEvidenceTools(pi: ExtensionAPI, deps: Record<string,
|
|
|
37
40
|
return { content: [{ type: "text", text: `Task not found in this session: ${params.taskId}` }], isError: true };
|
|
38
41
|
}
|
|
39
42
|
if (task.trace.outcome !== "pending") {
|
|
40
|
-
return { content: [{ type: "text", text: `Task ${task.taskId} is immutable after ${task.trace.outcome}; start a fresh session for another attempt.` }], details: task, isError: true };
|
|
43
|
+
return { content: [{ type: "text", text: `Task ${task.taskId} is immutable after ${task.trace.outcome}; start a fresh session for another attempt.` }], details: taskErrorDetails(task, "task-immutable"), isError: true };
|
|
41
44
|
}
|
|
42
45
|
const stepId = safeTaskId(params.stepId).slice(0, 40);
|
|
43
46
|
const step = task.workPlan.find((item) => item.id === stepId);
|
|
44
47
|
if (!step) {
|
|
45
|
-
const
|
|
48
|
+
const validSteps = task.workPlan.slice(0, 20).map((item) => ({ id: item.id, status: item.status }));
|
|
49
|
+
const valid = validSteps.map((item) => `${item.id} (${item.status})`).join(", ");
|
|
46
50
|
const actionable = task.workPlan
|
|
47
51
|
.filter((item) => item.status === "in-progress" || (
|
|
48
52
|
item.status === "pending"
|
|
49
53
|
&& (item.dependsOn ?? []).every((dependency) => task.workPlan.some((candidate) => candidate.id === dependency && ["done", "skipped"].includes(candidate.status)))
|
|
50
54
|
))
|
|
51
|
-
.map((item) => item.id)
|
|
55
|
+
.map((item) => item.id)
|
|
56
|
+
.slice(0, 20);
|
|
52
57
|
return {
|
|
53
58
|
content: [{
|
|
54
59
|
type: "text",
|
|
55
60
|
text: `Work-plan step not found: ${stepId}. Valid step IDs: ${valid || "none"}. Currently actionable: ${actionable.join(", ") || "none"}.`
|
|
56
61
|
}],
|
|
57
|
-
details: task
|
|
62
|
+
details: { ...taskErrorDetails(task, "work-plan-step-not-found"), stepId, validSteps, actionable },
|
|
58
63
|
isError: true
|
|
59
64
|
};
|
|
60
65
|
}
|
|
@@ -66,7 +71,7 @@ export function registerTaskEvidenceTools(pi: ExtensionAPI, deps: Record<string,
|
|
|
66
71
|
if ((params.status === "in-progress" || params.status === "done") && unresolved.length > 0) {
|
|
67
72
|
return {
|
|
68
73
|
content: [{ type: "text", text: `Step ${stepId} is blocked by unfinished dependencies: ${unresolved.join(", ")}` }],
|
|
69
|
-
details: { step, unresolved },
|
|
74
|
+
details: { ...taskErrorDetails(task, "work-plan-step-blocked"), stepId, unresolved: unresolved.slice(0, 20) },
|
|
70
75
|
isError: true
|
|
71
76
|
};
|
|
72
77
|
}
|
|
@@ -75,13 +80,13 @@ export function registerTaskEvidenceTools(pi: ExtensionAPI, deps: Record<string,
|
|
|
75
80
|
return { content: [{ type: "text", text: `A concrete note is required when step ${stepId} fails.` }], isError: true };
|
|
76
81
|
}
|
|
77
82
|
if ((step.status === "done" || step.status === "skipped") && params.status !== step.status) {
|
|
78
|
-
return { content: [{ type: "text", text: `Work-plan step ${stepId} is already ${step.status} and cannot be reopened in the same attempt.` }], details: step, isError: true };
|
|
83
|
+
return { content: [{ type: "text", text: `Work-plan step ${stepId} is already ${step.status} and cannot be reopened in the same attempt.` }], details: { ...taskErrorDetails(task, "work-plan-step-immutable"), stepId, status: step.status }, isError: true };
|
|
79
84
|
}
|
|
80
85
|
if (step.status === params.status) {
|
|
81
|
-
return { content: [{ type: "text", text: `Work-plan step ${stepId} is already ${params.status}; no state change was recorded.` }], details: step, isError: true };
|
|
86
|
+
return { content: [{ type: "text", text: `Work-plan step ${stepId} is already ${params.status}; no state change was recorded.` }], details: { ...taskErrorDetails(task, "work-plan-step-unchanged"), stepId, status: step.status }, isError: true };
|
|
82
87
|
}
|
|
83
88
|
if (step.status === "failed" && params.status === "in-progress" && !note) {
|
|
84
|
-
return { content: [{ type: "text", text: `A concrete note is required to reopen failed step ${stepId} within this attempt.` }], details: step, isError: true };
|
|
89
|
+
return { content: [{ type: "text", text: `A concrete note is required to reopen failed step ${stepId} within this attempt.` }], details: { ...taskErrorDetails(task, "work-plan-step-reopen-note-required"), stepId, status: step.status }, isError: true };
|
|
85
90
|
}
|
|
86
91
|
|
|
87
92
|
const recordedAt = nowIso();
|
|
@@ -254,7 +259,7 @@ export function registerTaskEvidenceTools(pi: ExtensionAPI, deps: Record<string,
|
|
|
254
259
|
registerPiagentTool(pi, {
|
|
255
260
|
name: "piagent_source_checkout",
|
|
256
261
|
label: "Piagent Source Checkout",
|
|
257
|
-
description: "
|
|
262
|
+
description: "Prepare an external Git repository for bounded, read-only local inspection.",
|
|
258
263
|
promptSnippet: "Use this before reading a user-provided external source repository.",
|
|
259
264
|
promptGuidelines: [
|
|
260
265
|
"Use for GitHub/GitLab/Bitbucket source repositories supplied by the user.",
|
|
@@ -264,15 +269,19 @@ export function registerTaskEvidenceTools(pi: ExtensionAPI, deps: Record<string,
|
|
|
264
269
|
repoRef: Type.String({ minLength: 3, description: "owner/repo, host/owner/repo, https URL, or git@host:owner/repo.git" }),
|
|
265
270
|
forceUpdate: Type.Optional(Type.Boolean({ description: "Fetch immediately even if the cache was refreshed recently." }))
|
|
266
271
|
}),
|
|
267
|
-
async execute(_toolCallId, params) {
|
|
272
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
268
273
|
try {
|
|
269
274
|
const repo = checkoutReferenceRepo(params.repoRef, params.forceUpdate === true);
|
|
275
|
+
const grantedPath = runtimeState.grantSourceCheckoutReadRoot(ctx, repo.checkoutPath);
|
|
276
|
+
repo.checkoutPath = grantedPath;
|
|
270
277
|
const text = [
|
|
271
278
|
"Source cache ready:",
|
|
272
279
|
`path: ${repo.checkoutPath}`,
|
|
273
280
|
`url: ${repo.cloneUrl}`,
|
|
274
281
|
`commit: ${repo.commit ?? "unknown"}`,
|
|
275
|
-
`fetched: ${repo.fetched ? "yes" : "no"}
|
|
282
|
+
`fetched: ${repo.fetched ? "yes" : "no"}`,
|
|
283
|
+
"access: read-only for this session via read, grep, find, or ls",
|
|
284
|
+
"Do not run a checkout script or use shell mutation against this shared cache."
|
|
276
285
|
].join("\n");
|
|
277
286
|
return {
|
|
278
287
|
content: [{ type: "text", text }],
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
type CriterionNode = {
|
|
2
|
+
id?: string;
|
|
3
|
+
obligation?: string;
|
|
4
|
+
kind?: string;
|
|
5
|
+
proofKinds?: string[];
|
|
6
|
+
targetHints?: string[];
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type CriterionGraph = { mode?: string; nodes?: CriterionNode[] } | undefined;
|
|
10
|
+
|
|
11
|
+
export type FocusedTestCandidate = {
|
|
12
|
+
criterionId: string;
|
|
13
|
+
path: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type CriterionProofGuidance = {
|
|
17
|
+
criterionId: string;
|
|
18
|
+
guidance: string[];
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
type ProofTask = {
|
|
22
|
+
changeMode?: string;
|
|
23
|
+
mutationPolicy?: string;
|
|
24
|
+
criterionGraph?: CriterionGraph;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const RUNTIME_SOURCE_REUSE_GUIDANCE = "Use runtime-delivered source; do not reread it. On edit drift/oldText mismatch, use attached recovery; otherwise reread the affected region once. Never retry guessed anchors. Otherwise read only missing source or a named criterion-focused test. Globs/directories grant scope, not file targets.";
|
|
28
|
+
|
|
29
|
+
export const EXACT_VERIFIER_EXECUTION_GUIDANCE = "Keep each exact verifier command separate and unmodified. Run the set once on the final tree; rerun only after a later mutation or a runtime-authorized same-tree infrastructure retry.";
|
|
30
|
+
|
|
31
|
+
function normalizedPath(value: string): string {
|
|
32
|
+
return value.trim().replaceAll("\\", "/");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function exactNameMentioned(obligation: string, file: string): boolean {
|
|
36
|
+
const text = obligation.toLowerCase().replaceAll("\\", "/");
|
|
37
|
+
const normalized = normalizedPath(file).toLowerCase();
|
|
38
|
+
const basename = normalized.split("/").pop() ?? "";
|
|
39
|
+
return [normalized, basename].filter(Boolean).some((candidate) => {
|
|
40
|
+
let offset = text.indexOf(candidate);
|
|
41
|
+
while (offset >= 0) {
|
|
42
|
+
const before = text[offset - 1] ?? "";
|
|
43
|
+
const after = text[offset + candidate.length] ?? "";
|
|
44
|
+
const afterNext = text[offset + candidate.length + 1] ?? "";
|
|
45
|
+
const nameContinuesAfter = /[a-z0-9_/-]/i.test(after) || (after === "." && /[a-z0-9_-]/i.test(afterNext));
|
|
46
|
+
if (!/[a-z0-9_./-]/i.test(before) && !nameContinuesAfter) return true;
|
|
47
|
+
offset = text.indexOf(candidate, offset + candidate.length);
|
|
48
|
+
}
|
|
49
|
+
return false;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function behavioralNodes(criterionGraph: CriterionGraph): CriterionNode[] {
|
|
54
|
+
return (criterionGraph?.nodes ?? []).filter((node) => (
|
|
55
|
+
typeof node?.id === "string"
|
|
56
|
+
&& typeof node?.obligation === "string"
|
|
57
|
+
&& Array.isArray(node?.proofKinds)
|
|
58
|
+
&& node.proofKinds.includes("behavioral-check")
|
|
59
|
+
));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function concretePlannedAcceptanceTests(
|
|
63
|
+
plannedContext: Array<{ path: string; reason: string }>,
|
|
64
|
+
repositoryFiles: string[],
|
|
65
|
+
criterionGraph: CriterionGraph,
|
|
66
|
+
isAcceptanceTestPath: (file: string) => boolean,
|
|
67
|
+
acceptanceLanguageAdapterForPath: (file: string) => { disposition?: string }
|
|
68
|
+
): FocusedTestCandidate[] {
|
|
69
|
+
const concreteFiles = new Set(repositoryFiles.map(normalizedPath));
|
|
70
|
+
const nodes = behavioralNodes(criterionGraph);
|
|
71
|
+
const candidates: FocusedTestCandidate[] = [];
|
|
72
|
+
for (const entry of plannedContext) {
|
|
73
|
+
const file = normalizedPath(entry.path);
|
|
74
|
+
if (!file || /[?*[\]{}]/.test(file) || !concreteFiles.has(file)) continue;
|
|
75
|
+
if (!isAcceptanceTestPath(file) || acceptanceLanguageAdapterForPath(file)?.disposition !== "supported") continue;
|
|
76
|
+
if (/\.d\.[cm]?ts$/i.test(file)) continue;
|
|
77
|
+
for (const node of nodes) {
|
|
78
|
+
if (exactNameMentioned(node.obligation!, file)) candidates.push({ criterionId: node.id!, path: file });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const seen = new Set<string>();
|
|
82
|
+
return candidates.filter((candidate) => {
|
|
83
|
+
const identity = `${candidate.criterionId}\0${candidate.path}`;
|
|
84
|
+
if (seen.has(identity)) return false;
|
|
85
|
+
seen.add(identity);
|
|
86
|
+
return true;
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function behavioralCriterionProofGuidance(
|
|
91
|
+
criterionGraph: CriterionGraph,
|
|
92
|
+
acceptanceProofGuidance: (text: string) => string[]
|
|
93
|
+
): CriterionProofGuidance[] {
|
|
94
|
+
return behavioralNodes(criterionGraph).map((node) => ({
|
|
95
|
+
criterionId: node.id!,
|
|
96
|
+
guidance: acceptanceProofGuidance(node.obligation!)
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function criticalProofSection(
|
|
101
|
+
proofGuidance: string[],
|
|
102
|
+
acceptanceTests: FocusedTestCandidate[],
|
|
103
|
+
criterionProofs: CriterionProofGuidance[]
|
|
104
|
+
): string[] {
|
|
105
|
+
if (proofGuidance.length === 0 && criterionProofs.length === 0) return [];
|
|
106
|
+
const candidateMap = new Map<string, string[]>();
|
|
107
|
+
for (const candidate of acceptanceTests) {
|
|
108
|
+
candidateMap.set(candidate.criterionId, [...new Set([...(candidateMap.get(candidate.criterionId) ?? []), candidate.path])]);
|
|
109
|
+
}
|
|
110
|
+
let mappedCandidate = false;
|
|
111
|
+
const criterionTag = (criterionId: string): string => {
|
|
112
|
+
const candidates = candidateMap.get(criterionId) ?? [];
|
|
113
|
+
if (candidates.length === 0) return `[${criterionId}:fallback]`;
|
|
114
|
+
mappedCandidate = true;
|
|
115
|
+
return `[${criterionId}:candidate=${candidates.join("|")}]`;
|
|
116
|
+
};
|
|
117
|
+
const proofLines = proofGuidance.flatMap((proof) => {
|
|
118
|
+
const criterionIds = [...new Set(criterionProofs
|
|
119
|
+
.filter((entry) => entry.guidance.includes(proof))
|
|
120
|
+
.map((entry) => entry.criterionId))];
|
|
121
|
+
if (criterionIds.length === 0) {
|
|
122
|
+
return [`- [fallback] ${proof}`];
|
|
123
|
+
}
|
|
124
|
+
const tags = criterionIds.map(criterionTag);
|
|
125
|
+
return [`- ${tags.join("")} ${proof}`];
|
|
126
|
+
});
|
|
127
|
+
const genericTags = criterionProofs.filter((entry) => entry.guidance.length === 0).map((entry) => criterionTag(entry.criterionId));
|
|
128
|
+
const genericLines = genericTags.length > 0
|
|
129
|
+
? [`- ${genericTags.join("")} Prove every tagged observable clause with linked live assertions; the generic verifier alone is insufficient.`]
|
|
130
|
+
: [];
|
|
131
|
+
return [[
|
|
132
|
+
"Critical behavioral proof:",
|
|
133
|
+
...(!mappedCandidate && genericTags.length === 0 ? ["No concrete criterion-linked focused test was selected."] : []),
|
|
134
|
+
...proofLines,
|
|
135
|
+
...genericLines,
|
|
136
|
+
"Candidate tags are locations, not proof; add/update durable scoped tests with live assertions and changed-test evidence. Each fallback tag requires adding/updating a durable scoped focused test with live assertions. If test scope is unavailable, report missing durable proof and do not claim completion. Run proof after the criterion's final intended mutation and before exact verifiers; rerun after later target mutation or a runtime-authorized same-tree infrastructure retry. Transient/print-only probes and prose are insufficient. Claims must match diff/tests; never claim unproved behavior."
|
|
137
|
+
].join("\n")];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function automaticTaskExecutionGuidance(task: ProofTask): string {
|
|
141
|
+
if (task.changeMode === "read-only" || task.mutationPolicy === "forbidden") {
|
|
142
|
+
return task.changeMode === "source-change"
|
|
143
|
+
? "Stay mutation-free. Runtime permits bounded inspection and the exact configured verifier, records completion evidence, and requires a zero task delta."
|
|
144
|
+
: "Stay read-only. Runtime records targeted reads and completion evidence; do not call task-management tools.";
|
|
145
|
+
}
|
|
146
|
+
return task.criterionGraph?.mode === "criterion-graph"
|
|
147
|
+
? "Follow the execution map and implement dependency-ready criteria. The map plans work but never overrides the operator request or exact verifiers. Runtime records evidence and completion; do not call task-management tools."
|
|
148
|
+
: "Privately map every operator criterion to implementation and durable focused-test coverage before mutating. Finish intended edits and a criterion-by-criterion self-review. Runtime records evidence and completion; do not call task-management tools.";
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function taskCriticalProofSection(
|
|
152
|
+
task: ProofTask,
|
|
153
|
+
plannedContext: Array<{ path: string; reason: string }>,
|
|
154
|
+
repositoryFiles: string[],
|
|
155
|
+
acceptanceProofGuidance: (taskOrText: unknown) => string[],
|
|
156
|
+
isAcceptanceTestPath: (file: string) => boolean,
|
|
157
|
+
acceptanceLanguageAdapterForPath: (file: string) => { disposition?: string },
|
|
158
|
+
requireGenericFallback = true
|
|
159
|
+
): string[] {
|
|
160
|
+
const proofGuidance = task.changeMode === "source-change" && task.mutationPolicy !== "forbidden"
|
|
161
|
+
? acceptanceProofGuidance(task)
|
|
162
|
+
: [];
|
|
163
|
+
const acceptanceTests = concretePlannedAcceptanceTests(
|
|
164
|
+
plannedContext, repositoryFiles, task.criterionGraph, isAcceptanceTestPath, acceptanceLanguageAdapterForPath
|
|
165
|
+
);
|
|
166
|
+
const criterionProofs = behavioralCriterionProofGuidance(task.criterionGraph, acceptanceProofGuidance)
|
|
167
|
+
.filter((entry) => requireGenericFallback || entry.guidance.length > 0);
|
|
168
|
+
return criticalProofSection(proofGuidance, acceptanceTests, criterionProofs);
|
|
169
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
type RepositoryManifestCompatibilityReason = "missing-details-provider" | "nonfunction-details-provider" | "invalid-details-result";
|
|
2
|
+
|
|
3
|
+
type TaskStartRepositoryManifest = {
|
|
4
|
+
files: string[];
|
|
5
|
+
complete: boolean;
|
|
6
|
+
candidateCount: number;
|
|
7
|
+
provider: "details" | "legacy-array-fallback";
|
|
8
|
+
compatibilityReason?: RepositoryManifestCompatibilityReason;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
function normalizedManifestFiles(value: unknown): string[] {
|
|
12
|
+
if (!Array.isArray(value)) return [];
|
|
13
|
+
return [...new Set(value.filter((item): item is string => typeof item === "string" && item.trim().length > 0))];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Keep rolling upgrades safe when a long-lived host has the legacy extension
|
|
18
|
+
* entrypoint cached but imports a newer task-start registration module. The
|
|
19
|
+
* legacy array provider cannot prove whether its result was truncated, so the
|
|
20
|
+
* compatibility path deliberately reports `complete: false` and records the
|
|
21
|
+
* provider/reason in the durable task trace.
|
|
22
|
+
*/
|
|
23
|
+
export function resolveTaskStartRepositoryManifestProvider(deps: Record<string, any>): {
|
|
24
|
+
read(cwd: string, maxFiles?: number): TaskStartRepositoryManifest;
|
|
25
|
+
} {
|
|
26
|
+
const legacyProvider = deps.repositoryFileManifest;
|
|
27
|
+
if (typeof legacyProvider !== "function") {
|
|
28
|
+
throw new TypeError("Piagent task-start registration requires repositoryFileManifest to be a function.");
|
|
29
|
+
}
|
|
30
|
+
const detailsProvider = deps.repositoryFileManifestDetails;
|
|
31
|
+
const compatibilityReason: RepositoryManifestCompatibilityReason | undefined = detailsProvider === undefined
|
|
32
|
+
? "missing-details-provider"
|
|
33
|
+
: typeof detailsProvider !== "function"
|
|
34
|
+
? "nonfunction-details-provider"
|
|
35
|
+
: undefined;
|
|
36
|
+
const legacyManifest = (cwd: string, maxFiles?: number,
|
|
37
|
+
reason: RepositoryManifestCompatibilityReason = compatibilityReason ?? "invalid-details-result"): TaskStartRepositoryManifest => {
|
|
38
|
+
const files = normalizedManifestFiles(legacyProvider(cwd, maxFiles));
|
|
39
|
+
return { files, complete: false, candidateCount: files.length, provider: "legacy-array-fallback", compatibilityReason: reason };
|
|
40
|
+
};
|
|
41
|
+
if (compatibilityReason) return { read: legacyManifest };
|
|
42
|
+
return {
|
|
43
|
+
read(cwd: string, maxFiles?: number): TaskStartRepositoryManifest {
|
|
44
|
+
const details = detailsProvider(cwd, maxFiles);
|
|
45
|
+
if (!details || typeof details !== "object" || !Array.isArray(details.files)) {
|
|
46
|
+
return legacyManifest(cwd, maxFiles, "invalid-details-result");
|
|
47
|
+
}
|
|
48
|
+
const files = normalizedManifestFiles(details.files);
|
|
49
|
+
return { files, complete: details.complete === true,
|
|
50
|
+
candidateCount: Number.isInteger(details.candidateCount) ? Math.max(files.length, details.candidateCount) : files.length,
|
|
51
|
+
provider: "details" };
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
type AuthoritySnapshot = {
|
|
2
|
+
profile: string;
|
|
3
|
+
capabilities: Array<{ id: string; authority: string }>;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
type AuthorityReplacementTask = {
|
|
7
|
+
authoritySnapshot?: AuthoritySnapshot;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
type AuthorityReplacementState = {
|
|
11
|
+
reason?: string;
|
|
12
|
+
killedCapabilities: string[];
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export function sameStringRecord(left: Record<string, string>, right: Record<string, string>): boolean {
|
|
16
|
+
const leftEntries = Object.entries(left).sort(([a], [b]) => a.localeCompare(b));
|
|
17
|
+
const rightEntries = Object.entries(right).sort(([a], [b]) => a.localeCompare(b));
|
|
18
|
+
return leftEntries.length === rightEntries.length
|
|
19
|
+
&& leftEntries.every(([key, value], index) => rightEntries[index]?.[0] === key && rightEntries[index]?.[1] === value);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function satisfiesAuthorityReplacement(
|
|
23
|
+
task: AuthorityReplacementTask,
|
|
24
|
+
state: AuthorityReplacementState
|
|
25
|
+
): boolean {
|
|
26
|
+
if (!task.authoritySnapshot) return false;
|
|
27
|
+
const authoritySnapshot = task.authoritySnapshot;
|
|
28
|
+
if (state.reason === "mechanical-rollback-requested") {
|
|
29
|
+
return authoritySnapshot.profile === "mechanical-only";
|
|
30
|
+
}
|
|
31
|
+
if (state.reason === "capability-kill-switch-requested") {
|
|
32
|
+
return state.killedCapabilities.length > 0 && state.killedCapabilities.every((capabilityId) => (
|
|
33
|
+
authoritySnapshot.capabilities.some((entry) => entry.id === capabilityId && entry.authority === "off")
|
|
34
|
+
));
|
|
35
|
+
}
|
|
36
|
+
return true;
|
|
37
|
+
}
|