@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
|
@@ -2,7 +2,6 @@ import path from "node:path";
|
|
|
2
2
|
|
|
3
3
|
import type { TaskContract } from "../../extensions/guard-types.ts";
|
|
4
4
|
import { allVerifyCommandsPassCurrentTree } from "../../extensions/task-contract-view.js";
|
|
5
|
-
import { criterionGraphGuidance } from "../../extensions/criterion-graph.js";
|
|
6
5
|
import { replayTaskCheckpoints, taskRecoveryDecision } from "../../extensions/task-journal.js";
|
|
7
6
|
import { taskDigestMigrationArchiveStatus, workingTreeSnapshot, workingTreeSnapshotHasUnavailableEvidence } from "../../extensions/task-state.js";
|
|
8
7
|
import { workingTreeEvidenceDigest } from "../../extensions/task-lifecycle.js";
|
|
@@ -10,12 +9,21 @@ import { hashEvidenceCommand } from "../../extensions/runtime-evidence.js";
|
|
|
10
9
|
import { WORKING_TREE_DIGEST_ALGORITHM, isCurrentWorkingTreeDigest, workingTreeSnapshotUsesCurrentAlgorithm } from "../../extensions/working-tree-digest.js";
|
|
11
10
|
import { handoffProjectionPath, readHandoffProjection } from "./handoff-projection.ts";
|
|
12
11
|
import { inspectTaskAuthorityResumePolicy, type AuthorityResumeDecision } from "../policy/authority-resume-policy.ts";
|
|
12
|
+
import { presentedAcceptanceCriteria, presentedCriterionGraphGuidance } from "../session/task-contract-presentation.ts";
|
|
13
|
+
import { operatorRequestCarryLines } from "../session/operator-request-carry.ts";
|
|
13
14
|
import { readTrajectoryStore } from "../trajectory/trajectory-store.ts";
|
|
14
15
|
import { findVerifierFileSnapshot, inspectVerifierStaleness, readVerifierFileSnapshots } from "../inspection/verifier-snapshot-store.ts";
|
|
15
16
|
|
|
16
17
|
export const RESUME_STATE_VERSION = "resume-v1" as const;
|
|
17
18
|
export const RESUME_CONTEXT_VERSION = "resume-context-v1" as const;
|
|
18
|
-
export const RESUME_CONTEXT_MAX_CHARS =
|
|
19
|
+
export const RESUME_CONTEXT_MAX_CHARS = 12_000;
|
|
20
|
+
// Validated task text is bounded in Unicode code points by JSON Schema. Astral
|
|
21
|
+
// characters occupy two UTF-16 code units, so the exceptional resume ceiling
|
|
22
|
+
// must cover the worst valid lossless task even though the normal target stays
|
|
23
|
+
// at 12k. This overflow is explicit and is never used for routine compaction.
|
|
24
|
+
export const RESUME_CONTEXT_LOSSLESS_MAX_CHARS = 40_000;
|
|
25
|
+
export const RESUME_CONTEXT_LOSSLESS_OVERFLOW_MARKER = `[Piagent lossless resume overflow: the ${RESUME_CONTEXT_MAX_CHARS}-character target was exceeded; complete authoritative task truth is retained.]`;
|
|
26
|
+
export const LEGACY_TASK_TRUTH_OVERFLOW_MARKER = `[Piagent legacy-task-truth-overflow: automatic continuation is paused because lossless task truth exceeds the ${RESUME_CONTEXT_LOSSLESS_MAX_CHARS}-character exceptional ceiling.]`;
|
|
19
27
|
type ResumePlanStep = {
|
|
20
28
|
id: string;
|
|
21
29
|
title: string;
|
|
@@ -153,44 +161,129 @@ function limitedLines(values: unknown[], maximumItems: number, maximumChars: num
|
|
|
153
161
|
return selected;
|
|
154
162
|
}
|
|
155
163
|
|
|
164
|
+
function losslessManualResumeContent(task: TaskContract): string {
|
|
165
|
+
return [
|
|
166
|
+
"[Piagent durable task resume]",
|
|
167
|
+
RESUME_CONTEXT_LOSSLESS_OVERFLOW_MARKER,
|
|
168
|
+
`Task: ${compact(task.taskId, 120)} (${compact(task.taskRunId, 120)})`,
|
|
169
|
+
`Goal: ${task.summary}`,
|
|
170
|
+
`Expected output: ${task.expectedOutput}`,
|
|
171
|
+
"Acceptance focus:", ...presentedAcceptanceCriteria(task).map((value, index) => `- C${index + 1}: ${value}`),
|
|
172
|
+
"Exact verifier commands:", ...task.verifyCommands.map((value, index) => `${index + 1}. ${value}`),
|
|
173
|
+
"Continue only under the current runtime-enforced task state and every durable task clause above; do not infer progress from transcript memory."
|
|
174
|
+
].join("\n");
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** Exact UTF-16 traffic size for the smallest lossless automatic legacy resume. */
|
|
178
|
+
export function legacyTaskResumeTruthChars(task: TaskContract): number {
|
|
179
|
+
return operatorRequestCarryLines(task).length > 0 ? 0 : losslessManualResumeContent(task).length;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function legacyTaskTruthOverflowsAutomaticResume(task: TaskContract): boolean {
|
|
183
|
+
const chars = legacyTaskResumeTruthChars(task);
|
|
184
|
+
return chars > RESUME_CONTEXT_LOSSLESS_MAX_CHARS;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function legacyOverflowResumeContent(task: TaskContract): string {
|
|
188
|
+
return [
|
|
189
|
+
"[Piagent durable task resume]",
|
|
190
|
+
LEGACY_TASK_TRUTH_OVERFLOW_MARKER,
|
|
191
|
+
`Task: ${compact(task.taskId, 120)} (${compact(task.taskRunId, 120)})`,
|
|
192
|
+
"The durable task remains byte-for-byte intact, but its complete acceptance truth cannot be injected safely in one automatic resume.",
|
|
193
|
+
"Do not mutate or auto-continue this task. Ask the operator to re-intake the original request as a new bounded task or provide an explicit compact handoff."
|
|
194
|
+
].join("\n");
|
|
195
|
+
}
|
|
196
|
+
|
|
156
197
|
export function buildTaskResumeContext(task: TaskContract, resume: ResumeState): {
|
|
157
198
|
customType: "piagent-runtime-task-resume";
|
|
158
199
|
content: string;
|
|
159
200
|
details: Record<string, unknown>;
|
|
160
201
|
} {
|
|
161
|
-
|
|
202
|
+
if (legacyTaskTruthOverflowsAutomaticResume(task)) {
|
|
203
|
+
return {
|
|
204
|
+
customType: "piagent-runtime-task-resume",
|
|
205
|
+
content: legacyOverflowResumeContent(task),
|
|
206
|
+
details: {
|
|
207
|
+
resumeContextVersion: RESUME_CONTEXT_VERSION, taskId: task.taskId, taskRunId: task.taskRunId,
|
|
208
|
+
phase: resume.phase, checkpointId: resume.latestCheckpoint?.checkpointId ?? null,
|
|
209
|
+
decision: "blocked", enforcementSafe: false, verifierEvidenceCurrent: resume.verifierEvidenceCurrent,
|
|
210
|
+
staleVerifierEvidence: resume.staleVerifierEvidence, losslessOverflow: true,
|
|
211
|
+
legacyTaskTruthOverflow: true, nextAction: resume.reconstruction.nextAction
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
const operatorTruth = operatorRequestCarryLines(task);
|
|
216
|
+
const presentedCriteria = presentedAcceptanceCriteria(task);
|
|
217
|
+
const criteria = operatorTruth.length > 0
|
|
218
|
+
? limitedLines(presentedCriteria, 8, 180, (value, index) => `- C${index + 1}: ${value}`)
|
|
219
|
+
: presentedCriteria.map((value, index) => `- C${index + 1}: ${value}`);
|
|
162
220
|
const scope = limitedLines(task.scope, 8, 100, (value) => `- ${value}`);
|
|
163
221
|
const plan = resume.reconstruction.plan.slice(0, 12).map((step) => (
|
|
164
222
|
`- ${step.id}: ${step.status}; ${compact(step.title, 140)}${step.dependsOn.length > 0 ? `; after=${step.dependsOn.join(",")}` : ""}`
|
|
165
223
|
));
|
|
166
224
|
if (resume.reconstruction.plan.length > 12) plan.push(`- [${resume.reconstruction.plan.length - 12} more steps retained in the Task Contract]`);
|
|
167
|
-
const verifiers =
|
|
168
|
-
const executionMap =
|
|
225
|
+
const verifiers = task.verifyCommands.map((value, index) => `${index + 1}. ${value}`);
|
|
226
|
+
const executionMap = presentedCriterionGraphGuidance(task, 8).map((line: string) => `- ${line}`);
|
|
169
227
|
const next = resume.reconstruction.nextAction;
|
|
170
228
|
const lines = [
|
|
171
229
|
"[Piagent durable task resume]",
|
|
172
230
|
"This brief is reconstructed from the current Task Contract, journal, trajectory, handoff and working tree. Durable files remain authoritative.",
|
|
173
231
|
`Task: ${compact(task.taskId, 160)} (${compact(task.taskRunId, 160)})`,
|
|
174
|
-
`Goal: ${compact(task.summary, 500)}`,
|
|
175
|
-
|
|
232
|
+
`Goal: ${operatorTruth.length > 0 ? compact(task.summary, 500) : task.summary}`,
|
|
233
|
+
...operatorTruth,
|
|
234
|
+
`Expected output: ${operatorTruth.length > 0 ? compact(task.expectedOutput, 400) : task.expectedOutput}`,
|
|
176
235
|
"Acceptance focus:", ...criteria,
|
|
177
|
-
"
|
|
236
|
+
"Initial focus (advisory):", ...scope,
|
|
237
|
+
"Initial focus guides retrieval/review and neither authorizes nor forbids mutation.",
|
|
178
238
|
"Work plan/progress:", ...plan,
|
|
179
239
|
...(executionMap.length > 0 ? ["Execution map (planning only):", ...executionMap] : []),
|
|
180
240
|
`Current phase/checkpoint: ${resume.phase ?? "unknown"} / ${resume.latestCheckpoint?.checkpointId ?? "none"}`,
|
|
181
241
|
`Verifier state: current=${resume.verifierEvidenceCurrent}; stale=${resume.staleVerifierEvidence}; tree=${resume.currentTreeDigest}`,
|
|
182
242
|
"Exact verifier commands:", ...verifiers,
|
|
183
243
|
`Next safe action: ${next.action}${next.stepId ? ` (${next.stepId})` : ""}. ${compact(next.reason, 500)}`,
|
|
184
|
-
...(next.exactCommands.length > 0 ? ["Required commands for that action:", ...
|
|
244
|
+
...(next.exactCommands.length > 0 ? ["Required commands for that action:", ...next.exactCommands.map((value, index) => `${index + 1}. ${value}`)] : []),
|
|
185
245
|
`Resume safety: decision=${resume.decision}; enforcementSafe=${resume.enforcementSafe}; handoff=${resume.handoff.exists ? resume.handoff.path : "none"}.`,
|
|
186
246
|
"Do not infer progress from transcript memory, reopen completed steps, or mutate when the next action is inspect-handoff, wait-paused, or terminal."
|
|
187
247
|
];
|
|
188
248
|
let content = lines.join("\n");
|
|
189
249
|
if (content.length > RESUME_CONTEXT_MAX_CHARS) {
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
250
|
+
const boundedOperatorTruth = operatorRequestCarryLines(task);
|
|
251
|
+
content = boundedOperatorTruth.length > 0 ? [
|
|
252
|
+
"[Piagent durable task resume]",
|
|
253
|
+
`Task: ${compact(task.taskId, 160)} (${compact(task.taskRunId, 160)})`,
|
|
254
|
+
...boundedOperatorTruth,
|
|
255
|
+
"Acceptance focus:", ...limitedLines(presentedAcceptanceCriteria(task), 8, 160, (value, index) => `- C${index + 1}: ${value}`),
|
|
256
|
+
`Current phase/checkpoint: ${resume.phase ?? "unknown"} / ${resume.latestCheckpoint?.checkpointId ?? "none"}`,
|
|
257
|
+
"Exact verifier commands:", ...verifiers,
|
|
258
|
+
`Next safe action: ${next.action}${next.stepId ? ` (${next.stepId})` : ""}. ${compact(next.reason, 400)}`,
|
|
259
|
+
`Resume safety: decision=${resume.decision}; enforcementSafe=${resume.enforcementSafe}.`,
|
|
260
|
+
"Continue from current source. Preserve every operator clause exactly; do not infer progress from transcript memory."
|
|
261
|
+
].join("\n") : losslessManualResumeContent(task);
|
|
262
|
+
if (content.length > RESUME_CONTEXT_MAX_CHARS && boundedOperatorTruth.length > 0) {
|
|
263
|
+
content = [
|
|
264
|
+
"[Piagent durable task resume]",
|
|
265
|
+
RESUME_CONTEXT_LOSSLESS_OVERFLOW_MARKER,
|
|
266
|
+
`Task: ${compact(task.taskId, 120)} (${compact(task.taskRunId, 120)})`,
|
|
267
|
+
...boundedOperatorTruth,
|
|
268
|
+
"Acceptance focus:", ...limitedLines(presentedAcceptanceCriteria(task), 12, 220, (value, index) => `- C${index + 1}: ${value}`),
|
|
269
|
+
"Exact verifier commands:", ...verifiers,
|
|
270
|
+
`Next safe action: ${next.action}${next.stepId ? ` (${next.stepId})` : ""}. ${compact(next.reason, 300)}`,
|
|
271
|
+
`Resume safety: decision=${resume.decision}; enforcementSafe=${resume.enforcementSafe}.`,
|
|
272
|
+
"Continue only from current source, the complete operator request, exact verifiers, and durable atomic acceptance facts."
|
|
273
|
+
].join("\n");
|
|
274
|
+
if (content.length > RESUME_CONTEXT_LOSSLESS_MAX_CHARS) {
|
|
275
|
+
content = [
|
|
276
|
+
"[Piagent durable task resume]",
|
|
277
|
+
RESUME_CONTEXT_LOSSLESS_OVERFLOW_MARKER,
|
|
278
|
+
`Task: ${compact(task.taskId, 120)} (${compact(task.taskRunId, 120)})`,
|
|
279
|
+
...boundedOperatorTruth,
|
|
280
|
+
"Exact verifier commands:", ...verifiers,
|
|
281
|
+
`Next safe action: ${next.action}${next.stepId ? ` (${next.stepId})` : ""}. ${compact(next.reason, 240)}`,
|
|
282
|
+
`Resume safety: decision=${resume.decision}; enforcementSafe=${resume.enforcementSafe}.`,
|
|
283
|
+
"The complete operator request above is the acceptance source of truth. Continue only from current source and these exact verifiers."
|
|
284
|
+
].join("\n");
|
|
285
|
+
}
|
|
286
|
+
}
|
|
194
287
|
}
|
|
195
288
|
return {
|
|
196
289
|
customType: "piagent-runtime-task-resume",
|
|
@@ -205,6 +298,7 @@ export function buildTaskResumeContext(task: TaskContract, resume: ResumeState):
|
|
|
205
298
|
enforcementSafe: resume.enforcementSafe,
|
|
206
299
|
verifierEvidenceCurrent: resume.verifierEvidenceCurrent,
|
|
207
300
|
staleVerifierEvidence: resume.staleVerifierEvidence,
|
|
301
|
+
losslessOverflow: content.length > RESUME_CONTEXT_MAX_CHARS,
|
|
208
302
|
nextAction: resume.reconstruction.nextAction
|
|
209
303
|
}
|
|
210
304
|
};
|
|
@@ -220,6 +314,7 @@ export function inspectTaskResumeState(
|
|
|
220
314
|
const currentTreeDigest = workingTreeEvidenceDigest(currentDigests);
|
|
221
315
|
const authorityPolicy = inspectTaskAuthorityResumePolicy(cwd, task);
|
|
222
316
|
const archive = taskDigestMigrationArchiveStatus(cwd, task);
|
|
317
|
+
const legacyTruthOverflow = task.trace.outcome === "pending" && legacyTaskTruthOverflowsAutomaticResume(task);
|
|
223
318
|
const algorithmReady = task.workingTreeDigestAlgorithm === WORKING_TREE_DIGEST_ALGORITHM
|
|
224
319
|
&& isCurrentWorkingTreeDigest(currentTreeDigest)
|
|
225
320
|
&& workingTreeSnapshotUsesCurrentAlgorithm(currentDigests)
|
|
@@ -265,8 +360,9 @@ export function inspectTaskResumeState(
|
|
|
265
360
|
if (!trajectory.enforcementSafe) warnings.push(`trajectory state is unsafe: ${trajectory.warnings[0] ?? "unknown error"}`);
|
|
266
361
|
if (!algorithmReady) warnings.push("working-tree digest algorithm/evidence is not current and proof-capable");
|
|
267
362
|
if (!archive.valid) warnings.push(archive.reason);
|
|
363
|
+
if (legacyTruthOverflow) warnings.push("legacy-task-truth-overflow requires explicit operator re-intake or compact handoff");
|
|
268
364
|
if (refreshRequired) warnings.push("legacy evidence was archived; run each exact configured verifier once against the current wt-content-v2 tree");
|
|
269
|
-
const enforcementSafe = authorityReady && !identityConflict && algorithmReady && archive.valid && journal.corruptions.length === 0 && trajectory.enforcementSafe && handoffValid;
|
|
365
|
+
const enforcementSafe = authorityReady && !identityConflict && !legacyTruthOverflow && algorithmReady && archive.valid && journal.corruptions.length === 0 && trajectory.enforcementSafe && handoffValid;
|
|
270
366
|
const recovery = refreshRequired ? { decision: "resume", reason: "Digest migration requires current exact verifier evidence." } : taskRecoveryDecision(task, journal);
|
|
271
367
|
const decision: ResumeState["decision"] = terminal
|
|
272
368
|
? "terminal"
|
|
@@ -281,6 +377,8 @@ export function inspectTaskResumeState(
|
|
|
281
377
|
: "resume";
|
|
282
378
|
const reason = terminal
|
|
283
379
|
? `Task contract is immutable after ${task.trace.outcome}.`
|
|
380
|
+
: legacyTruthOverflow
|
|
381
|
+
? "legacy-task-truth-overflow: automatic continuation is paused; ask the operator to re-intake the original request or provide a compact handoff."
|
|
284
382
|
: journal.corruptions.length > 0
|
|
285
383
|
? `task journal is corrupt: ${journal.corruptions[0]}`
|
|
286
384
|
: !authorityReady
|
|
@@ -11,12 +11,12 @@ import { safeTaskId } from "../../extensions/task-state.js";
|
|
|
11
11
|
import { isCurrentWorkingTreeDigest } from "../../extensions/working-tree-digest.js";
|
|
12
12
|
|
|
13
13
|
const HASH = /^[a-f0-9]{64}$/;
|
|
14
|
-
const GLOB = /[?*[\]{}]/;
|
|
15
14
|
const MAX_FILE_BYTES = 256 * 1024;
|
|
16
15
|
export const MAX_SEMANTIC_REPAIR_PATHS = 12;
|
|
17
16
|
export const MAX_SEMANTIC_REPAIR_REVISIONS = 2;
|
|
18
17
|
export const MAX_SEMANTIC_REPAIR_MUTATIONS = 8;
|
|
19
18
|
export const MAX_SEMANTIC_REPAIR_UNSUCCESSFUL_CALLS = 3;
|
|
19
|
+
export const BOUNDED_RECOVERY_CONFLICT_CODE = "post-verifier-model-omission";
|
|
20
20
|
|
|
21
21
|
type RepairStatus = "reserved" | "authorized" | "active" | "retry-ready" | "verifier-pending" | "passed" | "cancelled" | "locked";
|
|
22
22
|
type RepairPendingCall = {
|
|
@@ -105,13 +105,6 @@ function tokenHash(token: string): string {
|
|
|
105
105
|
return crypto.createHash("sha256").update(token).digest("hex");
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
function exactDeclaredPath(task: TaskContract, target: string): boolean {
|
|
109
|
-
return (task.scope ?? []).some((candidate) => {
|
|
110
|
-
const normalized = normalizePathCandidate(candidate);
|
|
111
|
-
return Boolean(normalized && !GLOB.test(normalized) && normalized === target);
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
|
|
115
108
|
function isTestPath(file: string): boolean {
|
|
116
109
|
return /(^|\/)(?:test|tests|spec|__tests__)(\/|$)|[._-](?:test|spec)\.[cm]?[jt]sx?$/i.test(file);
|
|
117
110
|
}
|
|
@@ -145,12 +138,13 @@ export function decideSemanticRepairHandshake(input: {
|
|
|
145
138
|
verifierCurrent: boolean;
|
|
146
139
|
}): SemanticRepairHandshakeDecision {
|
|
147
140
|
const targets = uniquePaths(input.mutationTargets), delta = new Set(uniquePaths(input.currentDeltaPaths));
|
|
141
|
+
const targetSet = new Set(targets);
|
|
148
142
|
const empty = { authorized: false, conflictCodes: [], eligibleTargets: [], eligiblePaths: [], pathConflictCodes: {} };
|
|
149
143
|
if (!input.verifierCurrent || targets.length === 0 || delta.size === 0) return empty;
|
|
150
|
-
const candidates = uniquePaths([...delta, ...
|
|
144
|
+
const candidates = uniquePaths([...delta, ...targets]);
|
|
151
145
|
const pathConflictCodes: Record<string, string[]> = {}, sourceTexts = new Map<string, string>();
|
|
152
146
|
for (const file of candidates) {
|
|
153
|
-
if (isTestPath(file) || (!delta.has(file) && !
|
|
147
|
+
if (isTestPath(file) || (!delta.has(file) && !targetSet.has(file))) continue;
|
|
154
148
|
const source = readSmallFile(input.cwd, file), conflicts = contentLinkedConflictCodes(input.task, source);
|
|
155
149
|
if (conflicts.length === 0) continue;
|
|
156
150
|
pathConflictCodes[file] = conflicts;
|
|
@@ -160,7 +154,7 @@ export function decideSemanticRepairHandshake(input: {
|
|
|
160
154
|
if (sourcePaths.length === 0) return empty;
|
|
161
155
|
const relatedTests = candidates.filter((file) => (
|
|
162
156
|
isTestPath(file)
|
|
163
|
-
&& (delta.has(file) ||
|
|
157
|
+
&& (delta.has(file) || targetSet.has(file))
|
|
164
158
|
&& sourcePaths.some((sourcePath) => acceptanceExecutableTestBinding({
|
|
165
159
|
sourceEntry: { path: sourcePath, text: sourceTexts.get(sourcePath) ?? "" },
|
|
166
160
|
testEntry: { path: file, text: readSmallFile(input.cwd, file) }
|
|
@@ -173,6 +167,47 @@ export function decideSemanticRepairHandshake(input: {
|
|
|
173
167
|
return { authorized: includesConflictingSource && targets.length === eligibleTargets.length, conflictCodes, eligibleTargets, eligiblePaths, pathConflictCodes };
|
|
174
168
|
}
|
|
175
169
|
|
|
170
|
+
/**
|
|
171
|
+
* Grant a single fail-closed recovery entry when the model notices an omission
|
|
172
|
+
* after a current exact verifier has already moved the task into verify.
|
|
173
|
+
*
|
|
174
|
+
* This is intentionally narrower than semantic repair: every target must be
|
|
175
|
+
* an actual current task delta, must have been observed in this task, and must
|
|
176
|
+
* still carry successful structured-mutation authorship for the exact task,
|
|
177
|
+
* run, and session. The caller separately enforces CAP-09/12/13 and every
|
|
178
|
+
* normal protected-path, permission, approval, and exact-target guard before
|
|
179
|
+
* persisting the reservation; task scope is advisory.
|
|
180
|
+
*/
|
|
181
|
+
export function decideBoundedRecoveryHandshake(input: {
|
|
182
|
+
task: TaskContract;
|
|
183
|
+
mutationTargets: string[];
|
|
184
|
+
currentDeltaPaths: string[];
|
|
185
|
+
observedChangedPaths: string[];
|
|
186
|
+
authoredFileDigests: Record<string, string>;
|
|
187
|
+
verifierCurrent: boolean;
|
|
188
|
+
}): SemanticRepairHandshakeDecision {
|
|
189
|
+
const targets = uniquePaths(input.mutationTargets);
|
|
190
|
+
const empty = { authorized: false, conflictCodes: [], eligibleTargets: [], eligiblePaths: [], pathConflictCodes: {} };
|
|
191
|
+
if (!input.verifierCurrent || targets.length === 0 || targets.length > MAX_SEMANTIC_REPAIR_PATHS) return empty;
|
|
192
|
+
|
|
193
|
+
const delta = new Set(uniquePaths(input.currentDeltaPaths));
|
|
194
|
+
const observed = new Set(uniquePaths(input.observedChangedPaths));
|
|
195
|
+
const authored = new Set(uniquePaths(Object.entries(input.authoredFileDigests)
|
|
196
|
+
.filter(([, digest]) => typeof digest === "string" && digest.length > 0)
|
|
197
|
+
.map(([file]) => file)));
|
|
198
|
+
const eligibleTargets = targets.filter((file) => delta.has(file) && observed.has(file) && authored.has(file));
|
|
199
|
+
if (eligibleTargets.length !== targets.length) return empty;
|
|
200
|
+
|
|
201
|
+
const pathConflictCodes = Object.fromEntries(targets.map((file) => [file, [BOUNDED_RECOVERY_CONFLICT_CODE]]));
|
|
202
|
+
return {
|
|
203
|
+
authorized: true,
|
|
204
|
+
conflictCodes: [BOUNDED_RECOVERY_CONFLICT_CODE],
|
|
205
|
+
eligibleTargets: targets,
|
|
206
|
+
eligiblePaths: targets,
|
|
207
|
+
pathConflictCodes
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
176
211
|
export function semanticRepairStatePath(cwd: string, taskRunId: string): string {
|
|
177
212
|
return path.join(cwd, ".pi", "piagent-state", "semantic-repair", `${safeTaskId(taskRunId)}.json`);
|
|
178
213
|
}
|
|
@@ -364,6 +399,7 @@ export function authorizeSemanticRepairCall(input: {
|
|
|
364
399
|
return { handled: true, allowed: true, bypassPhase: false, state: writeState(input.cwd, state) };
|
|
365
400
|
}
|
|
366
401
|
if (state.status !== "active") return { handled: true, allowed: false, bypassPhase: false, reason: `semantic repair is ${state.status}` };
|
|
402
|
+
const boundedRecovery = state.conflictCodes.includes(BOUNDED_RECOVERY_CONFLICT_CODE);
|
|
367
403
|
if (input.exactVerifier) {
|
|
368
404
|
if (state.successfulMutationsInRevision < 1) return denyActiveCall(input.cwd, state, "semantic repair verifier requires a successful mutation in this revision", input.recordedAt);
|
|
369
405
|
state.status = "verifier-pending";
|
|
@@ -372,6 +408,7 @@ export function authorizeSemanticRepairCall(input: {
|
|
|
372
408
|
return { handled: true, allowed: true, bypassPhase: false, state: writeState(input.cwd, state) };
|
|
373
409
|
}
|
|
374
410
|
if (input.projectMutation) {
|
|
411
|
+
if (boundedRecovery) return denyActiveCall(input.cwd, state, "bounded recovery permits exactly one successful mutation before the exact verifier", input.recordedAt);
|
|
375
412
|
const targets = uniquePaths(input.targetPaths);
|
|
376
413
|
if (input.targetExtractionComplete === false) return denyActiveCall(input.cwd, state, "semantic repair mutation targets are not statically complete", input.recordedAt);
|
|
377
414
|
if (targets.length === 0 || targets.some((file) => !state.eligiblePaths.includes(file))) return denyActiveCall(input.cwd, state, "semantic repair mutation is outside the exact persisted grant", input.recordedAt);
|
|
@@ -402,6 +439,7 @@ export function pendingSemanticRepairCall(cwd: string, taskRunId: string, toolCa
|
|
|
402
439
|
export function completeSemanticRepairCall(input: {
|
|
403
440
|
cwd: string; taskRunId: string; toolCallId: string; success: boolean; exitCode?: number;
|
|
404
441
|
currentDigest: string; changedPaths: string[]; retryableFailure?: boolean; correctiveFailure?: boolean; recordedAt?: string;
|
|
442
|
+
authoredChangedPaths?: string[];
|
|
405
443
|
}): { result: "unmatched" | "opened" | "recorded" | "retry" | "correction" | "passed" | "cancelled" | "locked"; state?: SemanticRepairState } {
|
|
406
444
|
const view = readSemanticRepairState(input.cwd, input.taskRunId), state = view.state;
|
|
407
445
|
if (!view.enforcementSafe || !state?.pending || state.pending.toolCallId !== input.toolCallId) return { result: "unmatched", state };
|
|
@@ -410,6 +448,7 @@ export function completeSemanticRepairCall(input: {
|
|
|
410
448
|
return { result: "locked", state: writeState(input.cwd, state) };
|
|
411
449
|
}
|
|
412
450
|
const pending = state.pending, changed = uniquePaths(input.changedPaths), treeChanged = input.currentDigest !== pending.preDigest;
|
|
451
|
+
const boundedRecovery = state.conflictCodes.includes(BOUNDED_RECOVERY_CONFLICT_CODE);
|
|
413
452
|
const now = input.recordedAt ?? new Date().toISOString();
|
|
414
453
|
if (!pending.authorized) { state.status = "locked"; state.pending = null; state.updatedAt = now; return { result: "locked", state: writeState(input.cwd, state) }; }
|
|
415
454
|
if (pending.kind === "verifier") {
|
|
@@ -418,12 +457,20 @@ export function completeSemanticRepairCall(input: {
|
|
|
418
457
|
state.failedCalls += 1;
|
|
419
458
|
if (treeChanged || unsuccessful(state) >= MAX_SEMANTIC_REPAIR_UNSUCCESSFUL_CALLS) state.status = "locked";
|
|
420
459
|
else if (input.retryableFailure && !state.transientRetryUsed) { state.transientRetryUsed = true; state.status = "retry-ready"; }
|
|
421
|
-
else if (input.correctiveFailure && state.revision < MAX_SEMANTIC_REPAIR_REVISIONS) { state.revision += 1; state.successfulMutationsInRevision = 0; state.status = "active"; }
|
|
460
|
+
else if (!boundedRecovery && input.correctiveFailure && state.revision < MAX_SEMANTIC_REPAIR_REVISIONS) { state.revision += 1; state.successfulMutationsInRevision = 0; state.status = "active"; }
|
|
422
461
|
else state.status = "locked";
|
|
423
462
|
const result = state.status === "retry-ready" ? "retry" : state.status === "active" ? "correction" : "locked";
|
|
424
463
|
return { result, state: writeState(input.cwd, state) };
|
|
425
464
|
}
|
|
426
|
-
const
|
|
465
|
+
const authoredChanged = uniquePaths(input.authoredChangedPaths ?? []);
|
|
466
|
+
const boundedTargetMismatch = boundedRecovery
|
|
467
|
+
&& (changed.length !== pending.targetPaths.length || changed.some((file, index) => pending.targetPaths[index] !== file));
|
|
468
|
+
const boundedAuthorshipMissing = boundedRecovery
|
|
469
|
+
&& (authoredChanged.length !== changed.length || changed.some((file, index) => authoredChanged[index] !== file));
|
|
470
|
+
const unexpected = changed.length === 0
|
|
471
|
+
|| changed.some((file) => !pending.targetPaths.includes(file) || !state.eligiblePaths.includes(file))
|
|
472
|
+
|| boundedTargetMismatch
|
|
473
|
+
|| boundedAuthorshipMissing;
|
|
427
474
|
if (treeChanged && (!input.success || unexpected)) { state.status = "locked"; state.pending = null; state.updatedAt = now; return { result: "locked", state: writeState(input.cwd, state) }; }
|
|
428
475
|
if (input.success && treeChanged) {
|
|
429
476
|
if (pending.opensRepair) writeSemanticRepairOrigin(input.cwd, state, input.currentDigest, now);
|
|
@@ -3,8 +3,10 @@ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
|
3
3
|
import type { TaskContract } from "../../extensions/guard-types.ts";
|
|
4
4
|
import type { TrajectorySyncResult } from "../trajectory/trajectory-runtime.ts";
|
|
5
5
|
import {
|
|
6
|
+
BOUNDED_RECOVERY_CONFLICT_CODE,
|
|
6
7
|
authorizeSemanticRepairCall,
|
|
7
8
|
completeSemanticRepairCall,
|
|
9
|
+
decideBoundedRecoveryHandshake,
|
|
8
10
|
decideSemanticRepairHandshake,
|
|
9
11
|
pendingSemanticRepairCall,
|
|
10
12
|
readSemanticRepairState,
|
|
@@ -31,6 +33,7 @@ export type SemanticRepairCompletionMetadata = {
|
|
|
31
33
|
exitCode: number;
|
|
32
34
|
currentWorkingTreeDigest: string;
|
|
33
35
|
changedPaths: string[];
|
|
36
|
+
authoredChangedPaths?: string[];
|
|
34
37
|
retryableFailure: boolean; correctiveFailure: boolean;
|
|
35
38
|
};
|
|
36
39
|
|
|
@@ -100,6 +103,52 @@ export class SemanticRepairRuntime {
|
|
|
100
103
|
return true;
|
|
101
104
|
}
|
|
102
105
|
|
|
106
|
+
prepareBoundedRecovery(input: {
|
|
107
|
+
ctx: ExtensionContext; task: TaskContract; event: ToolEvent; currentDigest: string;
|
|
108
|
+
currentDeltaPaths: string[]; observedChangedPaths: string[];
|
|
109
|
+
authoredFileDigests: Record<string, string>; targetPaths: string[]; verifierCurrent: boolean;
|
|
110
|
+
}): boolean {
|
|
111
|
+
// This path is a one-shot CAP-12 bridge, not CAP-13's multi-revision
|
|
112
|
+
// semantic-repair protocol. Any durable state, including a cancelled or
|
|
113
|
+
// interrupted reservation, consumes the task-run grant and fails closed.
|
|
114
|
+
if (readSemanticRepairState(input.ctx.cwd, input.task.taskRunId).status !== "missing"
|
|
115
|
+
|| semanticRepairStateRequired(input.ctx.cwd, input.task.taskRunId)) return false;
|
|
116
|
+
const decision = decideBoundedRecoveryHandshake({
|
|
117
|
+
task: input.task,
|
|
118
|
+
mutationTargets: input.targetPaths,
|
|
119
|
+
currentDeltaPaths: input.currentDeltaPaths,
|
|
120
|
+
observedChangedPaths: input.observedChangedPaths,
|
|
121
|
+
authoredFileDigests: input.authoredFileDigests,
|
|
122
|
+
verifierCurrent: input.verifierCurrent
|
|
123
|
+
});
|
|
124
|
+
if (!decision.authorized) return false;
|
|
125
|
+
const toolCallId = String(input.event.toolCallId);
|
|
126
|
+
const reserved = reserveSemanticRepairCall({
|
|
127
|
+
cwd: input.ctx.cwd,
|
|
128
|
+
task: input.task,
|
|
129
|
+
sessionId: input.ctx.sessionManager.getSessionId(),
|
|
130
|
+
toolCallId,
|
|
131
|
+
toolName: input.event.toolName,
|
|
132
|
+
currentDigest: input.currentDigest,
|
|
133
|
+
decision,
|
|
134
|
+
targetPaths: input.targetPaths,
|
|
135
|
+
recordedAt: this.dependencies.now()
|
|
136
|
+
});
|
|
137
|
+
if (!reserved.reserved) return false;
|
|
138
|
+
this.reservationTokens.set(
|
|
139
|
+
this.reservationKey(input.ctx.cwd, input.task.taskRunId, toolCallId),
|
|
140
|
+
reserved.reservationToken!
|
|
141
|
+
);
|
|
142
|
+
this.dependencies.trace(input.ctx, input.task, {
|
|
143
|
+
event: "bounded_recovery_repair_reserved",
|
|
144
|
+
workingTreeDigest: input.currentDigest,
|
|
145
|
+
reasonCode: BOUNDED_RECOVERY_CONFLICT_CODE,
|
|
146
|
+
eligiblePaths: decision.eligiblePaths,
|
|
147
|
+
targetCount: input.targetPaths.length
|
|
148
|
+
});
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
|
|
103
152
|
reservedCallMatches(input: {
|
|
104
153
|
cwd: string; taskRunId: string; sessionId: string; toolCallId: string;
|
|
105
154
|
toolName: string; currentDigest: string; targetPaths: string[];
|
|
@@ -131,7 +180,8 @@ export class SemanticRepairRuntime {
|
|
|
131
180
|
if (view.state.successfulMutations > 0 && !semanticRepairOriginMatches(cwd, view.state)) return "semantic repair durable origin is missing or invalid";
|
|
132
181
|
if (view.state.status === "passed") return;
|
|
133
182
|
if (view.state.successfulMutations === 0 && view.state.pending === null && !semanticRepairStateRequired(cwd, taskRunId)) return;
|
|
134
|
-
|
|
183
|
+
const label = view.state.conflictCodes.includes(BOUNDED_RECOVERY_CONFLICT_CODE) ? "bounded recovery" : "semantic repair";
|
|
184
|
+
return `${label} exact final verifier is not satisfied (${view.state.status})`;
|
|
135
185
|
}
|
|
136
186
|
|
|
137
187
|
complete(
|
|
@@ -148,13 +198,16 @@ export class SemanticRepairRuntime {
|
|
|
148
198
|
exitCode: metadata.exitCode,
|
|
149
199
|
currentDigest: metadata.currentWorkingTreeDigest,
|
|
150
200
|
changedPaths: metadata.changedPaths,
|
|
201
|
+
authoredChangedPaths: metadata.authoredChangedPaths,
|
|
151
202
|
retryableFailure: metadata.retryableFailure,
|
|
152
203
|
correctiveFailure: metadata.correctiveFailure,
|
|
153
204
|
recordedAt: this.dependencies.now()
|
|
154
205
|
});
|
|
155
206
|
if (completion.result === "unmatched") return;
|
|
156
207
|
this.dependencies.trace(ctx, task, {
|
|
157
|
-
event: completion.
|
|
208
|
+
event: completion.state?.conflictCodes.includes(BOUNDED_RECOVERY_CONFLICT_CODE)
|
|
209
|
+
? `bounded_recovery_repair_${completion.result}`
|
|
210
|
+
: completion.result === "opened" ? "semantic_contradiction_repair_opened" : `semantic_repair_${completion.result}`,
|
|
158
211
|
toolName: event.toolName,
|
|
159
212
|
revision: completion.state?.revision,
|
|
160
213
|
successfulMutations: completion.state?.successfulMutations,
|