@blxzer/pactile 0.6.0-beta.2 → 0.6.0-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -84
- package/README.zh-CN.md +90 -81
- package/dist/bin/compat-warning.d.ts +2 -0
- package/dist/bin/compat-warning.d.ts.map +1 -0
- package/dist/bin/compat-warning.js +8 -0
- package/dist/bin/compat-warning.js.map +1 -0
- package/dist/bin/cstl.d.ts +3 -0
- package/dist/bin/cstl.d.ts.map +1 -0
- package/{bin → dist/bin}/cstl.js +2 -3
- package/dist/bin/cstl.js.map +1 -0
- package/dist/bin/pactile.d.ts +3 -0
- package/dist/bin/pactile.d.ts.map +1 -0
- package/dist/bin/pactile.js +4 -0
- package/dist/bin/pactile.js.map +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +77 -17
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/capability.d.ts +3 -0
- package/dist/commands/capability.d.ts.map +1 -0
- package/dist/commands/capability.js +129 -0
- package/dist/commands/capability.js.map +1 -0
- package/dist/commands/codex.d.ts.map +1 -1
- package/dist/commands/codex.js +54 -6
- package/dist/commands/codex.js.map +1 -1
- package/dist/commands/context.d.ts +2 -0
- package/dist/commands/context.d.ts.map +1 -1
- package/dist/commands/context.js +367 -41
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/detach.d.ts.map +1 -1
- package/dist/commands/detach.js +5 -4
- package/dist/commands/detach.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +615 -259
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/legacy-task.d.ts +3 -0
- package/dist/commands/legacy-task.d.ts.map +1 -0
- package/dist/commands/legacy-task.js +295 -0
- package/dist/commands/legacy-task.js.map +1 -0
- package/dist/commands/pi.d.ts +3 -2
- package/dist/commands/pi.d.ts.map +1 -1
- package/dist/commands/pi.js +333 -10
- package/dist/commands/pi.js.map +1 -1
- package/dist/commands/session.d.ts.map +1 -1
- package/dist/commands/session.js +148 -24
- package/dist/commands/session.js.map +1 -1
- package/dist/commands/task-schedule.d.ts +17 -0
- package/dist/commands/task-schedule.d.ts.map +1 -0
- package/dist/commands/task-schedule.js +395 -0
- package/dist/commands/task-schedule.js.map +1 -0
- package/dist/commands/task-verify-plan.d.ts +3 -0
- package/dist/commands/task-verify-plan.d.ts.map +1 -0
- package/dist/commands/task-verify-plan.js +503 -0
- package/dist/commands/task-verify-plan.js.map +1 -0
- package/dist/commands/task-worktree-close.d.ts +4 -0
- package/dist/commands/task-worktree-close.d.ts.map +1 -0
- package/dist/commands/task-worktree-close.js +81 -0
- package/dist/commands/task-worktree-close.js.map +1 -0
- package/dist/commands/task.d.ts +3 -0
- package/dist/commands/task.d.ts.map +1 -1
- package/dist/commands/task.js +1373 -184
- package/dist/commands/task.js.map +1 -1
- package/dist/commands/tile-selection.d.ts +3 -0
- package/dist/commands/tile-selection.d.ts.map +1 -0
- package/dist/commands/tile-selection.js +174 -0
- package/dist/commands/tile-selection.js.map +1 -0
- package/dist/commands/update.d.ts +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +80 -55
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/worktree.d.ts +4 -0
- package/dist/commands/worktree.d.ts.map +1 -0
- package/dist/commands/worktree.js +198 -0
- package/dist/commands/worktree.js.map +1 -0
- package/dist/configurators/shared.js +1 -1
- package/dist/configurators/shared.js.map +1 -1
- package/dist/core/compat/environment.d.ts +0 -1
- package/dist/core/compat/environment.d.ts.map +1 -1
- package/dist/core/compat/environment.js +1 -9
- package/dist/core/compat/environment.js.map +1 -1
- package/dist/core/pactile/bounded-capabilities.d.ts +127 -0
- package/dist/core/pactile/bounded-capabilities.d.ts.map +1 -0
- package/dist/core/pactile/bounded-capabilities.js +188 -0
- package/dist/core/pactile/bounded-capabilities.js.map +1 -0
- package/dist/core/pactile/index.d.ts +1 -0
- package/dist/core/pactile/index.d.ts.map +1 -1
- package/dist/core/pactile/index.js +1 -0
- package/dist/core/pactile/index.js.map +1 -1
- package/dist/core/task/index.d.ts +4 -2
- package/dist/core/task/index.d.ts.map +1 -1
- package/dist/core/task/index.js +2 -1
- package/dist/core/task/index.js.map +1 -1
- package/dist/core/task/kernel-cli.d.ts +14 -1
- package/dist/core/task/kernel-cli.d.ts.map +1 -1
- package/dist/core/task/kernel-cli.js +128 -0
- package/dist/core/task/kernel-cli.js.map +1 -1
- package/dist/core/task/kernel-contract.d.ts +6 -3
- package/dist/core/task/kernel-contract.d.ts.map +1 -1
- package/dist/core/task/kernel-contract.js +71 -23
- package/dist/core/task/kernel-contract.js.map +1 -1
- package/dist/core/task/kernel-store.d.ts +6 -0
- package/dist/core/task/kernel-store.d.ts.map +1 -1
- package/dist/core/task/kernel-store.js +54 -2
- package/dist/core/task/kernel-store.js.map +1 -1
- package/dist/core/task/legacy-task-migration-reader.d.ts +137 -0
- package/dist/core/task/legacy-task-migration-reader.d.ts.map +1 -0
- package/dist/core/task/legacy-task-migration-reader.js +512 -0
- package/dist/core/task/legacy-task-migration-reader.js.map +1 -0
- package/dist/core/task/legacy-task-migration.d.ts +105 -0
- package/dist/core/task/legacy-task-migration.d.ts.map +1 -0
- package/dist/core/task/legacy-task-migration.js +562 -0
- package/dist/core/task/legacy-task-migration.js.map +1 -0
- package/dist/core/task/legacy-task-overlay-durable-io.d.ts +18 -0
- package/dist/core/task/legacy-task-overlay-durable-io.d.ts.map +1 -0
- package/dist/core/task/legacy-task-overlay-durable-io.js +66 -0
- package/dist/core/task/legacy-task-overlay-durable-io.js.map +1 -0
- package/dist/core/task/legacy-task-reconciliation-reader.d.ts +26 -0
- package/dist/core/task/legacy-task-reconciliation-reader.d.ts.map +1 -0
- package/dist/core/task/legacy-task-reconciliation-reader.js +324 -0
- package/dist/core/task/legacy-task-reconciliation-reader.js.map +1 -0
- package/dist/core/task/legacy-task-v2-import.d.ts +67 -0
- package/dist/core/task/legacy-task-v2-import.d.ts.map +1 -0
- package/dist/core/task/legacy-task-v2-import.js +1046 -0
- package/dist/core/task/legacy-task-v2-import.js.map +1 -0
- package/dist/core/task/project-file-observer.d.ts +46 -0
- package/dist/core/task/project-file-observer.d.ts.map +1 -0
- package/dist/core/task/project-file-observer.js +618 -0
- package/dist/core/task/project-file-observer.js.map +1 -0
- package/dist/core/task/start-authority.d.ts +14 -0
- package/dist/core/task/start-authority.d.ts.map +1 -0
- package/dist/core/task/start-authority.js +146 -0
- package/dist/core/task/start-authority.js.map +1 -0
- package/dist/core/task/strategy.d.ts +24 -0
- package/dist/core/task/strategy.d.ts.map +1 -0
- package/dist/core/task/strategy.js +136 -0
- package/dist/core/task/strategy.js.map +1 -0
- package/dist/core/task/task-candidate-observer.d.ts +111 -0
- package/dist/core/task/task-candidate-observer.d.ts.map +1 -0
- package/dist/core/task/task-candidate-observer.js +762 -0
- package/dist/core/task/task-candidate-observer.js.map +1 -0
- package/dist/core/task/task-kernel-close.d.ts +5 -0
- package/dist/core/task/task-kernel-close.d.ts.map +1 -0
- package/dist/core/task/task-kernel-close.js +367 -0
- package/dist/core/task/task-kernel-close.js.map +1 -0
- package/dist/core/task/task-kernel-dependencies.d.ts +3 -0
- package/dist/core/task/task-kernel-dependencies.d.ts.map +1 -0
- package/dist/core/task/task-kernel-dependencies.js +24 -0
- package/dist/core/task/task-kernel-dependencies.js.map +1 -0
- package/dist/core/task/task-kernel-host.d.ts +5 -0
- package/dist/core/task/task-kernel-host.d.ts.map +1 -0
- package/dist/core/task/task-kernel-host.js +136 -0
- package/dist/core/task/task-kernel-host.js.map +1 -0
- package/dist/core/task/task-kernel-paths.d.ts +19 -0
- package/dist/core/task/task-kernel-paths.d.ts.map +1 -0
- package/dist/core/task/task-kernel-paths.js +237 -0
- package/dist/core/task/task-kernel-paths.js.map +1 -0
- package/dist/core/task/task-kernel-reviews.d.ts +3 -0
- package/dist/core/task/task-kernel-reviews.d.ts.map +1 -0
- package/dist/core/task/task-kernel-reviews.js +100 -0
- package/dist/core/task/task-kernel-reviews.js.map +1 -0
- package/dist/core/task/task-kernel-runs.d.ts +5 -0
- package/dist/core/task/task-kernel-runs.d.ts.map +1 -0
- package/dist/core/task/task-kernel-runs.js +196 -0
- package/dist/core/task/task-kernel-runs.js.map +1 -0
- package/dist/core/task/task-kernel-schema.d.ts +29 -0
- package/dist/core/task/task-kernel-schema.d.ts.map +1 -0
- package/dist/core/task/task-kernel-schema.js +1169 -0
- package/dist/core/task/task-kernel-schema.js.map +1 -0
- package/dist/core/task/task-kernel-store-v2.d.ts +33 -0
- package/dist/core/task/task-kernel-store-v2.d.ts.map +1 -0
- package/dist/core/task/task-kernel-store-v2.js +574 -0
- package/dist/core/task/task-kernel-store-v2.js.map +1 -0
- package/dist/core/task/task-kernel-types.d.ts +585 -0
- package/dist/core/task/task-kernel-types.d.ts.map +1 -0
- package/dist/core/task/task-kernel-types.js +31 -0
- package/dist/core/task/task-kernel-types.js.map +1 -0
- package/dist/core/task/task-kernel-workspaces.d.ts +10 -0
- package/dist/core/task/task-kernel-workspaces.d.ts.map +1 -0
- package/dist/core/task/task-kernel-workspaces.js +250 -0
- package/dist/core/task/task-kernel-workspaces.js.map +1 -0
- package/dist/core/task/task-kernel-write-set.d.ts +3 -0
- package/dist/core/task/task-kernel-write-set.d.ts.map +1 -0
- package/dist/core/task/task-kernel-write-set.js +12 -0
- package/dist/core/task/task-kernel-write-set.js.map +1 -0
- package/dist/core/task/task-kernel.d.ts +19 -0
- package/dist/core/task/task-kernel.d.ts.map +1 -0
- package/dist/core/task/task-kernel.js +17 -0
- package/dist/core/task/task-kernel.js.map +1 -0
- package/dist/core/task/task-pull-request-observer.d.ts +25 -0
- package/dist/core/task/task-pull-request-observer.d.ts.map +1 -0
- package/dist/core/task/task-pull-request-observer.js +187 -0
- package/dist/core/task/task-pull-request-observer.js.map +1 -0
- package/dist/core/task/task-review-evidence.d.ts +27 -0
- package/dist/core/task/task-review-evidence.d.ts.map +1 -0
- package/dist/core/task/task-review-evidence.js +383 -0
- package/dist/core/task/task-review-evidence.js.map +1 -0
- package/dist/migrations/index.d.ts +1 -1
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/migrations/index.js +5 -3
- package/dist/migrations/index.js.map +1 -1
- package/dist/migrations/manifests/0.6.0-beta.3.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.4.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.5.json +9 -0
- package/dist/pactile/artifacts/documents.d.ts +72 -0
- package/dist/pactile/artifacts/documents.d.ts.map +1 -0
- package/dist/pactile/artifacts/documents.js +406 -0
- package/dist/pactile/artifacts/documents.js.map +1 -0
- package/dist/pactile/artifacts/index.d.ts +6 -0
- package/dist/pactile/artifacts/index.d.ts.map +1 -0
- package/dist/pactile/artifacts/index.js +6 -0
- package/dist/pactile/artifacts/index.js.map +1 -0
- package/dist/pactile/artifacts/projection.d.ts +50 -0
- package/dist/pactile/artifacts/projection.d.ts.map +1 -0
- package/dist/pactile/artifacts/projection.js +139 -0
- package/dist/pactile/artifacts/projection.js.map +1 -0
- package/dist/pactile/artifacts/reader.d.ts +20 -0
- package/dist/pactile/artifacts/reader.d.ts.map +1 -0
- package/dist/pactile/artifacts/reader.js +559 -0
- package/dist/pactile/artifacts/reader.js.map +1 -0
- package/dist/pactile/artifacts/schema.d.ts +4 -0
- package/dist/pactile/artifacts/schema.d.ts.map +1 -0
- package/dist/pactile/artifacts/schema.js +217 -0
- package/dist/pactile/artifacts/schema.js.map +1 -0
- package/dist/pactile/artifacts/types.d.ts +66 -0
- package/dist/pactile/artifacts/types.d.ts.map +1 -0
- package/dist/pactile/artifacts/types.js +45 -0
- package/dist/pactile/artifacts/types.js.map +1 -0
- package/dist/pactile/capabilities/claim.d.ts +3 -0
- package/dist/pactile/capabilities/claim.d.ts.map +1 -0
- package/dist/pactile/capabilities/claim.js +97 -0
- package/dist/pactile/capabilities/claim.js.map +1 -0
- package/dist/pactile/capabilities/command.d.ts +6 -0
- package/dist/pactile/capabilities/command.d.ts.map +1 -0
- package/dist/pactile/capabilities/command.js +253 -0
- package/dist/pactile/capabilities/command.js.map +1 -0
- package/dist/pactile/capabilities/discover.d.ts +6 -0
- package/dist/pactile/capabilities/discover.d.ts.map +1 -0
- package/dist/pactile/capabilities/discover.js +48 -0
- package/dist/pactile/capabilities/discover.js.map +1 -0
- package/dist/pactile/capabilities/node.d.ts +15 -0
- package/dist/pactile/capabilities/node.d.ts.map +1 -0
- package/dist/pactile/capabilities/node.js +292 -0
- package/dist/pactile/capabilities/node.js.map +1 -0
- package/dist/pactile/capabilities/read.d.ts +6 -0
- package/dist/pactile/capabilities/read.d.ts.map +1 -0
- package/dist/pactile/capabilities/read.js +100 -0
- package/dist/pactile/capabilities/read.js.map +1 -0
- package/dist/pactile/capabilities/receipts.d.ts +17 -0
- package/dist/pactile/capabilities/receipts.d.ts.map +1 -0
- package/dist/pactile/capabilities/receipts.js +90 -0
- package/dist/pactile/capabilities/receipts.js.map +1 -0
- package/dist/pactile/capabilities/search.d.ts +6 -0
- package/dist/pactile/capabilities/search.d.ts.map +1 -0
- package/dist/pactile/capabilities/search.js +181 -0
- package/dist/pactile/capabilities/search.js.map +1 -0
- package/dist/pactile/capabilities/shared.d.ts +29 -0
- package/dist/pactile/capabilities/shared.d.ts.map +1 -0
- package/dist/pactile/capabilities/shared.js +134 -0
- package/dist/pactile/capabilities/shared.js.map +1 -0
- package/dist/pactile/capabilities/workspace.d.ts +19 -0
- package/dist/pactile/capabilities/workspace.d.ts.map +1 -0
- package/dist/pactile/capabilities/workspace.js +273 -0
- package/dist/pactile/capabilities/workspace.js.map +1 -0
- package/dist/pactile/codex/bridge.d.ts +105 -2
- package/dist/pactile/codex/bridge.d.ts.map +1 -1
- package/dist/pactile/codex/bridge.js +1266 -92
- package/dist/pactile/codex/bridge.js.map +1 -1
- package/dist/pactile/codex/dispatch-stop-proof.d.ts +43 -0
- package/dist/pactile/codex/dispatch-stop-proof.d.ts.map +1 -0
- package/dist/pactile/codex/dispatch-stop-proof.js +177 -0
- package/dist/pactile/codex/dispatch-stop-proof.js.map +1 -0
- package/dist/pactile/codex/host-stop.d.ts +47 -0
- package/dist/pactile/codex/host-stop.d.ts.map +1 -0
- package/dist/pactile/codex/host-stop.js +344 -0
- package/dist/pactile/codex/host-stop.js.map +1 -0
- package/dist/pactile/coordination/index.d.ts +4 -0
- package/dist/pactile/coordination/index.d.ts.map +1 -0
- package/dist/pactile/coordination/index.js +4 -0
- package/dist/pactile/coordination/index.js.map +1 -0
- package/dist/pactile/coordination/journal.d.ts +10 -0
- package/dist/pactile/coordination/journal.d.ts.map +1 -0
- package/dist/pactile/coordination/journal.js +255 -0
- package/dist/pactile/coordination/journal.js.map +1 -0
- package/dist/pactile/coordination/kernel-resume.d.ts +8 -0
- package/dist/pactile/coordination/kernel-resume.d.ts.map +1 -0
- package/dist/pactile/coordination/kernel-resume.js +87 -0
- package/dist/pactile/coordination/kernel-resume.js.map +1 -0
- package/dist/pactile/coordination/model.d.ts +553 -0
- package/dist/pactile/coordination/model.d.ts.map +1 -0
- package/dist/pactile/coordination/model.js +239 -0
- package/dist/pactile/coordination/model.js.map +1 -0
- package/dist/pactile/coordination/state.d.ts +46 -0
- package/dist/pactile/coordination/state.d.ts.map +1 -0
- package/dist/pactile/coordination/state.js +301 -0
- package/dist/pactile/coordination/state.js.map +1 -0
- package/dist/pactile/coordination/store.d.ts +94 -0
- package/dist/pactile/coordination/store.d.ts.map +1 -0
- package/dist/pactile/coordination/store.js +396 -0
- package/dist/pactile/coordination/store.js.map +1 -0
- package/dist/pactile/index.d.ts +7 -1
- package/dist/pactile/index.d.ts.map +1 -1
- package/dist/pactile/index.js +7 -1
- package/dist/pactile/index.js.map +1 -1
- package/dist/pactile/jev/contracts.d.ts +104 -0
- package/dist/pactile/jev/contracts.d.ts.map +1 -0
- package/dist/pactile/jev/contracts.js +53 -0
- package/dist/pactile/jev/contracts.js.map +1 -0
- package/dist/pactile/jev/decision.d.ts +58 -0
- package/dist/pactile/jev/decision.d.ts.map +1 -0
- package/dist/pactile/jev/decision.js +149 -0
- package/dist/pactile/jev/decision.js.map +1 -0
- package/dist/pactile/jev/http.d.ts +25 -0
- package/dist/pactile/jev/http.d.ts.map +1 -0
- package/dist/pactile/jev/http.js +234 -0
- package/dist/pactile/jev/http.js.map +1 -0
- package/dist/pactile/jev/index.d.ts +8 -0
- package/dist/pactile/jev/index.d.ts.map +1 -0
- package/dist/pactile/jev/index.js +4 -0
- package/dist/pactile/jev/index.js.map +1 -0
- package/dist/pactile/jev/outbound.d.ts +15 -0
- package/dist/pactile/jev/outbound.d.ts.map +1 -0
- package/dist/pactile/jev/outbound.js +264 -0
- package/dist/pactile/jev/outbound.js.map +1 -0
- package/dist/pactile/jev/project-policy.d.ts +10 -0
- package/dist/pactile/jev/project-policy.d.ts.map +1 -0
- package/dist/pactile/jev/project-policy.js +53 -0
- package/dist/pactile/jev/project-policy.js.map +1 -0
- package/dist/pactile/jev/response.d.ts +17 -0
- package/dist/pactile/jev/response.d.ts.map +1 -0
- package/dist/pactile/jev/response.js +280 -0
- package/dist/pactile/jev/response.js.map +1 -0
- package/dist/pactile/jev/scheduler-advice.d.ts +92 -0
- package/dist/pactile/jev/scheduler-advice.d.ts.map +1 -0
- package/dist/pactile/jev/scheduler-advice.js +323 -0
- package/dist/pactile/jev/scheduler-advice.js.map +1 -0
- package/dist/pactile/jev/transport.d.ts +6 -0
- package/dist/pactile/jev/transport.d.ts.map +1 -0
- package/dist/pactile/jev/transport.js +87 -0
- package/dist/pactile/jev/transport.js.map +1 -0
- package/dist/pactile/lifecycle/deferred-live.d.ts +17 -0
- package/dist/pactile/lifecycle/deferred-live.d.ts.map +1 -0
- package/dist/pactile/lifecycle/deferred-live.js +173 -0
- package/dist/pactile/lifecycle/deferred-live.js.map +1 -0
- package/dist/pactile/lifecycle/legacy-import.d.ts +1 -1
- package/dist/pactile/lifecycle/legacy-import.js +3 -3
- package/dist/pactile/lifecycle/legacy-import.js.map +1 -1
- package/dist/pactile/lifecycle/project-files.d.ts.map +1 -1
- package/dist/pactile/lifecycle/project-files.js +1 -0
- package/dist/pactile/lifecycle/project-files.js.map +1 -1
- package/dist/pactile/migration/legacy-task-batch-generation.d.ts +23 -0
- package/dist/pactile/migration/legacy-task-batch-generation.d.ts.map +1 -0
- package/dist/pactile/migration/legacy-task-batch-generation.js +102 -0
- package/dist/pactile/migration/legacy-task-batch-generation.js.map +1 -0
- package/dist/pactile/migration/legacy-task-batch-io.d.ts +9 -0
- package/dist/pactile/migration/legacy-task-batch-io.d.ts.map +1 -0
- package/dist/pactile/migration/legacy-task-batch-io.js +208 -0
- package/dist/pactile/migration/legacy-task-batch-io.js.map +1 -0
- package/dist/pactile/migration/legacy-task-batch-journal.d.ts +6 -0
- package/dist/pactile/migration/legacy-task-batch-journal.d.ts.map +1 -0
- package/dist/pactile/migration/legacy-task-batch-journal.js +121 -0
- package/dist/pactile/migration/legacy-task-batch-journal.js.map +1 -0
- package/dist/pactile/migration/legacy-task-batch-orchestrator.d.ts +18 -0
- package/dist/pactile/migration/legacy-task-batch-orchestrator.d.ts.map +1 -0
- package/dist/pactile/migration/legacy-task-batch-orchestrator.js +634 -0
- package/dist/pactile/migration/legacy-task-batch-orchestrator.js.map +1 -0
- package/dist/pactile/migration/legacy-task-batch-source.d.ts +8 -0
- package/dist/pactile/migration/legacy-task-batch-source.d.ts.map +1 -0
- package/dist/pactile/migration/legacy-task-batch-source.js +158 -0
- package/dist/pactile/migration/legacy-task-batch-source.js.map +1 -0
- package/dist/pactile/migration/legacy-task-batch-types.d.ts +127 -0
- package/dist/pactile/migration/legacy-task-batch-types.d.ts.map +1 -0
- package/dist/pactile/migration/legacy-task-batch-types.js +127 -0
- package/dist/pactile/migration/legacy-task-batch-types.js.map +1 -0
- package/dist/pactile/migration/legacy-task-batch.d.ts +3 -0
- package/dist/pactile/migration/legacy-task-batch.d.ts.map +1 -0
- package/dist/pactile/migration/legacy-task-batch.js +2 -0
- package/dist/pactile/migration/legacy-task-batch.js.map +1 -0
- package/dist/pactile/migration/legacy-task-held-source-reader.d.ts +27 -0
- package/dist/pactile/migration/legacy-task-held-source-reader.d.ts.map +1 -0
- package/dist/pactile/migration/legacy-task-held-source-reader.js +261 -0
- package/dist/pactile/migration/legacy-task-held-source-reader.js.map +1 -0
- package/dist/pactile/migration/legacy-task-reconciliation.d.ts +46 -0
- package/dist/pactile/migration/legacy-task-reconciliation.d.ts.map +1 -0
- package/dist/pactile/migration/legacy-task-reconciliation.js +589 -0
- package/dist/pactile/migration/legacy-task-reconciliation.js.map +1 -0
- package/dist/pactile/migration/legacy-task-update.d.ts +19 -0
- package/dist/pactile/migration/legacy-task-update.d.ts.map +1 -0
- package/dist/pactile/migration/legacy-task-update.js +164 -0
- package/dist/pactile/migration/legacy-task-update.js.map +1 -0
- package/dist/pactile/parallel/batch.d.ts +21 -2
- package/dist/pactile/parallel/batch.d.ts.map +1 -1
- package/dist/pactile/parallel/batch.js +271 -50
- package/dist/pactile/parallel/batch.js.map +1 -1
- package/dist/pactile/parallel/policy.d.ts +1 -1
- package/dist/pactile/parallel/policy.d.ts.map +1 -1
- package/dist/pactile/parallel/policy.js +130 -102
- package/dist/pactile/parallel/policy.js.map +1 -1
- package/dist/pactile/pi/bridge.d.ts +139 -2
- package/dist/pactile/pi/bridge.d.ts.map +1 -1
- package/dist/pactile/pi/bridge.js +1317 -106
- package/dist/pactile/pi/bridge.js.map +1 -1
- package/dist/pactile/pi/rpc.d.ts +23 -4
- package/dist/pactile/pi/rpc.d.ts.map +1 -1
- package/dist/pactile/pi/rpc.js +243 -42
- package/dist/pactile/pi/rpc.js.map +1 -1
- package/dist/pactile/pi/v2-dispatch.d.ts +113 -0
- package/dist/pactile/pi/v2-dispatch.d.ts.map +1 -0
- package/dist/pactile/pi/v2-dispatch.js +919 -0
- package/dist/pactile/pi/v2-dispatch.js.map +1 -0
- package/dist/pactile/projection/planner.js +1 -1
- package/dist/pactile/projection/planner.js.map +1 -1
- package/dist/pactile/registry.d.ts +63 -1
- package/dist/pactile/registry.d.ts.map +1 -1
- package/dist/pactile/registry.js +633 -4
- package/dist/pactile/registry.js.map +1 -1
- package/dist/pactile/retrieval/index.d.ts +1 -0
- package/dist/pactile/retrieval/index.d.ts.map +1 -1
- package/dist/pactile/retrieval/index.js +1 -0
- package/dist/pactile/retrieval/index.js.map +1 -1
- package/dist/pactile/retrieval/jev-planning.d.ts +28 -0
- package/dist/pactile/retrieval/jev-planning.d.ts.map +1 -0
- package/dist/pactile/retrieval/jev-planning.js +96 -0
- package/dist/pactile/retrieval/jev-planning.js.map +1 -0
- package/dist/pactile/review/contract.d.ts +232 -0
- package/dist/pactile/review/contract.d.ts.map +1 -0
- package/dist/pactile/review/contract.js +695 -0
- package/dist/pactile/review/contract.js.map +1 -0
- package/dist/pactile/review/escalation-artifacts.d.ts +41 -0
- package/dist/pactile/review/escalation-artifacts.d.ts.map +1 -0
- package/dist/pactile/review/escalation-artifacts.js +415 -0
- package/dist/pactile/review/escalation-artifacts.js.map +1 -0
- package/dist/pactile/review/escalation-contract.d.ts +115 -0
- package/dist/pactile/review/escalation-contract.d.ts.map +1 -0
- package/dist/pactile/review/escalation-contract.js +151 -0
- package/dist/pactile/review/escalation-contract.js.map +1 -0
- package/dist/pactile/review/escalation-finalize.d.ts +14 -0
- package/dist/pactile/review/escalation-finalize.d.ts.map +1 -0
- package/dist/pactile/review/escalation-finalize.js +319 -0
- package/dist/pactile/review/escalation-finalize.js.map +1 -0
- package/dist/pactile/review/escalation-reply.d.ts +10 -0
- package/dist/pactile/review/escalation-reply.d.ts.map +1 -0
- package/dist/pactile/review/escalation-reply.js +61 -0
- package/dist/pactile/review/escalation-reply.js.map +1 -0
- package/dist/pactile/review/escalation-transport.d.ts +32 -0
- package/dist/pactile/review/escalation-transport.d.ts.map +1 -0
- package/dist/pactile/review/escalation-transport.js +216 -0
- package/dist/pactile/review/escalation-transport.js.map +1 -0
- package/dist/pactile/review/escalation.d.ts +7 -0
- package/dist/pactile/review/escalation.d.ts.map +1 -0
- package/dist/pactile/review/escalation.js +6 -0
- package/dist/pactile/review/escalation.js.map +1 -0
- package/dist/pactile/review/evidence.d.ts +25 -0
- package/dist/pactile/review/evidence.d.ts.map +1 -0
- package/dist/pactile/review/evidence.js +58 -0
- package/dist/pactile/review/evidence.js.map +1 -0
- package/dist/pactile/review/jev-escalation-advice.d.ts +77 -0
- package/dist/pactile/review/jev-escalation-advice.d.ts.map +1 -0
- package/dist/pactile/review/jev-escalation-advice.js +358 -0
- package/dist/pactile/review/jev-escalation-advice.js.map +1 -0
- package/dist/pactile/scheduler/index.d.ts +6 -0
- package/dist/pactile/scheduler/index.d.ts.map +1 -0
- package/dist/pactile/scheduler/index.js +5 -0
- package/dist/pactile/scheduler/index.js.map +1 -0
- package/dist/pactile/scheduler/project-lease-store.d.ts +35 -0
- package/dist/pactile/scheduler/project-lease-store.d.ts.map +1 -0
- package/dist/pactile/scheduler/project-lease-store.js +189 -0
- package/dist/pactile/scheduler/project-lease-store.js.map +1 -0
- package/dist/pactile/scheduler/scheduler.d.ts +91 -0
- package/dist/pactile/scheduler/scheduler.d.ts.map +1 -0
- package/dist/pactile/scheduler/scheduler.js +589 -0
- package/dist/pactile/scheduler/scheduler.js.map +1 -0
- package/dist/pactile/scheduler/task-kernel-admission.d.ts +173 -0
- package/dist/pactile/scheduler/task-kernel-admission.d.ts.map +1 -0
- package/dist/pactile/scheduler/task-kernel-admission.js +1560 -0
- package/dist/pactile/scheduler/task-kernel-admission.js.map +1 -0
- package/dist/pactile/scheduler/task-kernel-wave-dispatch.d.ts +184 -0
- package/dist/pactile/scheduler/task-kernel-wave-dispatch.d.ts.map +1 -0
- package/dist/pactile/scheduler/task-kernel-wave-dispatch.js +952 -0
- package/dist/pactile/scheduler/task-kernel-wave-dispatch.js.map +1 -0
- package/dist/pactile/scheduler/task-map-scheduler.d.ts +116 -0
- package/dist/pactile/scheduler/task-map-scheduler.d.ts.map +1 -0
- package/dist/pactile/scheduler/task-map-scheduler.js +1448 -0
- package/dist/pactile/scheduler/task-map-scheduler.js.map +1 -0
- package/dist/pactile/scheduler/task-run-dispatch-state.d.ts +7 -0
- package/dist/pactile/scheduler/task-run-dispatch-state.d.ts.map +1 -0
- package/dist/pactile/scheduler/task-run-dispatch-state.js +69 -0
- package/dist/pactile/scheduler/task-run-dispatch-state.js.map +1 -0
- package/dist/pactile/task/authorization.d.ts +3 -0
- package/dist/pactile/task/authorization.d.ts.map +1 -0
- package/dist/pactile/task/authorization.js +43 -0
- package/dist/pactile/task/authorization.js.map +1 -0
- package/dist/pactile/task/creation.d.ts +22 -0
- package/dist/pactile/task/creation.d.ts.map +1 -0
- package/dist/pactile/task/creation.js +95 -0
- package/dist/pactile/task/creation.js.map +1 -0
- package/dist/pactile/task/guards.d.ts +1 -4
- package/dist/pactile/task/guards.d.ts.map +1 -1
- package/dist/pactile/task/guards.js +5 -57
- package/dist/pactile/task/guards.js.map +1 -1
- package/dist/pactile/task/session-jev-receipt.d.ts +58 -0
- package/dist/pactile/task/session-jev-receipt.d.ts.map +1 -0
- package/dist/pactile/task/session-jev-receipt.js +375 -0
- package/dist/pactile/task/session-jev-receipt.js.map +1 -0
- package/dist/pactile/task/session-jev.d.ts +7 -0
- package/dist/pactile/task/session-jev.d.ts.map +1 -0
- package/dist/pactile/task/session-jev.js +217 -0
- package/dist/pactile/task/session-jev.js.map +1 -0
- package/dist/pactile/task/session-pack.d.ts.map +1 -1
- package/dist/pactile/task/session-pack.js +155 -16
- package/dist/pactile/task/session-pack.js.map +1 -1
- package/dist/pactile/task/session-retrieval-jev-receipt.d.ts +49 -0
- package/dist/pactile/task/session-retrieval-jev-receipt.d.ts.map +1 -0
- package/dist/pactile/task/session-retrieval-jev-receipt.js +97 -0
- package/dist/pactile/task/session-retrieval-jev-receipt.js.map +1 -0
- package/dist/pactile/task/session-retrieval-jev.d.ts +8 -0
- package/dist/pactile/task/session-retrieval-jev.d.ts.map +1 -0
- package/dist/pactile/task/session-retrieval-jev.js +352 -0
- package/dist/pactile/task/session-retrieval-jev.js.map +1 -0
- package/dist/pactile/task/strategy.d.ts +1 -23
- package/dist/pactile/task/strategy.d.ts.map +1 -1
- package/dist/pactile/task/strategy.js +2 -135
- package/dist/pactile/task/strategy.js.map +1 -1
- package/dist/pactile/tiles/jev-selection.d.ts +44 -0
- package/dist/pactile/tiles/jev-selection.d.ts.map +1 -0
- package/dist/pactile/tiles/jev-selection.js +210 -0
- package/dist/pactile/tiles/jev-selection.js.map +1 -0
- package/dist/pactile/tiles/safe-yaml.d.ts +5 -1
- package/dist/pactile/tiles/safe-yaml.d.ts.map +1 -1
- package/dist/pactile/tiles/safe-yaml.js +8 -8
- package/dist/pactile/tiles/safe-yaml.js.map +1 -1
- package/dist/pactile/tiles/selection-receipts.d.ts +71 -0
- package/dist/pactile/tiles/selection-receipts.d.ts.map +1 -0
- package/dist/pactile/tiles/selection-receipts.js +319 -0
- package/dist/pactile/tiles/selection-receipts.js.map +1 -0
- package/dist/pactile/tiles/selection.d.ts +185 -0
- package/dist/pactile/tiles/selection.d.ts.map +1 -0
- package/dist/pactile/tiles/selection.js +985 -0
- package/dist/pactile/tiles/selection.js.map +1 -0
- package/dist/pactile/verification/git-observer.d.ts +3 -0
- package/dist/pactile/verification/git-observer.d.ts.map +1 -0
- package/dist/pactile/verification/git-observer.js +3 -0
- package/dist/pactile/verification/git-observer.js.map +1 -0
- package/dist/pactile/verification/index.d.ts +11 -0
- package/dist/pactile/verification/index.d.ts.map +1 -0
- package/dist/pactile/verification/index.js +6 -0
- package/dist/pactile/verification/index.js.map +1 -0
- package/dist/pactile/verification/jev-advice.d.ts +78 -0
- package/dist/pactile/verification/jev-advice.d.ts.map +1 -0
- package/dist/pactile/verification/jev-advice.js +397 -0
- package/dist/pactile/verification/jev-advice.js.map +1 -0
- package/dist/pactile/verification/planner.d.ts +15 -0
- package/dist/pactile/verification/planner.d.ts.map +1 -0
- package/dist/pactile/verification/planner.js +412 -0
- package/dist/pactile/verification/planner.js.map +1 -0
- package/dist/pactile/verification/receipt.d.ts +101 -0
- package/dist/pactile/verification/receipt.d.ts.map +1 -0
- package/dist/pactile/verification/receipt.js +400 -0
- package/dist/pactile/verification/receipt.js.map +1 -0
- package/dist/pactile/verification/types.d.ts +72 -0
- package/dist/pactile/verification/types.d.ts.map +1 -0
- package/dist/pactile/verification/types.js +21 -0
- package/dist/pactile/verification/types.js.map +1 -0
- package/dist/pactile/worktree/git-probe.d.ts +32 -0
- package/dist/pactile/worktree/git-probe.d.ts.map +1 -0
- package/dist/pactile/worktree/git-probe.js +336 -0
- package/dist/pactile/worktree/git-probe.js.map +1 -0
- package/dist/pactile/worktree/git-removal.d.ts +3 -0
- package/dist/pactile/worktree/git-removal.d.ts.map +1 -0
- package/dist/pactile/worktree/git-removal.js +11 -0
- package/dist/pactile/worktree/git-removal.js.map +1 -0
- package/dist/pactile/worktree/index.d.ts +2 -0
- package/dist/pactile/worktree/index.d.ts.map +1 -0
- package/dist/pactile/worktree/index.js +2 -0
- package/dist/pactile/worktree/index.js.map +1 -0
- package/dist/pactile/worktree/integration-evidence.d.ts +80 -0
- package/dist/pactile/worktree/integration-evidence.d.ts.map +1 -0
- package/dist/pactile/worktree/integration-evidence.js +166 -0
- package/dist/pactile/worktree/integration-evidence.js.map +1 -0
- package/dist/pactile/worktree/manager-core.d.ts +50 -0
- package/dist/pactile/worktree/manager-core.d.ts.map +1 -0
- package/dist/pactile/worktree/manager-core.js +359 -0
- package/dist/pactile/worktree/manager-core.js.map +1 -0
- package/dist/pactile/worktree/manager-provenance.d.ts +32 -0
- package/dist/pactile/worktree/manager-provenance.d.ts.map +1 -0
- package/dist/pactile/worktree/manager-provenance.js +409 -0
- package/dist/pactile/worktree/manager-provenance.js.map +1 -0
- package/dist/pactile/worktree/manager-types.d.ts +84 -0
- package/dist/pactile/worktree/manager-types.d.ts.map +1 -0
- package/dist/pactile/worktree/manager-types.js +11 -0
- package/dist/pactile/worktree/manager-types.js.map +1 -0
- package/dist/pactile/worktree/manager.d.ts +10 -0
- package/dist/pactile/worktree/manager.d.ts.map +1 -0
- package/dist/pactile/worktree/manager.js +6 -0
- package/dist/pactile/worktree/manager.js.map +1 -0
- package/dist/pactile/worktree/run-workspace-cleanup.d.ts +10 -0
- package/dist/pactile/worktree/run-workspace-cleanup.d.ts.map +1 -0
- package/dist/pactile/worktree/run-workspace-cleanup.js +695 -0
- package/dist/pactile/worktree/run-workspace-cleanup.js.map +1 -0
- package/dist/pactile/worktree/run-workspace-lifecycle.d.ts +65 -0
- package/dist/pactile/worktree/run-workspace-lifecycle.d.ts.map +1 -0
- package/dist/pactile/worktree/run-workspace-lifecycle.js +276 -0
- package/dist/pactile/worktree/run-workspace-lifecycle.js.map +1 -0
- package/dist/templates/common/bundled-skills/pactile-meta/references/customize-local/change-hooks.md +1 -1
- package/dist/templates/common/bundled-skills/pactile-meta/references/local-architecture/generated-files.md +1 -1
- package/dist/templates/common/bundled-skills/pactile-meta/references/platform-files/hooks-and-settings.md +1 -1
- package/dist/templates/common/bundled-skills/pactile-meta/references/platform-files/overview.md +1 -1
- package/dist/templates/common/bundled-skills/pactile-meta/references/platform-files/platform-map.md +1 -1
- package/dist/templates/common/bundled-skills/smart-search-cli/SKILL.md +2 -0
- package/dist/templates/common/skills/brainstorm.md +19 -13
- package/dist/templates/markdown/agents.md +2 -2
- package/dist/templates/markdown/framework/execution-strategy.md.txt +1 -1
- package/dist/templates/markdown/framework/index.md.txt +1 -1
- package/dist/templates/markdown/framework/parallel-first-execution.md.txt +17 -105
- package/dist/templates/markdown/framework/prd-grill-frontier.md.txt +3 -3
- package/dist/templates/markdown/framework/release-boundary.md.txt +9 -10
- package/dist/templates/markdown/framework/retrieval-daily-guide.md.txt +1 -1
- package/dist/templates/pactile/CONTEXT.md +48 -36
- package/dist/templates/pactile/modules/approval-personal/contract.md +12 -24
- package/dist/templates/pactile/modules/close-basic/contract.md +15 -18
- package/dist/templates/pactile/modules/debug-recovery/contract.md +4 -4
- package/dist/templates/pactile/modules/define-basic/contract.md +15 -19
- package/dist/templates/pactile/modules/define-extended/contract.md +3 -3
- package/dist/templates/pactile/modules/execute-agent/contract.md +11 -19
- package/dist/templates/pactile/modules/intake-basic/contract.md +16 -13
- package/dist/templates/pactile/modules/verify-basic/contract.md +11 -20
- package/dist/templates/pactile/tasks/templates/release-execution/design.md +5 -5
- package/dist/templates/pactile/tasks/templates/release-readiness/design.md +6 -6
- package/dist/templates/pactile/tasks/templates/release-readiness/handoff-template.md +8 -2
- package/dist/templates/pactile/workflow.md +37 -41
- package/dist/templates/template-utils.d.ts +1 -2
- package/dist/templates/template-utils.d.ts.map +1 -1
- package/dist/templates/template-utils.js +2 -13
- package/dist/templates/template-utils.js.map +1 -1
- package/dist/types/ai-tools.d.ts +1 -1
- package/dist/types/ai-tools.js +1 -1
- package/dist/utils/manifest-prune.js +1 -1
- package/dist/utils/manifest-prune.js.map +1 -1
- package/dist/utils/p36-upgrade.d.ts +2 -2
- package/dist/utils/p36-upgrade.d.ts.map +1 -1
- package/dist/utils/p36-upgrade.js +2 -10
- package/dist/utils/p36-upgrade.js.map +1 -1
- package/dist/utils/post-update-smoke.js +1 -1
- package/dist/utils/post-update-smoke.js.map +1 -1
- package/dist/utils/readiness.d.ts +2 -2
- package/dist/utils/readiness.js +2 -2
- package/dist/utils/uninstall-scrubbers.js +1 -1
- package/dist/utils/workflow-dir.d.ts +1 -1
- package/dist/utils/workflow-dir.d.ts.map +1 -1
- package/dist/utils/workflow-dir.js +1 -1
- package/dist/utils/workflow-dir.js.map +1 -1
- package/package.json +42 -37
- package/CHANGELOG.md +0 -651
- package/bin/compat-warning.js +0 -11
- package/bin/pactile.js +0 -3
- package/bin/smart-search.js +0 -38
- package/dist/migrations/manifests/0.1.0.json +0 -9
- package/dist/migrations/manifests/0.1.1.json +0 -9
- package/dist/migrations/manifests/0.1.2.json +0 -9
- package/dist/migrations/manifests/0.1.3.json +0 -9
- package/dist/migrations/manifests/0.1.4.json +0 -9
- package/dist/migrations/manifests/0.2.1.json +0 -9
- package/dist/migrations/manifests/0.2.10.json +0 -374
- package/dist/migrations/manifests/0.2.2.json +0 -9
- package/dist/migrations/manifests/0.2.3.json +0 -9
- package/dist/migrations/manifests/0.2.4.json +0 -9
- package/dist/migrations/manifests/0.2.5.json +0 -9
- package/dist/migrations/manifests/0.2.6.json +0 -9
- package/dist/migrations/manifests/0.2.7.json +0 -9
- package/dist/migrations/manifests/0.2.8.json +0 -9
- package/dist/migrations/manifests/0.2.9.json +0 -9
- package/dist/migrations/manifests/0.3.0.json +0 -89
- package/dist/migrations/manifests/0.3.1.json +0 -32
- package/dist/migrations/manifests/0.3.2.json +0 -16
- package/dist/migrations/manifests/0.3.3.json +0 -9
- package/dist/migrations/manifests/0.3.4.json +0 -9
- package/dist/migrations/manifests/0.3.5.json +0 -9
- package/dist/migrations/manifests/0.3.6.json +0 -9
- package/dist/migrations/manifests/0.4.0.json +0 -9
- package/dist/migrations/manifests/0.4.1.json +0 -9
- package/dist/migrations/manifests/0.4.2.json +0 -9
- package/dist/migrations/manifests/0.4.3.json +0 -9
- package/dist/migrations/manifests/0.5.0-beta.0.json +0 -9
- package/dist/migrations/manifests/0.5.0-beta.1.json +0 -9
- package/dist/migrations/manifests/0.5.0-beta.2.json +0 -9
- package/dist/migrations/manifests/0.5.0-beta.3.json +0 -9
- package/dist/migrations/manifests/0.5.0-beta.4.json +0 -9
- package/dist/migrations/manifests/0.5.0-beta.5.json +0 -9
- package/dist/migrations/manifests/0.5.0.json +0 -9
- package/dist/pactile/compat/retired-alternate-client.d.ts +0 -42
- package/dist/pactile/compat/retired-alternate-client.d.ts.map +0 -1
- package/dist/pactile/compat/retired-alternate-client.js +0 -153
- package/dist/pactile/compat/retired-alternate-client.js.map +0 -1
- package/dist/pactile/compat/update-residue.d.ts +0 -4
- package/dist/pactile/compat/update-residue.d.ts.map +0 -1
- package/dist/pactile/compat/update-residue.js +0 -55
- package/dist/pactile/compat/update-residue.js.map +0 -1
- package/dist/utils/cursor-retrieval-env.d.ts +0 -17
- package/dist/utils/cursor-retrieval-env.d.ts.map +0 -1
- package/dist/utils/cursor-retrieval-env.js +0 -19
- package/dist/utils/cursor-retrieval-env.js.map +0 -1
- package/dist/utils/proxy.d.ts +0 -25
- package/dist/utils/proxy.d.ts.map +0 -1
- package/dist/utils/proxy.js +0 -60
- package/dist/utils/proxy.js.map +0 -1
- package/scripts/postinstall.js +0 -6
|
@@ -2,23 +2,368 @@ import { createHash, randomUUID } from "node:crypto";
|
|
|
2
2
|
import { execFileSync } from "node:child_process";
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
|
-
import { readDependencyGraph, readKernel, unmetRequires } from "../../core/task/index.js";
|
|
6
5
|
import { readStrategyContract } from "../task/strategy.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { parallelChild, reserveParallelChild, updateParallelChildPid } from "../parallel/policy.js";
|
|
6
|
+
import { approvedExecuteTask } from "../task/authorization.js";
|
|
7
|
+
import { parallelChild, reserveParallelChild, updateParallelChildPid, } from "../parallel/policy.js";
|
|
10
8
|
import { sameGitRoot } from "../../utils/git-root.js";
|
|
11
|
-
import {
|
|
9
|
+
import { readTaskKernel, } from "../../core/task/index.js";
|
|
10
|
+
import { isRunCandidateObservationEntry } from "../../core/task/task-candidate-observer.js";
|
|
11
|
+
import { resolveTaskDir } from "../task/session.js";
|
|
12
|
+
import { PiRpcClient, } from "./rpc.js";
|
|
13
|
+
import { buildIndependentPiReviewPrompt, } from "../review/contract.js";
|
|
14
|
+
import { resolvePiReviewEvidenceV1 } from "../review/evidence.js";
|
|
15
|
+
import { bindPiV2RunHost, persistPiV2StopAndRelease, preparePiV2RunDispatch, recheckPiV2RunDispatchBeforeSpawn, recheckPiV2RunDispatchWorkspace, } from "./v2-dispatch.js";
|
|
16
|
+
export { readPiHostStopReceipt, readPiTaskRunEvidence, } from "./v2-dispatch.js";
|
|
17
|
+
function safeReviewEvidencePath(taskDir, reference) {
|
|
18
|
+
if (!reference || path.isAbsolute(reference) || reference.includes("\0")) {
|
|
19
|
+
throw new Error("Pi Review evidence reference is invalid");
|
|
20
|
+
}
|
|
21
|
+
const resolved = path.resolve(taskDir, reference);
|
|
22
|
+
const relative = path.relative(path.resolve(taskDir), resolved);
|
|
23
|
+
if (!relative ||
|
|
24
|
+
relative === ".." ||
|
|
25
|
+
relative.startsWith(`..${path.sep}`) ||
|
|
26
|
+
path.isAbsolute(relative)) {
|
|
27
|
+
throw new Error("Pi Review evidence reference escapes the Task directory");
|
|
28
|
+
}
|
|
29
|
+
const realTaskDir = fs.realpathSync(taskDir);
|
|
30
|
+
const realFile = fs.realpathSync(resolved);
|
|
31
|
+
const realRelative = path.relative(realTaskDir, realFile);
|
|
32
|
+
if (!realRelative ||
|
|
33
|
+
realRelative === ".." ||
|
|
34
|
+
realRelative.startsWith(`..${path.sep}`) ||
|
|
35
|
+
path.isAbsolute(realRelative)) {
|
|
36
|
+
throw new Error("Pi Review evidence resolves outside the Task directory");
|
|
37
|
+
}
|
|
38
|
+
if (!fs.statSync(realFile).isFile())
|
|
39
|
+
throw new Error("Pi Review evidence is not a file");
|
|
40
|
+
return realFile;
|
|
41
|
+
}
|
|
42
|
+
function requireObjectJson(file, label) {
|
|
43
|
+
let parsed;
|
|
44
|
+
try {
|
|
45
|
+
parsed = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
throw new Error(`${label} is not valid JSON`);
|
|
49
|
+
}
|
|
50
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
51
|
+
throw new Error(`${label} must be a JSON object`);
|
|
52
|
+
}
|
|
53
|
+
return parsed;
|
|
54
|
+
}
|
|
55
|
+
/** Read the persisted Check receipts and exact structured response bytes before Review parsing. */
|
|
56
|
+
export function readPiCheckRunEvidenceV1(taskDir, record) {
|
|
57
|
+
if (record.role !== "check" ||
|
|
58
|
+
record.outcome !== "settled" ||
|
|
59
|
+
record.tool_errors !== 0 ||
|
|
60
|
+
record.result_redacted === true ||
|
|
61
|
+
!record.session_id ||
|
|
62
|
+
!record.reviewer_id ||
|
|
63
|
+
!record.task_id ||
|
|
64
|
+
!record.task_run_id ||
|
|
65
|
+
!record.candidate_snapshot_id ||
|
|
66
|
+
!record.candidate_fingerprint ||
|
|
67
|
+
!record.start_request_id ||
|
|
68
|
+
!Number.isSafeInteger(record.process_id) ||
|
|
69
|
+
!record.process_id ||
|
|
70
|
+
!record.review_start_receipt_ref ||
|
|
71
|
+
!record.review_stop_receipt_ref ||
|
|
72
|
+
!record.result_file ||
|
|
73
|
+
!record.result_sha256 ||
|
|
74
|
+
!record.review_attempts_ref ||
|
|
75
|
+
!record.review_attempts_sha256 ||
|
|
76
|
+
!record.progress_evidence_ref ||
|
|
77
|
+
!record.kernel_revision_at_dispatch) {
|
|
78
|
+
throw new Error("Pi Check run is incomplete or did not settle safely");
|
|
79
|
+
}
|
|
80
|
+
if (record.review_start_receipt_ref !==
|
|
81
|
+
`pi-bridge/starts/${record.run_id}.json` ||
|
|
82
|
+
record.review_stop_receipt_ref !==
|
|
83
|
+
`pi-bridge/stops/${record.run_id}.json` ||
|
|
84
|
+
record.progress_evidence_ref !==
|
|
85
|
+
`pi-bridge/events/${record.run_id}.jsonl` ||
|
|
86
|
+
record.result_file !== `pi-bridge/results/${record.run_id}.md`) {
|
|
87
|
+
throw new Error("Pi Check evidence references are not bound to this run");
|
|
88
|
+
}
|
|
89
|
+
const startPath = safeReviewEvidencePath(taskDir, record.review_start_receipt_ref);
|
|
90
|
+
const stopPath = safeReviewEvidencePath(taskDir, record.review_stop_receipt_ref);
|
|
91
|
+
const attemptsPath = safeReviewEvidencePath(taskDir, record.review_attempts_ref ?? "");
|
|
92
|
+
const eventPath = safeReviewEvidencePath(taskDir, record.progress_evidence_ref);
|
|
93
|
+
const resultPath = safeReviewEvidencePath(taskDir, record.result_file);
|
|
94
|
+
const start = requireObjectJson(startPath, "Pi Check start receipt");
|
|
95
|
+
const stop = requireObjectJson(stopPath, "Pi Check stop receipt");
|
|
96
|
+
const events = fs
|
|
97
|
+
.readFileSync(eventPath, "utf8")
|
|
98
|
+
.split(/\r?\n/u)
|
|
99
|
+
.filter(Boolean);
|
|
100
|
+
const resultBytes = fs.readFileSync(resultPath);
|
|
101
|
+
const attemptsBytes = fs.readFileSync(attemptsPath);
|
|
102
|
+
const resultSha256 = createHash("sha256").update(resultBytes).digest("hex");
|
|
103
|
+
const progressSha256 = createHash("sha256")
|
|
104
|
+
.update(fs.readFileSync(eventPath))
|
|
105
|
+
.digest("hex");
|
|
106
|
+
const attemptsSha256 = createHash("sha256").update(attemptsBytes).digest("hex");
|
|
107
|
+
if (attemptsBytes.byteLength > MAX_PI_REVIEW_ATTEMPT_MANIFEST_BYTES) {
|
|
108
|
+
throw new Error("Pi Check Review attempt manifest exceeds its evidence bound");
|
|
109
|
+
}
|
|
110
|
+
let attemptSummaries;
|
|
111
|
+
try {
|
|
112
|
+
attemptSummaries = attemptsBytes
|
|
113
|
+
.toString("utf8")
|
|
114
|
+
.split(/\r?\n/u)
|
|
115
|
+
.filter(Boolean)
|
|
116
|
+
.map((line) => JSON.parse(line));
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
throw new Error("Pi Check Review attempt manifest is not valid JSONL");
|
|
120
|
+
}
|
|
121
|
+
if (attemptSummaries.length < 1 || attemptSummaries.length > 2) {
|
|
122
|
+
throw new Error("Pi Check Review must contain one or two ordered attempts");
|
|
123
|
+
}
|
|
124
|
+
const responseEvidence = [];
|
|
125
|
+
const responseTexts = [];
|
|
126
|
+
for (const [index, attempt] of attemptSummaries.entries()) {
|
|
127
|
+
const number = index + 1;
|
|
128
|
+
const expectedKind = number === 1 ? "initial" : "format-correction";
|
|
129
|
+
const expectedRef = `pi-bridge/review-responses/${record.run_id}-attempt-${number}.txt`;
|
|
130
|
+
if (attempt?.attempt !== number ||
|
|
131
|
+
attempt.kind !== expectedKind ||
|
|
132
|
+
attempt.outcome !== "settled" ||
|
|
133
|
+
attempt.responseRef !== expectedRef ||
|
|
134
|
+
typeof attempt.responseSha256 !== "string" ||
|
|
135
|
+
!/^[a-f0-9]{64}$/u.test(attempt.responseSha256) ||
|
|
136
|
+
!Number.isSafeInteger(attempt.responseBytes) ||
|
|
137
|
+
attempt.responseBytes < 1 ||
|
|
138
|
+
attempt.responseBytes > MAX_PI_REVIEW_RESPONSE_BYTES ||
|
|
139
|
+
typeof attempt.redacted !== "boolean" ||
|
|
140
|
+
attempt.stopReason !== "stop") {
|
|
141
|
+
throw new Error("Pi Check Review attempt order or response binding is invalid");
|
|
142
|
+
}
|
|
143
|
+
const responsePath = safeReviewEvidencePath(taskDir, expectedRef);
|
|
144
|
+
const responseBytes = fs.readFileSync(responsePath);
|
|
145
|
+
if (responseBytes.byteLength !== attempt.responseBytes ||
|
|
146
|
+
createHash("sha256").update(responseBytes).digest("hex") !==
|
|
147
|
+
attempt.responseSha256) {
|
|
148
|
+
throw new Error("Pi Check Review response evidence does not match its attempt");
|
|
149
|
+
}
|
|
150
|
+
let responseText;
|
|
151
|
+
try {
|
|
152
|
+
responseText = new TextDecoder("utf-8", { fatal: true }).decode(responseBytes);
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
throw new Error("Pi Check Review response evidence is not valid UTF-8");
|
|
156
|
+
}
|
|
157
|
+
if (!Buffer.from(responseText, "utf8").equals(responseBytes)) {
|
|
158
|
+
throw new Error("Pi Check Review response evidence is not canonical UTF-8");
|
|
159
|
+
}
|
|
160
|
+
const format = isStructuredJsonObject(responseText)
|
|
161
|
+
? "structured-json-object"
|
|
162
|
+
: "non-structured-json";
|
|
163
|
+
if (attempt.format !== format) {
|
|
164
|
+
throw new Error("Pi Check Review response format does not match its evidence");
|
|
165
|
+
}
|
|
166
|
+
responseTexts.push(responseText);
|
|
167
|
+
responseEvidence.push({
|
|
168
|
+
ref: expectedRef,
|
|
169
|
+
sha256: attempt.responseSha256,
|
|
170
|
+
sizeBytes: attempt.responseBytes,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
const correction = record.review_format_correction;
|
|
174
|
+
const firstFormat = attemptSummaries[0]?.format;
|
|
175
|
+
const lastFormat = attemptSummaries.at(-1)?.format;
|
|
176
|
+
if ((attemptSummaries.length === 1 &&
|
|
177
|
+
(firstFormat !== "structured-json-object" ||
|
|
178
|
+
correction?.attempted !== false ||
|
|
179
|
+
correction.outcome !== "not-needed")) ||
|
|
180
|
+
(attemptSummaries.length === 2 &&
|
|
181
|
+
(firstFormat !== "non-structured-json" ||
|
|
182
|
+
lastFormat !== "structured-json-object" ||
|
|
183
|
+
correction?.attempted !== true ||
|
|
184
|
+
correction.outcome !== "accepted"))) {
|
|
185
|
+
throw new Error("Pi Check Review correction sequence is inconsistent");
|
|
186
|
+
}
|
|
187
|
+
const finalResponseText = responseTexts.at(-1);
|
|
188
|
+
if (finalResponseText === undefined ||
|
|
189
|
+
!Buffer.from(`${finalResponseText || "(Pi returned no assistant text.)"}\n`, "utf8")
|
|
190
|
+
.equals(resultBytes)) {
|
|
191
|
+
throw new Error("Pi Check result bytes do not match the final settled response");
|
|
192
|
+
}
|
|
193
|
+
const receiptResponses = stop.reviewResponses;
|
|
194
|
+
if (!Array.isArray(receiptResponses) ||
|
|
195
|
+
receiptResponses.length !== responseEvidence.length ||
|
|
196
|
+
receiptResponses.some((item, index) => {
|
|
197
|
+
const expected = responseEvidence[index];
|
|
198
|
+
return (item?.ref !== expected?.ref ||
|
|
199
|
+
item?.sha256 !== expected?.sha256 ||
|
|
200
|
+
item?.sizeBytes !== expected?.sizeBytes);
|
|
201
|
+
})) {
|
|
202
|
+
throw new Error("Pi Check stop receipt does not bind every response artifact");
|
|
203
|
+
}
|
|
204
|
+
if (start.schemaVersion !== 1 ||
|
|
205
|
+
start.source !== "pactile-pi-review" ||
|
|
206
|
+
start.role !== "check" ||
|
|
207
|
+
stop.schemaVersion !== 1 ||
|
|
208
|
+
stop.source !== "pactile-pi-review" ||
|
|
209
|
+
stop.assurance !== "manager-owned-child-exit" ||
|
|
210
|
+
stop.role !== "check" ||
|
|
211
|
+
start.taskId !== record.task_id ||
|
|
212
|
+
stop.taskId !== record.task_id ||
|
|
213
|
+
start.taskRunId !== record.task_run_id ||
|
|
214
|
+
stop.taskRunId !== record.task_run_id ||
|
|
215
|
+
start.piRunId !== record.run_id ||
|
|
216
|
+
stop.piRunId !== record.run_id ||
|
|
217
|
+
start.sessionId !== record.session_id ||
|
|
218
|
+
stop.sessionId !== record.session_id ||
|
|
219
|
+
start.processId !== record.process_id ||
|
|
220
|
+
stop.processId !== record.process_id ||
|
|
221
|
+
start.startRequestId !== record.start_request_id ||
|
|
222
|
+
stop.startRequestId !== record.start_request_id ||
|
|
223
|
+
start.kernelRevision !== record.kernel_revision_at_dispatch ||
|
|
224
|
+
start.candidateSnapshotId !== record.candidate_snapshot_id ||
|
|
225
|
+
start.candidateFingerprint !== record.candidate_fingerprint ||
|
|
226
|
+
start.evidenceRef !== `pi-bridge/runs/${record.run_id}.json` ||
|
|
227
|
+
stop.evidenceRef !== start.evidenceRef ||
|
|
228
|
+
start.progressEvidenceRef !== record.progress_evidence_ref ||
|
|
229
|
+
stop.progressEvidenceRef !== record.progress_evidence_ref ||
|
|
230
|
+
stop.progressEvidenceSha256 !== progressSha256 ||
|
|
231
|
+
stop.startReceiptRef !== record.review_start_receipt_ref ||
|
|
232
|
+
stop.stopReceiptId !== `review-stop-${record.run_id}` ||
|
|
233
|
+
stop.terminal !== "exited" ||
|
|
234
|
+
stop.exitCode !== 0 ||
|
|
235
|
+
stop.signalCode !== null ||
|
|
236
|
+
stop.cancellationRequestId !== null ||
|
|
237
|
+
stop.resultRef !== record.result_file ||
|
|
238
|
+
stop.resultSha256 !== record.result_sha256 ||
|
|
239
|
+
resultSha256 !== record.result_sha256 ||
|
|
240
|
+
record.review_attempts_ref !==
|
|
241
|
+
`pi-bridge/review-attempts/${record.run_id}.jsonl` ||
|
|
242
|
+
record.review_attempts_sha256 !== attemptsSha256 ||
|
|
243
|
+
stop.reviewAttemptsRef !== record.review_attempts_ref ||
|
|
244
|
+
stop.reviewAttemptsSha256 !== attemptsSha256 ||
|
|
245
|
+
record.event_count < 1 ||
|
|
246
|
+
events.length !== record.event_count ||
|
|
247
|
+
stop.processExit?.terminationVerified !== true ||
|
|
248
|
+
!stop.processExit.exitObservedAt ||
|
|
249
|
+
stop.processExit.processId !== record.process_id ||
|
|
250
|
+
stop.processExit.exitCode !== 0 ||
|
|
251
|
+
stop.processExit.signalCode !== null ||
|
|
252
|
+
record.reviewer_id !== `pi-session:${record.session_id}` ||
|
|
253
|
+
(record.cancellation_request_id !== undefined &&
|
|
254
|
+
record.cancellation_request_id !== null) ||
|
|
255
|
+
record.reviewer_identity_assurance !== "caller-declared") {
|
|
256
|
+
throw new Error("Pi Check receipts, events, or result bytes do not match the persisted run");
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
start,
|
|
260
|
+
stop,
|
|
261
|
+
resultBytes,
|
|
262
|
+
evidenceRefs: [
|
|
263
|
+
record.review_start_receipt_ref,
|
|
264
|
+
record.review_stop_receipt_ref,
|
|
265
|
+
record.progress_evidence_ref,
|
|
266
|
+
record.result_file,
|
|
267
|
+
record.review_attempts_ref,
|
|
268
|
+
...responseEvidence.map((item) => item.ref),
|
|
269
|
+
],
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
const MAX_PI_REVIEW_RESPONSE_BYTES = 64 * 1024;
|
|
273
|
+
const MAX_PI_REVIEW_ATTEMPT_MANIFEST_BYTES = 16 * 1024;
|
|
274
|
+
/** Build the independent Review binding from the current Verify Kernel state. */
|
|
275
|
+
export function preparePiReviewRoute(root, taskReference) {
|
|
276
|
+
const projectRoot = fs.realpathSync(root);
|
|
277
|
+
const taskDir = resolveTaskDir(projectRoot, taskReference);
|
|
278
|
+
if (!fs.statSync(taskDir, { throwIfNoEntry: false })?.isDirectory()) {
|
|
279
|
+
throw new Error(`Task not found: ${taskReference}`);
|
|
280
|
+
}
|
|
281
|
+
if (!fs.existsSync(path.join(taskDir, "verify.md"))) {
|
|
282
|
+
throw new Error("Pi Review requires verify.md");
|
|
283
|
+
}
|
|
284
|
+
const read = readTaskKernel({ root: projectRoot, taskDir, cwd: projectRoot });
|
|
285
|
+
if (read.kind !== "task-kernel-v2") {
|
|
286
|
+
throw new Error("Pi Review requires a V2 Task Kernel");
|
|
287
|
+
}
|
|
288
|
+
const kernel = read.kernel;
|
|
289
|
+
if (kernel.phase !== "verify") {
|
|
290
|
+
throw new Error("Pi Review requires the Task Kernel Verify phase");
|
|
291
|
+
}
|
|
292
|
+
const run = kernel.runs.at(-1);
|
|
293
|
+
if (run?.state !== "completed" || !run.result || !run.candidateSnapshot) {
|
|
294
|
+
throw new Error("Pi Review requires the latest completed Run candidate");
|
|
295
|
+
}
|
|
296
|
+
const workdir = run.workspace
|
|
297
|
+
? fs.realpathSync(run.workspace.canonicalPath)
|
|
298
|
+
: projectRoot;
|
|
299
|
+
if (run.workspace) {
|
|
300
|
+
const worktreesRoot = fs.realpathSync(path.join(projectRoot, ".pactile", "worktrees"));
|
|
301
|
+
const relative = path.relative(worktreesRoot, workdir);
|
|
302
|
+
if (!relative ||
|
|
303
|
+
relative === ".." ||
|
|
304
|
+
relative.startsWith(`..${path.sep}`) ||
|
|
305
|
+
path.isAbsolute(relative)) {
|
|
306
|
+
throw new Error("Pi Review Run worktree is outside .pactile/worktrees");
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
const binding = {
|
|
310
|
+
kernelRevision: kernel.revision,
|
|
311
|
+
taskId: kernel.identity.taskId,
|
|
312
|
+
runId: run.id,
|
|
313
|
+
candidateSnapshotId: run.candidateSnapshot.id,
|
|
314
|
+
candidateFingerprint: run.candidateSnapshot.fingerprint,
|
|
315
|
+
taskTitle: kernel.definition.title,
|
|
316
|
+
taskDescription: kernel.definition.description,
|
|
317
|
+
deliverable: kernel.definition.deliverable,
|
|
318
|
+
runSummary: run.result.summary,
|
|
319
|
+
runReferences: [...run.input.references],
|
|
320
|
+
authorizationScope: run.authorization.scope,
|
|
321
|
+
writeSetSnapshot: [...run.writeSetSnapshot],
|
|
322
|
+
candidateEntries: run.candidateSnapshot.entries.map((entry) => ({
|
|
323
|
+
ref: entry.ref,
|
|
324
|
+
fingerprint: entry.fingerprint,
|
|
325
|
+
})),
|
|
326
|
+
allowedEvidenceRefs: [...new Set([
|
|
327
|
+
...run.candidateSnapshot.entries
|
|
328
|
+
.filter((entry) => !isRunCandidateObservationEntry(entry.ref))
|
|
329
|
+
.map((entry) => entry.ref),
|
|
330
|
+
...run.result.evidenceRefs.filter((reference) => !isRunCandidateObservationEntry(reference)),
|
|
331
|
+
])].sort(),
|
|
332
|
+
acceptanceCriteria: kernel.definition.acceptanceCriteria.map((criterion) => ({
|
|
333
|
+
id: criterion.id,
|
|
334
|
+
description: criterion.description,
|
|
335
|
+
})),
|
|
336
|
+
};
|
|
337
|
+
return { taskDir, kernel, run, workdir, binding };
|
|
338
|
+
}
|
|
339
|
+
function assertSameReviewBinding(actual, expected) {
|
|
340
|
+
if (JSON.stringify(actual) !== JSON.stringify(expected)) {
|
|
341
|
+
throw new Error("Pi Review binding is stale or does not match the current Run candidate");
|
|
342
|
+
}
|
|
343
|
+
}
|
|
12
344
|
function atomicJson(file, value) {
|
|
13
345
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
14
346
|
const temporary = `${file}.${process.pid}.${randomUUID()}.tmp`;
|
|
15
|
-
fs.writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\n`, {
|
|
347
|
+
fs.writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\n`, {
|
|
348
|
+
encoding: "utf8",
|
|
349
|
+
mode: 0o600,
|
|
350
|
+
});
|
|
16
351
|
fs.renameSync(temporary, file);
|
|
17
352
|
}
|
|
353
|
+
function writeExclusiveJson(file, value) {
|
|
354
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
355
|
+
fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`, {
|
|
356
|
+
encoding: "utf8",
|
|
357
|
+
flag: "wx",
|
|
358
|
+
mode: 0o600,
|
|
359
|
+
});
|
|
360
|
+
}
|
|
18
361
|
function parseJson(file) {
|
|
19
362
|
try {
|
|
20
363
|
const raw = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
21
|
-
return raw && typeof raw === "object" && !Array.isArray(raw)
|
|
364
|
+
return raw && typeof raw === "object" && !Array.isArray(raw)
|
|
365
|
+
? raw
|
|
366
|
+
: null;
|
|
22
367
|
}
|
|
23
368
|
catch {
|
|
24
369
|
return null;
|
|
@@ -37,59 +382,205 @@ function alive(pid) {
|
|
|
37
382
|
}
|
|
38
383
|
function redact(text) {
|
|
39
384
|
return text
|
|
40
|
-
.replace(/\b(?:sk[-_]|
|
|
41
|
-
.replace(/\bBearer\s+[A-Za-z0-9._
|
|
385
|
+
.replace(/\b(?:sk[-_]|gh[pousr]_|glpat-|plane_api_)[A-Za-z0-9_-]{12,}/gi, "[redacted]")
|
|
386
|
+
.replace(/\bBearer\s+[A-Za-z0-9._~+/-]{12,}/gi, "Bearer [redacted]")
|
|
42
387
|
.replace(/\b(api[_-]?key|token|password|secret)\s*[:=]\s*\S+/gi, "$1=[redacted]");
|
|
43
388
|
}
|
|
389
|
+
function safeProviderMessage(text) {
|
|
390
|
+
return [...redact(text)]
|
|
391
|
+
.map((character) => {
|
|
392
|
+
const code = character.charCodeAt(0);
|
|
393
|
+
return code <= 0x1f || code === 0x7f ? " " : character;
|
|
394
|
+
})
|
|
395
|
+
.join("")
|
|
396
|
+
.slice(0, 500);
|
|
397
|
+
}
|
|
398
|
+
function safeErrorSummary(value) {
|
|
399
|
+
const message = value.toLowerCase();
|
|
400
|
+
if (message.includes("cancel"))
|
|
401
|
+
return "Pi operation cancelled";
|
|
402
|
+
if (message.includes("timed out") || message.includes("timeout"))
|
|
403
|
+
return "Pi operation timed out";
|
|
404
|
+
if (message.includes("session changed"))
|
|
405
|
+
return "Pi Review session changed";
|
|
406
|
+
if (message.includes("implementation run host session"))
|
|
407
|
+
return "Pi Check session matches the implementation Run host session";
|
|
408
|
+
if (message.includes("candidate") ||
|
|
409
|
+
message.includes("review binding") ||
|
|
410
|
+
message.includes("evidence"))
|
|
411
|
+
return "Pi Review preflight failed";
|
|
412
|
+
if (message.includes("launch failed"))
|
|
413
|
+
return "Pi RPC launch failed";
|
|
414
|
+
if (message.includes("exited"))
|
|
415
|
+
return "Pi RPC process exited";
|
|
416
|
+
if (message.includes("rejected"))
|
|
417
|
+
return "Pi RPC request rejected";
|
|
418
|
+
if (message.includes("streaming") || message.includes("settled without"))
|
|
419
|
+
return "Pi did not settle a final response";
|
|
420
|
+
return "Pi operation failed";
|
|
421
|
+
}
|
|
422
|
+
function normalizeEventType(value) {
|
|
423
|
+
if (typeof value !== "string")
|
|
424
|
+
return "other";
|
|
425
|
+
const allowed = new Set([
|
|
426
|
+
"agent_start",
|
|
427
|
+
"agent_end",
|
|
428
|
+
"agent_settled",
|
|
429
|
+
"auto_retry_start",
|
|
430
|
+
"auto_retry_end",
|
|
431
|
+
"auto_compaction_start",
|
|
432
|
+
"auto_compaction_end",
|
|
433
|
+
"extension_error",
|
|
434
|
+
"message_start",
|
|
435
|
+
"message_update",
|
|
436
|
+
"message_end",
|
|
437
|
+
"session_start",
|
|
438
|
+
"session_shutdown",
|
|
439
|
+
"session_before_compact",
|
|
440
|
+
"session_compact",
|
|
441
|
+
"tool_execution_start",
|
|
442
|
+
"tool_execution_update",
|
|
443
|
+
"tool_execution_end",
|
|
444
|
+
"turn_start",
|
|
445
|
+
"turn_end",
|
|
446
|
+
]);
|
|
447
|
+
return allowed.has(value) ? value : "other";
|
|
448
|
+
}
|
|
449
|
+
function normalizeToolName(value) {
|
|
450
|
+
const allowed = new Set([
|
|
451
|
+
"bash",
|
|
452
|
+
"edit",
|
|
453
|
+
"find",
|
|
454
|
+
"grep",
|
|
455
|
+
"ls",
|
|
456
|
+
"read",
|
|
457
|
+
"write",
|
|
458
|
+
]);
|
|
459
|
+
return allowed.has(value) ? value : "other";
|
|
460
|
+
}
|
|
461
|
+
function normalizeMessageRole(value) {
|
|
462
|
+
if (typeof value !== "string")
|
|
463
|
+
return "other";
|
|
464
|
+
const allowed = new Set(["assistant", "system", "tool", "user"]);
|
|
465
|
+
return allowed.has(value) ? value : "other";
|
|
466
|
+
}
|
|
467
|
+
function normalizeProviderError(value) {
|
|
468
|
+
return typeof value === "string" && value.length > 0
|
|
469
|
+
? "provider-reported-error"
|
|
470
|
+
: null;
|
|
471
|
+
}
|
|
472
|
+
function normalizeStopReason(value) {
|
|
473
|
+
if (typeof value !== "string")
|
|
474
|
+
return null;
|
|
475
|
+
const allowed = new Set([
|
|
476
|
+
"stop",
|
|
477
|
+
"length",
|
|
478
|
+
"error",
|
|
479
|
+
"aborted",
|
|
480
|
+
"cancelled",
|
|
481
|
+
"toolUse",
|
|
482
|
+
"end_turn",
|
|
483
|
+
]);
|
|
484
|
+
return allowed.has(value) ? value : "other";
|
|
485
|
+
}
|
|
44
486
|
function assistantText(event) {
|
|
45
487
|
const messages = Array.isArray(event.messages) ? event.messages : [];
|
|
46
|
-
const assistant = [...messages]
|
|
488
|
+
const assistant = [...messages]
|
|
489
|
+
.reverse()
|
|
490
|
+
.find((message) => message &&
|
|
491
|
+
typeof message === "object" &&
|
|
492
|
+
message.role === "assistant");
|
|
47
493
|
if (!assistant)
|
|
48
|
-
return {
|
|
494
|
+
return {
|
|
495
|
+
rawText: "",
|
|
496
|
+
text: "",
|
|
497
|
+
redacted: false,
|
|
498
|
+
stopReason: null,
|
|
499
|
+
errorMessage: null,
|
|
500
|
+
};
|
|
49
501
|
const content = Array.isArray(assistant.content) ? assistant.content : [];
|
|
50
|
-
const text = content
|
|
51
|
-
.
|
|
52
|
-
|
|
53
|
-
|
|
502
|
+
const text = content
|
|
503
|
+
.filter((part) => part &&
|
|
504
|
+
typeof part === "object" &&
|
|
505
|
+
part.type === "text")
|
|
506
|
+
.map((part) => String(part.text ?? ""))
|
|
507
|
+
.join("\n");
|
|
508
|
+
return {
|
|
509
|
+
rawText: text,
|
|
510
|
+
text: redact(text),
|
|
511
|
+
redacted: redact(text) !== text,
|
|
512
|
+
stopReason: normalizeStopReason(assistant.stopReason),
|
|
513
|
+
errorMessage: normalizeProviderError(assistant.errorMessage),
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
function isStructuredJsonObject(text) {
|
|
517
|
+
try {
|
|
518
|
+
const parsed = JSON.parse(text.trim());
|
|
519
|
+
return Boolean(parsed && typeof parsed === "object" && !Array.isArray(parsed));
|
|
520
|
+
}
|
|
521
|
+
catch {
|
|
522
|
+
return false;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
function reviewAttemptSummary(attempt, kind, startedAt, response, firstEventMs = null, error, responseRef = null) {
|
|
526
|
+
if (!response) {
|
|
527
|
+
return {
|
|
528
|
+
attempt,
|
|
529
|
+
kind,
|
|
530
|
+
outcome: "transport-error",
|
|
531
|
+
format: "unavailable",
|
|
532
|
+
responseRef: null,
|
|
533
|
+
responseSha256: null,
|
|
534
|
+
responseBytes: 0,
|
|
535
|
+
redacted: false,
|
|
536
|
+
stopReason: null,
|
|
537
|
+
errorMessage: error ? safeErrorSummary(error) : null,
|
|
538
|
+
firstEventMs,
|
|
539
|
+
elapsedMs: Math.round(performance.now() - startedAt),
|
|
540
|
+
recordedAt: new Date().toISOString(),
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
return {
|
|
544
|
+
attempt,
|
|
545
|
+
kind,
|
|
546
|
+
outcome: "settled",
|
|
547
|
+
format: isStructuredJsonObject(response.rawText)
|
|
548
|
+
? "structured-json-object"
|
|
549
|
+
: "non-structured-json",
|
|
550
|
+
responseRef,
|
|
551
|
+
responseSha256: createHash("sha256")
|
|
552
|
+
.update(Buffer.from(response.text, "utf8"))
|
|
553
|
+
.digest("hex"),
|
|
554
|
+
responseBytes: Buffer.byteLength(response.text, "utf8"),
|
|
555
|
+
redacted: response.redacted,
|
|
556
|
+
stopReason: response.stopReason,
|
|
557
|
+
errorMessage: response.errorMessage,
|
|
558
|
+
firstEventMs,
|
|
559
|
+
elapsedMs: Math.round(performance.now() - startedAt),
|
|
560
|
+
recordedAt: new Date().toISOString(),
|
|
561
|
+
};
|
|
54
562
|
}
|
|
55
563
|
function evidenceEvent(event) {
|
|
56
|
-
const safe = {
|
|
564
|
+
const safe = {
|
|
565
|
+
at: new Date().toISOString(),
|
|
566
|
+
type: normalizeEventType(event.type),
|
|
567
|
+
};
|
|
57
568
|
if (typeof event.toolName === "string")
|
|
58
|
-
safe.tool = event.toolName;
|
|
569
|
+
safe.tool = normalizeToolName(event.toolName);
|
|
59
570
|
if (event.type === "tool_execution_end")
|
|
60
571
|
safe.is_error = event.isError === true;
|
|
61
|
-
if (event.type === "message_end" &&
|
|
572
|
+
if (event.type === "message_end" &&
|
|
573
|
+
event.message &&
|
|
574
|
+
typeof event.message === "object") {
|
|
62
575
|
const message = event.message;
|
|
63
|
-
safe.role =
|
|
576
|
+
safe.role = normalizeMessageRole(message.role);
|
|
64
577
|
if (typeof message.stopReason === "string")
|
|
65
|
-
safe.stop_reason = message.stopReason;
|
|
578
|
+
safe.stop_reason = normalizeStopReason(message.stopReason) ?? "other";
|
|
66
579
|
}
|
|
67
580
|
return safe;
|
|
68
581
|
}
|
|
69
582
|
export function approvedTask(root, reference, role) {
|
|
70
|
-
const dir =
|
|
71
|
-
if (!fs.statSync(dir, { throwIfNoEntry: false })?.isDirectory())
|
|
72
|
-
throw new Error(`Task not found: ${reference}`);
|
|
73
|
-
const { kernel } = readKernel({ taskDir: dir, cwd: root });
|
|
74
|
-
if (kernel.phase !== "execute" || kernel.projection?.status !== "in_progress") {
|
|
75
|
-
throw new Error("Pi dispatch requires an approved task in Kernel Execute phase");
|
|
76
|
-
}
|
|
77
|
-
const approval = kernel.projection.extras.execution_approval;
|
|
78
|
-
if (!approval || typeof approval !== "object" || approval.approved_by !== "user") {
|
|
79
|
-
throw new Error("Pi dispatch requires recorded user execution approval");
|
|
80
|
-
}
|
|
81
|
-
const stamp = approval;
|
|
82
|
-
const current = kernel.projection.record;
|
|
83
|
-
const satisfied = Array.isArray(kernel.projection.extras.dependency_satisfied)
|
|
84
|
-
? kernel.projection.extras.dependency_satisfied.filter((item) => typeof item === "string") : [];
|
|
85
|
-
const unmet = unmetRequires(readDependencyGraph(kernel.projection.extras), satisfied, current.id);
|
|
86
|
-
if (unmet.length)
|
|
87
|
-
throw new Error(`requires unmet: ${unmet.join(", ")}`);
|
|
88
|
-
const fingerprints = [current, { ...current, status: "planning" }]
|
|
89
|
-
.map((candidate) => checkStartExecution(root, dir, candidate))
|
|
90
|
-
.some((guard) => guard.contractFingerprint === stamp.contract_fingerprint && guard.artifactFingerprint === stamp.artifact_fingerprint);
|
|
91
|
-
if (!fingerprints)
|
|
92
|
-
throw new Error("Execution contract changed after approval; return to Define and renew approval");
|
|
583
|
+
const dir = approvedExecuteTask(root, reference);
|
|
93
584
|
const implementation = path.join(dir, "implement.md");
|
|
94
585
|
if (role === "implement" && !fs.existsSync(implementation))
|
|
95
586
|
throw new Error("Pi implement dispatch requires implement.md");
|
|
@@ -115,7 +606,9 @@ export function piWorkdir(root, dir, role) {
|
|
|
115
606
|
if (parsed.contract.isolation === "main-worktree")
|
|
116
607
|
return root;
|
|
117
608
|
const task = JSON.parse(fs.readFileSync(path.join(dir, "task.json"), "utf8"));
|
|
118
|
-
const worktreePath = task && typeof task === "object" && "worktree_path" in task
|
|
609
|
+
const worktreePath = task && typeof task === "object" && "worktree_path" in task
|
|
610
|
+
? task.worktree_path
|
|
611
|
+
: null;
|
|
119
612
|
if (typeof worktreePath !== "string")
|
|
120
613
|
throw new Error("git-worktree isolation requires a prepared Child worktree");
|
|
121
614
|
const worktrees = path.resolve(root, ".pactile", "worktrees");
|
|
@@ -128,11 +621,20 @@ export function piWorkdir(root, dir, role) {
|
|
|
128
621
|
const realRoot = fs.realpathSync(worktrees);
|
|
129
622
|
const realCandidate = fs.realpathSync(candidate);
|
|
130
623
|
const realRelative = path.relative(realRoot, realCandidate);
|
|
131
|
-
if (!realRelative ||
|
|
624
|
+
if (!realRelative ||
|
|
625
|
+
realRelative.startsWith("..") ||
|
|
626
|
+
path.isAbsolute(realRelative))
|
|
132
627
|
throw new Error("Child worktree resolves outside .pactile/worktrees");
|
|
133
628
|
let gitRoot;
|
|
134
629
|
try {
|
|
135
|
-
gitRoot = execFileSync("git", [
|
|
630
|
+
gitRoot = execFileSync("git", [
|
|
631
|
+
"-c",
|
|
632
|
+
`safe.directory=${realCandidate.replaceAll("\\", "/")}`,
|
|
633
|
+
"-C",
|
|
634
|
+
realCandidate,
|
|
635
|
+
"rev-parse",
|
|
636
|
+
"--show-toplevel",
|
|
637
|
+
], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
136
638
|
}
|
|
137
639
|
catch {
|
|
138
640
|
throw new Error("Child worktree is not a usable Git checkout");
|
|
@@ -154,7 +656,7 @@ export class PiTaskBridge {
|
|
|
154
656
|
this.root = root;
|
|
155
657
|
this.launch = launch;
|
|
156
658
|
}
|
|
157
|
-
async ensureClient(dir, workdir, role, resumeFile) {
|
|
659
|
+
async ensureClient(dir, workdir, role, resumeFile, beforeSpawn, sessionDirOverride, startupTimeoutMs) {
|
|
158
660
|
if (this.client && this.taskDir !== dir)
|
|
159
661
|
throw new Error("Pi bridge is bound to one Pactile task");
|
|
160
662
|
if (this.client && this.workdir !== workdir)
|
|
@@ -163,38 +665,103 @@ export class PiTaskBridge {
|
|
|
163
665
|
throw new Error("Pi bridge cannot reuse a process across worker roles");
|
|
164
666
|
if (this.client?.isStarted)
|
|
165
667
|
return { client: this.client, startupMs: 0, mode: "warm" };
|
|
166
|
-
const client = new PiRpcClient({
|
|
668
|
+
const client = new PiRpcClient({
|
|
669
|
+
cwd: workdir,
|
|
670
|
+
sessionDir: sessionDirOverride ?? path.join(dir, "pi-bridge", "sessions"),
|
|
671
|
+
launch: this.launch,
|
|
672
|
+
readOnly: role !== "implement",
|
|
673
|
+
});
|
|
167
674
|
this.client = client;
|
|
168
675
|
this.taskDir = dir;
|
|
169
676
|
this.workdir = workdir;
|
|
170
677
|
this.role = role;
|
|
171
|
-
const startupMs = await client.start();
|
|
678
|
+
const startupMs = await client.start(beforeSpawn, startupTimeoutMs);
|
|
172
679
|
if (resumeFile)
|
|
173
680
|
await client.switchSession(resumeFile);
|
|
174
681
|
return { client, startupMs, mode: "cold" };
|
|
175
682
|
}
|
|
683
|
+
async closeForDispatch() {
|
|
684
|
+
const client = this.client;
|
|
685
|
+
if (!client)
|
|
686
|
+
return null;
|
|
687
|
+
const receipt = await client.closeAndObserve();
|
|
688
|
+
if (receipt?.terminationVerified) {
|
|
689
|
+
this.client = null;
|
|
690
|
+
this.taskDir = null;
|
|
691
|
+
this.workdir = null;
|
|
692
|
+
this.role = null;
|
|
693
|
+
}
|
|
694
|
+
return receipt;
|
|
695
|
+
}
|
|
696
|
+
async closeForReview() {
|
|
697
|
+
const client = this.client;
|
|
698
|
+
if (!client)
|
|
699
|
+
return null;
|
|
700
|
+
const receipt = await client.closeAndObserve();
|
|
701
|
+
if (receipt?.terminationVerified) {
|
|
702
|
+
this.client = null;
|
|
703
|
+
this.taskDir = null;
|
|
704
|
+
this.workdir = null;
|
|
705
|
+
this.role = null;
|
|
706
|
+
}
|
|
707
|
+
return receipt;
|
|
708
|
+
}
|
|
176
709
|
async run(input) {
|
|
177
710
|
if (path.resolve(input.root) !== path.resolve(this.root))
|
|
178
711
|
throw new Error("Pi bridge root mismatch");
|
|
179
712
|
if (!input.prompt.trim())
|
|
180
713
|
throw new Error("Pi prompt is empty");
|
|
181
|
-
if (!Number.isInteger(input.timeoutMs) ||
|
|
714
|
+
if (!Number.isInteger(input.timeoutMs) ||
|
|
715
|
+
input.timeoutMs < 1_000 ||
|
|
716
|
+
input.timeoutMs > 86_400_000)
|
|
182
717
|
throw new Error("Pi timeout must be 1 second to 24 hours");
|
|
183
718
|
if (this.locked)
|
|
184
719
|
throw new Error("Pi bridge already has an active run");
|
|
720
|
+
if (input.runId !== undefined) {
|
|
721
|
+
if (input.role !== "implement")
|
|
722
|
+
throw new Error("Pi V2 dispatch currently supports only the implement role");
|
|
723
|
+
if (input.resume)
|
|
724
|
+
throw new Error("Pi V2 dispatch cannot resume a prior session");
|
|
725
|
+
if (this.client)
|
|
726
|
+
throw new Error("Pi V2 dispatch requires a fresh Pi bridge process");
|
|
727
|
+
if (!input.runId.trim())
|
|
728
|
+
throw new Error("Pi V2 --run-id must be non-empty");
|
|
729
|
+
const dispatch = preparePiV2RunDispatch(this.root, input.task, input.runId, input.scheduleReceiptFingerprint);
|
|
730
|
+
return this.runApproved(input, dispatch.taskDir, dispatch.workdir, null, null, dispatch, null);
|
|
731
|
+
}
|
|
732
|
+
if (input.role === "check") {
|
|
733
|
+
if (!input.reviewBinding) {
|
|
734
|
+
throw new Error("Pi Check requires an independent Review binding");
|
|
735
|
+
}
|
|
736
|
+
if (input.runId || input.resume) {
|
|
737
|
+
throw new Error("Pi Review Check cannot resume or reuse an execution Run");
|
|
738
|
+
}
|
|
739
|
+
if (this.client) {
|
|
740
|
+
throw new Error("Pi Review Check requires a fresh independent Pi session");
|
|
741
|
+
}
|
|
742
|
+
const review = preparePiReviewRoute(this.root, input.task);
|
|
743
|
+
assertSameReviewBinding(review.binding, input.reviewBinding);
|
|
744
|
+
return this.runApproved(input, review.taskDir, review.workdir, null, null, null, review);
|
|
745
|
+
}
|
|
746
|
+
if (input.reviewBinding) {
|
|
747
|
+
throw new Error("Pi Review binding is only valid for the Check role");
|
|
748
|
+
}
|
|
185
749
|
const dir = approvedTask(this.root, input.task, input.role);
|
|
186
750
|
const workdir = piWorkdir(this.root, dir, input.role);
|
|
187
751
|
const scope = parallelChild(this.root, dir);
|
|
188
752
|
const leaseId = randomUUID();
|
|
189
|
-
const release = reserveParallelChild(this.root, dir, {
|
|
753
|
+
const release = reserveParallelChild(this.root, dir, {
|
|
754
|
+
id: leaseId,
|
|
755
|
+
scheduleReceiptFingerprint: input.scheduleReceiptFingerprint,
|
|
756
|
+
});
|
|
190
757
|
try {
|
|
191
|
-
return await this.runApproved(input, dir, workdir, scope?.touches ?? null, leaseId);
|
|
758
|
+
return await this.runApproved(input, dir, workdir, scope?.touches ?? null, leaseId, null, null);
|
|
192
759
|
}
|
|
193
760
|
finally {
|
|
194
761
|
release();
|
|
195
762
|
}
|
|
196
763
|
}
|
|
197
|
-
async runApproved(input, dir, workdir, touches,
|
|
764
|
+
async runApproved(input, dir, workdir, touches, parallelLeaseId, dispatch, review) {
|
|
198
765
|
const evidence = path.join(dir, "pi-bridge");
|
|
199
766
|
const lockFile = path.join(evidence, "active.json");
|
|
200
767
|
const latestFile = path.join(evidence, "latest.json");
|
|
@@ -206,7 +773,12 @@ export class PiTaskBridge {
|
|
|
206
773
|
throw new Error("Pi dispatch already active; stop the existing bridge before retrying");
|
|
207
774
|
const stale = parseJson(latestFile);
|
|
208
775
|
if (stale?.outcome === "running") {
|
|
209
|
-
const recovered = {
|
|
776
|
+
const recovered = {
|
|
777
|
+
...stale,
|
|
778
|
+
outcome: "interrupted",
|
|
779
|
+
ended_at: new Date().toISOString(),
|
|
780
|
+
reason: "bridge process exited before recording a terminal event",
|
|
781
|
+
};
|
|
210
782
|
atomicJson(path.join(evidence, "runs", `${stale.run_id}.json`), recovered);
|
|
211
783
|
atomicJson(latestFile, recovered);
|
|
212
784
|
}
|
|
@@ -215,37 +787,142 @@ export class PiTaskBridge {
|
|
|
215
787
|
const orphan = parseJson(latestFile);
|
|
216
788
|
if (orphan?.outcome === "running") {
|
|
217
789
|
const stale = orphan;
|
|
218
|
-
const recovered = {
|
|
790
|
+
const recovered = {
|
|
791
|
+
...stale,
|
|
792
|
+
outcome: "interrupted",
|
|
793
|
+
ended_at: new Date().toISOString(),
|
|
794
|
+
reason: "bridge process exited before recording a terminal event",
|
|
795
|
+
};
|
|
219
796
|
atomicJson(path.join(evidence, "runs", `${stale.run_id}.json`), recovered);
|
|
220
797
|
atomicJson(latestFile, recovered);
|
|
221
798
|
}
|
|
222
799
|
const previous = parseJson(latestFile);
|
|
223
800
|
const sessionRoot = path.resolve(evidence, "sessions");
|
|
224
|
-
const previousSession = typeof previous?.session_file === "string"
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
801
|
+
const previousSession = typeof previous?.session_file === "string"
|
|
802
|
+
? path.resolve(previous.session_file)
|
|
803
|
+
: null;
|
|
804
|
+
const previousSessionReal = previousSession &&
|
|
805
|
+
fs.statSync(previousSession, { throwIfNoEntry: false })?.isFile()
|
|
806
|
+
? fs.realpathSync(previousSession)
|
|
807
|
+
: null;
|
|
808
|
+
const sessionRootReal = fs.existsSync(sessionRoot)
|
|
809
|
+
? fs.realpathSync(sessionRoot)
|
|
810
|
+
: null;
|
|
811
|
+
const relativeSession = previousSessionReal && sessionRootReal
|
|
812
|
+
? path.relative(sessionRootReal, previousSessionReal)
|
|
813
|
+
: null;
|
|
814
|
+
const insideSessions = relativeSession &&
|
|
815
|
+
relativeSession !== ".." &&
|
|
816
|
+
!relativeSession.startsWith(`..${path.sep}`) &&
|
|
817
|
+
!path.isAbsolute(relativeSession);
|
|
818
|
+
if (input.resume &&
|
|
819
|
+
!(previous?.role === input.role &&
|
|
820
|
+
insideSessions &&
|
|
821
|
+
previousSession &&
|
|
822
|
+
fs.statSync(previousSession, { throwIfNoEntry: false })?.isFile())) {
|
|
230
823
|
throw new Error("No previous Pi session is available to resume");
|
|
231
824
|
}
|
|
232
825
|
const runId = randomUUID();
|
|
826
|
+
const startRequestId = dispatch || review ? randomUUID() : null;
|
|
233
827
|
const now = new Date();
|
|
828
|
+
const runFile = path.join(evidence, "runs", `${runId}.json`);
|
|
829
|
+
const eventFile = path.join(evidence, "events", `${runId}.jsonl`);
|
|
830
|
+
const runReceiptRef = path.relative(dir, runFile).replaceAll("\\", "/");
|
|
831
|
+
const progressEvidenceRef = path
|
|
832
|
+
.relative(dir, eventFile)
|
|
833
|
+
.replaceAll("\\", "/");
|
|
834
|
+
const startReceiptRef = `pi-bridge/starts/${runId}.json`;
|
|
835
|
+
const reviewStopReceiptRef = `pi-bridge/stops/${runId}.json`;
|
|
836
|
+
const reviewAttemptsRef = `pi-bridge/review-attempts/${runId}.jsonl`;
|
|
837
|
+
const reviewSessionEvidenceId = review
|
|
838
|
+
? `pirc-${createHash("sha256").update(runId).digest("hex").slice(0, 32)}`
|
|
839
|
+
: null;
|
|
234
840
|
const record = {
|
|
235
|
-
schema_version:
|
|
236
|
-
|
|
841
|
+
schema_version: dispatch ? 2 : 1,
|
|
842
|
+
run_id: runId,
|
|
843
|
+
task: path.relative(this.root, dir).replaceAll("\\", "/"),
|
|
844
|
+
role: input.role,
|
|
845
|
+
outcome: "running",
|
|
846
|
+
started_at: now.toISOString(),
|
|
847
|
+
ended_at: null,
|
|
237
848
|
prompt_sha256: createHash("sha256").update(input.prompt).digest("hex"),
|
|
238
|
-
session_id: null,
|
|
239
|
-
|
|
849
|
+
session_id: null,
|
|
850
|
+
session_file: null,
|
|
851
|
+
process_mode: "cold",
|
|
852
|
+
startup_ms: 0,
|
|
853
|
+
first_event_ms: null,
|
|
854
|
+
elapsed_ms: null,
|
|
855
|
+
event_count: 0,
|
|
856
|
+
tool_errors: 0,
|
|
857
|
+
reason: null,
|
|
858
|
+
result_file: null,
|
|
859
|
+
...(review
|
|
860
|
+
? {
|
|
861
|
+
task_id: review.run.taskId,
|
|
862
|
+
task_run_id: review.run.id,
|
|
863
|
+
candidate_snapshot_id: review.run.candidateSnapshot?.id ?? null,
|
|
864
|
+
candidate_fingerprint: review.run.candidateSnapshot?.fingerprint ?? null,
|
|
865
|
+
kernel_revision_at_dispatch: review.kernel.revision,
|
|
866
|
+
reviewer_identity_assurance: "caller-declared",
|
|
867
|
+
start_request_id: startRequestId,
|
|
868
|
+
process_id: null,
|
|
869
|
+
progress_evidence_ref: progressEvidenceRef,
|
|
870
|
+
result_sha256: null,
|
|
871
|
+
result_redacted: false,
|
|
872
|
+
review_attempts_ref: reviewAttemptsRef,
|
|
873
|
+
review_attempts_sha256: null,
|
|
874
|
+
review_format_correction: null,
|
|
875
|
+
review_status: "pending",
|
|
876
|
+
review_start_receipt_ref: null,
|
|
877
|
+
review_stop_receipt_ref: null,
|
|
878
|
+
review_file: null,
|
|
879
|
+
kernel_review_id: null,
|
|
880
|
+
codex_escalation: null,
|
|
881
|
+
codex_escalation_request_ref: null,
|
|
882
|
+
codex_escalation_request_status: "pending",
|
|
883
|
+
}
|
|
884
|
+
: {}),
|
|
885
|
+
...(dispatch
|
|
886
|
+
? {
|
|
887
|
+
task_id: dispatch.taskId,
|
|
888
|
+
task_run_id: dispatch.runId,
|
|
889
|
+
task_host_id: "pi",
|
|
890
|
+
start_request_id: startRequestId,
|
|
891
|
+
process_id: null,
|
|
892
|
+
host_start_receipt_ref: null,
|
|
893
|
+
progress_evidence_ref: progressEvidenceRef,
|
|
894
|
+
settle_receipt_id: null,
|
|
895
|
+
process_stop_receipt: null,
|
|
896
|
+
process_stop_error: null,
|
|
897
|
+
process_exit_receipt: null,
|
|
898
|
+
result_sha256: null,
|
|
899
|
+
result_redacted: false,
|
|
900
|
+
dispatch_lease_id: dispatch.leaseId,
|
|
901
|
+
schedule_receipt_fingerprint: dispatch.scheduleReceiptFingerprint,
|
|
902
|
+
admission_receipt_fingerprint: dispatch.admissionReceiptFingerprint,
|
|
903
|
+
dispatch_stop_proof_ref: null,
|
|
904
|
+
dispatch_lease_released: false,
|
|
905
|
+
dispatch_lease_release_reason: null,
|
|
906
|
+
}
|
|
907
|
+
: {}),
|
|
240
908
|
};
|
|
241
|
-
const runFile = path.join(evidence, "runs", `${runId}.json`);
|
|
242
|
-
const eventFile = path.join(evidence, "events", `${runId}.jsonl`);
|
|
243
909
|
fs.mkdirSync(path.dirname(eventFile), { recursive: true });
|
|
910
|
+
if (dispatch || review)
|
|
911
|
+
fs.writeFileSync(eventFile, "", { flag: "wx", mode: 0o600 });
|
|
912
|
+
if (review) {
|
|
913
|
+
const attemptsFile = path.join(dir, reviewAttemptsRef);
|
|
914
|
+
fs.mkdirSync(path.dirname(attemptsFile), { recursive: true });
|
|
915
|
+
fs.writeFileSync(attemptsFile, "", { flag: "wx", mode: 0o600 });
|
|
916
|
+
}
|
|
244
917
|
fs.writeFileSync(lockFile, JSON.stringify({ parent_pid: process.pid, run_id: runId }), { flag: "wx", mode: 0o600 });
|
|
245
918
|
this.locked = true;
|
|
246
919
|
atomicJson(runFile, record);
|
|
247
920
|
atomicJson(latestFile, record);
|
|
248
921
|
const started = performance.now();
|
|
922
|
+
const checkDeadlineAt = started + input.timeoutMs;
|
|
923
|
+
let activeReviewSessionId = null;
|
|
924
|
+
const remainingCheckMs = () => Math.floor(checkDeadlineAt - performance.now());
|
|
925
|
+
const reviewResponseEvidence = [];
|
|
249
926
|
let detach = () => undefined;
|
|
250
927
|
let closeFailed = false;
|
|
251
928
|
const controller = new AbortController();
|
|
@@ -254,78 +931,570 @@ export class PiTaskBridge {
|
|
|
254
931
|
const onAbort = () => controller.abort();
|
|
255
932
|
input.signal?.addEventListener("abort", onAbort, { once: true });
|
|
256
933
|
const pollCancel = setInterval(() => {
|
|
257
|
-
|
|
934
|
+
const cancellation = parseJson(cancelFile);
|
|
935
|
+
if (cancellation?.run_id === runId) {
|
|
936
|
+
if (review && typeof cancellation.request_id === "string") {
|
|
937
|
+
record.cancellation_request_id = cancellation.request_id;
|
|
938
|
+
atomicJson(runFile, record);
|
|
939
|
+
atomicJson(latestFile, record);
|
|
940
|
+
}
|
|
258
941
|
controller.abort();
|
|
942
|
+
}
|
|
259
943
|
}, 200);
|
|
944
|
+
const checkCancellationNow = (message) => {
|
|
945
|
+
const cancellation = parseJson(cancelFile);
|
|
946
|
+
if (cancellation?.run_id === runId) {
|
|
947
|
+
if (review && typeof cancellation.request_id === "string") {
|
|
948
|
+
record.cancellation_request_id = cancellation.request_id;
|
|
949
|
+
atomicJson(runFile, record);
|
|
950
|
+
atomicJson(latestFile, record);
|
|
951
|
+
}
|
|
952
|
+
controller.abort();
|
|
953
|
+
}
|
|
954
|
+
if (controller.signal.aborted)
|
|
955
|
+
throw new Error(message);
|
|
956
|
+
};
|
|
260
957
|
try {
|
|
261
958
|
const resumeFile = input.resume ? previousSession : null;
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
959
|
+
if (dispatch) {
|
|
960
|
+
recheckPiV2RunDispatchWorkspace(dispatch);
|
|
961
|
+
}
|
|
962
|
+
const startupTimeoutMs = review ? remainingCheckMs() : undefined;
|
|
963
|
+
if (review && (startupTimeoutMs ?? 0) <= 0)
|
|
964
|
+
throw new Error("Pi Check timeout exhausted before startup");
|
|
965
|
+
const { client, startupMs, mode } = await this.ensureClient(dir, workdir, input.role, resumeFile, dispatch
|
|
966
|
+
? () => {
|
|
967
|
+
const verifiedWorkdir = recheckPiV2RunDispatchBeforeSpawn(dispatch);
|
|
968
|
+
if (verifiedWorkdir !== workdir) {
|
|
969
|
+
throw new Error("Pi V2 Run worktree changed immediately before process start");
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
: undefined, review ? path.join(evidence, "review-sessions", runId) : undefined, startupTimeoutMs);
|
|
973
|
+
if (parallelLeaseId)
|
|
974
|
+
updateParallelChildPid(this.root, dir, parallelLeaseId, client.pid);
|
|
975
|
+
fs.writeFileSync(lockFile, JSON.stringify({
|
|
976
|
+
parent_pid: process.pid,
|
|
977
|
+
child_pid: client.pid,
|
|
978
|
+
run_id: runId,
|
|
979
|
+
}), "utf8");
|
|
265
980
|
record.process_mode = mode;
|
|
266
981
|
record.startup_ms = startupMs;
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
|
|
982
|
+
const initialStateBudgetMs = review ? remainingCheckMs() : undefined;
|
|
983
|
+
if (review && (initialStateBudgetMs ?? 0) <= 0)
|
|
984
|
+
throw new Error("Pi Check timeout exhausted before session verification");
|
|
985
|
+
const state = await client.state(initialStateBudgetMs);
|
|
986
|
+
if (review && remainingCheckMs() <= 0)
|
|
987
|
+
throw new Error("Pi Check timeout exhausted during session verification");
|
|
988
|
+
activeReviewSessionId =
|
|
989
|
+
typeof state.sessionId === "string" && state.sessionId.length > 0
|
|
990
|
+
? state.sessionId
|
|
991
|
+
: null;
|
|
992
|
+
record.session_id = review
|
|
993
|
+
? activeReviewSessionId
|
|
994
|
+
? reviewSessionEvidenceId
|
|
995
|
+
: null
|
|
996
|
+
: typeof state.sessionId === "string"
|
|
997
|
+
? state.sessionId
|
|
998
|
+
: null;
|
|
999
|
+
record.session_file = review
|
|
1000
|
+
? null
|
|
1001
|
+
: typeof state.sessionFile === "string"
|
|
1002
|
+
? state.sessionFile
|
|
1003
|
+
: null;
|
|
270
1004
|
atomicJson(runFile, record);
|
|
271
1005
|
atomicJson(latestFile, record);
|
|
1006
|
+
if (review) {
|
|
1007
|
+
if (!startRequestId ||
|
|
1008
|
+
!record.session_id ||
|
|
1009
|
+
!Number.isSafeInteger(client.pid) ||
|
|
1010
|
+
!client.pid) {
|
|
1011
|
+
throw new Error("Pi Review Check start did not provide process and session identities");
|
|
1012
|
+
}
|
|
1013
|
+
if (activeReviewSessionId === review.run.host?.sessionId) {
|
|
1014
|
+
throw new Error("Pi Check session matches the implementation Run host session");
|
|
1015
|
+
}
|
|
1016
|
+
const currentReview = preparePiReviewRoute(this.root, review.run.taskId);
|
|
1017
|
+
assertSameReviewBinding(currentReview.binding, review.binding);
|
|
1018
|
+
const startReceipt = {
|
|
1019
|
+
schemaVersion: 1,
|
|
1020
|
+
source: "pactile-pi-review",
|
|
1021
|
+
taskId: review.run.taskId,
|
|
1022
|
+
taskRunId: review.run.id,
|
|
1023
|
+
piRunId: runId,
|
|
1024
|
+
role: "check",
|
|
1025
|
+
sessionId: record.session_id,
|
|
1026
|
+
processId: client.pid,
|
|
1027
|
+
startRequestId,
|
|
1028
|
+
kernelRevision: review.kernel.revision,
|
|
1029
|
+
candidateSnapshotId: review.binding.candidateSnapshotId,
|
|
1030
|
+
candidateFingerprint: review.binding.candidateFingerprint,
|
|
1031
|
+
progressEvidenceRef,
|
|
1032
|
+
evidenceRef: runReceiptRef,
|
|
1033
|
+
recordedAt: new Date().toISOString(),
|
|
1034
|
+
};
|
|
1035
|
+
writeExclusiveJson(path.join(dir, startReceiptRef), startReceipt);
|
|
1036
|
+
record.process_id = client.pid;
|
|
1037
|
+
record.start_request_id = startRequestId;
|
|
1038
|
+
record.reviewer_id = `pi-session:${record.session_id}`;
|
|
1039
|
+
record.review_start_receipt_ref = startReceiptRef;
|
|
1040
|
+
atomicJson(runFile, record);
|
|
1041
|
+
atomicJson(latestFile, record);
|
|
1042
|
+
}
|
|
1043
|
+
if (dispatch) {
|
|
1044
|
+
if (!startRequestId ||
|
|
1045
|
+
!record.session_id ||
|
|
1046
|
+
!Number.isSafeInteger(client.pid) ||
|
|
1047
|
+
!client.pid) {
|
|
1048
|
+
throw new Error("Pi V2 host start did not provide a process and session identity");
|
|
1049
|
+
}
|
|
1050
|
+
const startReceipt = {
|
|
1051
|
+
schemaVersion: 1,
|
|
1052
|
+
source: "pactile-pi-rpc",
|
|
1053
|
+
taskId: dispatch.taskId,
|
|
1054
|
+
taskRunId: dispatch.runId,
|
|
1055
|
+
piRunId: runId,
|
|
1056
|
+
role: "implement",
|
|
1057
|
+
sessionId: record.session_id,
|
|
1058
|
+
processId: client.pid,
|
|
1059
|
+
startRequestId,
|
|
1060
|
+
progressEvidenceRef,
|
|
1061
|
+
evidenceRef: runReceiptRef,
|
|
1062
|
+
recordedAt: new Date().toISOString(),
|
|
1063
|
+
};
|
|
1064
|
+
const startFile = path.join(dir, startReceiptRef);
|
|
1065
|
+
fs.mkdirSync(path.dirname(startFile), { recursive: true });
|
|
1066
|
+
fs.writeFileSync(startFile, `${JSON.stringify(startReceipt, null, 2)}\n`, {
|
|
1067
|
+
encoding: "utf8",
|
|
1068
|
+
flag: "wx",
|
|
1069
|
+
mode: 0o600,
|
|
1070
|
+
});
|
|
1071
|
+
record.process_id = client.pid;
|
|
1072
|
+
record.host_start_receipt_ref = startReceiptRef;
|
|
1073
|
+
atomicJson(runFile, record);
|
|
1074
|
+
atomicJson(latestFile, record);
|
|
1075
|
+
bindPiV2RunHost(dispatch, {
|
|
1076
|
+
piRunId: runId,
|
|
1077
|
+
startRequestId,
|
|
1078
|
+
sessionId: record.session_id,
|
|
1079
|
+
processId: client.pid,
|
|
1080
|
+
progressEvidenceRef,
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
272
1083
|
detach = client.onEvent((event) => {
|
|
273
1084
|
if (event.type === "transport_error")
|
|
274
1085
|
return;
|
|
275
1086
|
record.event_count += 1;
|
|
276
1087
|
if (event.type === "tool_execution_end" && event.isError === true)
|
|
277
1088
|
record.tool_errors += 1;
|
|
278
|
-
if (event.type === "extension_error" ||
|
|
1089
|
+
if (event.type === "extension_error" ||
|
|
1090
|
+
(event.type === "auto_retry_end" && event.success === false))
|
|
279
1091
|
record.tool_errors += 1;
|
|
280
1092
|
const summary = evidenceEvent(event);
|
|
281
|
-
fs.appendFileSync(eventFile, `${JSON.stringify(summary)}\n`, {
|
|
1093
|
+
fs.appendFileSync(eventFile, `${JSON.stringify(summary)}\n`, {
|
|
1094
|
+
encoding: "utf8",
|
|
1095
|
+
mode: 0o600,
|
|
1096
|
+
});
|
|
282
1097
|
try {
|
|
283
1098
|
input.onProgress?.(summary);
|
|
284
1099
|
}
|
|
285
|
-
catch {
|
|
1100
|
+
catch {
|
|
1101
|
+
/* Observers cannot change the run outcome. */
|
|
1102
|
+
}
|
|
286
1103
|
});
|
|
287
1104
|
const taskPath = dir;
|
|
288
1105
|
const contractPath = path.join(dir, "implement.md");
|
|
289
|
-
const contractExcerpt = fs.existsSync(contractPath)
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
const
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
1106
|
+
const contractExcerpt = fs.existsSync(contractPath)
|
|
1107
|
+
? fs.readFileSync(contractPath, "utf8").slice(0, 1_200)
|
|
1108
|
+
: "(no implement.md)";
|
|
1109
|
+
const effectiveTouches = touches ??
|
|
1110
|
+
(dispatch
|
|
1111
|
+
? [
|
|
1112
|
+
...new Set([
|
|
1113
|
+
...dispatch.run.writeSetSnapshot,
|
|
1114
|
+
...(dispatch.run.workspace?.writeSet ?? []),
|
|
1115
|
+
]),
|
|
1116
|
+
]
|
|
1117
|
+
: null);
|
|
1118
|
+
const writeSetLabel = dispatch
|
|
1119
|
+
? "Task Run write set"
|
|
1120
|
+
: "Parent-declared write set";
|
|
1121
|
+
const instructions = review
|
|
1122
|
+
? [
|
|
1123
|
+
"Task-specific Review instructions:",
|
|
1124
|
+
fs.readFileSync(path.join(dir, "verify.md"), "utf8"),
|
|
1125
|
+
"Additional caller instructions:",
|
|
1126
|
+
input.prompt,
|
|
1127
|
+
buildIndependentPiReviewPrompt(review.binding),
|
|
1128
|
+
].join("\n\n")
|
|
1129
|
+
: [
|
|
1130
|
+
`Pactile task: ${taskPath}`,
|
|
1131
|
+
`Execution worktree: ${workdir}`,
|
|
1132
|
+
`Role: ${input.role}`,
|
|
1133
|
+
`Definition: ${taskPath}/prd.md`,
|
|
1134
|
+
`Evidence: ${taskPath}/verify.md`,
|
|
1135
|
+
"Approved execution contract excerpt:",
|
|
1136
|
+
contractExcerpt,
|
|
1137
|
+
"Follow the approved task contract and its write set. Do not commit, archive, finalize, or mutate Pactile Kernel state. Report evidence and unresolved issues. Do not include credentials in the final answer.",
|
|
1138
|
+
effectiveTouches?.length
|
|
1139
|
+
? `${writeSetLabel}: ${effectiveTouches.join(", ")}. Do not change files outside it.`
|
|
1140
|
+
: "",
|
|
1141
|
+
input.role === "implement"
|
|
1142
|
+
? "Implementation may change files only inside the approved write set."
|
|
1143
|
+
: "This role is read-only; do not change files.",
|
|
1144
|
+
"Worker assignment:",
|
|
1145
|
+
input.prompt,
|
|
1146
|
+
].join("\n\n");
|
|
308
1147
|
const resultFile = path.join(evidence, "results", `${runId}.md`);
|
|
309
1148
|
fs.mkdirSync(path.dirname(resultFile), { recursive: true });
|
|
310
|
-
fs.writeFileSync(resultFile, `${text || "(Pi returned no assistant text.)"}\n`, { encoding: "utf8", mode: 0o600 });
|
|
311
1149
|
record.result_file = path.relative(dir, resultFile).replaceAll("\\", "/");
|
|
1150
|
+
const attemptsFile = review ? path.join(dir, reviewAttemptsRef) : null;
|
|
1151
|
+
const appendAttempt = (summary) => {
|
|
1152
|
+
if (!attemptsFile)
|
|
1153
|
+
return;
|
|
1154
|
+
fs.appendFileSync(attemptsFile, `${JSON.stringify(summary)}\n`, {
|
|
1155
|
+
encoding: "utf8",
|
|
1156
|
+
mode: 0o600,
|
|
1157
|
+
});
|
|
1158
|
+
};
|
|
1159
|
+
const persistReviewResponse = (attempt, response) => {
|
|
1160
|
+
if (!review)
|
|
1161
|
+
return null;
|
|
1162
|
+
const bytes = Buffer.from(response.text, "utf8");
|
|
1163
|
+
if (bytes.byteLength > MAX_PI_REVIEW_RESPONSE_BYTES)
|
|
1164
|
+
return null;
|
|
1165
|
+
const ref = `pi-bridge/review-responses/${runId}-attempt-${attempt}.txt`;
|
|
1166
|
+
const file = path.join(dir, ref);
|
|
1167
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
1168
|
+
fs.writeFileSync(file, bytes, { flag: "wx", mode: 0o600 });
|
|
1169
|
+
const stored = {
|
|
1170
|
+
ref,
|
|
1171
|
+
sha256: createHash("sha256").update(bytes).digest("hex"),
|
|
1172
|
+
sizeBytes: bytes.byteLength,
|
|
1173
|
+
};
|
|
1174
|
+
reviewResponseEvidence.push(stored);
|
|
1175
|
+
return stored;
|
|
1176
|
+
};
|
|
1177
|
+
let text = "";
|
|
1178
|
+
let redacted = false;
|
|
1179
|
+
const persistResult = () => {
|
|
1180
|
+
const responseTooLarge = review !== null &&
|
|
1181
|
+
Buffer.byteLength(text, "utf8") > MAX_PI_REVIEW_RESPONSE_BYTES;
|
|
1182
|
+
const resultText = responseTooLarge
|
|
1183
|
+
? "(Pi Check response omitted because it exceeded the bounded evidence limit.)"
|
|
1184
|
+
: text || "(Pi returned no assistant text.)";
|
|
1185
|
+
fs.writeFileSync(resultFile, `${resultText}\n`, { encoding: "utf8", mode: 0o600 });
|
|
1186
|
+
if (dispatch || review) {
|
|
1187
|
+
const resultBytes = fs.readFileSync(resultFile);
|
|
1188
|
+
record.result_sha256 = createHash("sha256")
|
|
1189
|
+
.update(resultBytes)
|
|
1190
|
+
.digest("hex");
|
|
1191
|
+
record.result_redacted = redacted || responseTooLarge;
|
|
1192
|
+
}
|
|
1193
|
+
};
|
|
1194
|
+
const firstPromptStarted = performance.now();
|
|
1195
|
+
const firstPromptRemainingMs = remainingCheckMs();
|
|
1196
|
+
if (review && firstPromptRemainingMs <= 0)
|
|
1197
|
+
throw new Error("Pi Check timeout exhausted before first prompt");
|
|
1198
|
+
const result = await client.prompt(instructions, review ? firstPromptRemainingMs : input.timeoutMs, controller.signal, undefined, review ? checkDeadlineAt : undefined, review
|
|
1199
|
+
? () => checkCancellationNow("Pi Check cancelled before prompt dispatch")
|
|
1200
|
+
: undefined);
|
|
1201
|
+
record.first_event_ms = result.firstEventMs;
|
|
1202
|
+
let response = assistantText(result.event);
|
|
1203
|
+
const firstResponseEvidence = review
|
|
1204
|
+
? persistReviewResponse(1, response)
|
|
1205
|
+
: null;
|
|
1206
|
+
text = response.text;
|
|
1207
|
+
redacted = response.redacted;
|
|
1208
|
+
record.outcome =
|
|
1209
|
+
response.stopReason === "stop" && text && !record.tool_errors
|
|
1210
|
+
? "settled"
|
|
1211
|
+
: "needs_review";
|
|
1212
|
+
if (response.stopReason && response.stopReason !== "stop")
|
|
1213
|
+
record.reason = `Pi stopReason=${response.stopReason}${response.errorMessage ? `; ${response.errorMessage}` : ""}`;
|
|
1214
|
+
if (review) {
|
|
1215
|
+
appendAttempt(reviewAttemptSummary(1, "initial", firstPromptStarted, response, result.firstEventMs, undefined, firstResponseEvidence?.ref ?? null));
|
|
1216
|
+
record.review_format_correction = {
|
|
1217
|
+
attempted: false,
|
|
1218
|
+
outcome: "not-needed",
|
|
1219
|
+
};
|
|
1220
|
+
}
|
|
1221
|
+
persistResult();
|
|
1222
|
+
if (review && !firstResponseEvidence) {
|
|
1223
|
+
record.review_format_correction = {
|
|
1224
|
+
attempted: false,
|
|
1225
|
+
outcome: "failed",
|
|
1226
|
+
};
|
|
1227
|
+
throw new Error("Pi Check response exceeds the bounded Review evidence limit");
|
|
1228
|
+
}
|
|
1229
|
+
if (review &&
|
|
1230
|
+
record.outcome === "settled" &&
|
|
1231
|
+
!isStructuredJsonObject(response.rawText)) {
|
|
1232
|
+
record.review_format_correction = {
|
|
1233
|
+
attempted: false,
|
|
1234
|
+
outcome: "failed",
|
|
1235
|
+
};
|
|
1236
|
+
const currentReview = preparePiReviewRoute(this.root, review.run.taskId);
|
|
1237
|
+
assertSameReviewBinding(currentReview.binding, review.binding);
|
|
1238
|
+
resolvePiReviewEvidenceV1({
|
|
1239
|
+
root: this.root,
|
|
1240
|
+
taskDir: currentReview.taskDir,
|
|
1241
|
+
candidateRoot: currentReview.workdir,
|
|
1242
|
+
run: currentReview.run,
|
|
1243
|
+
references: [],
|
|
1244
|
+
});
|
|
1245
|
+
checkCancellationNow("Pi run cancelled before format correction");
|
|
1246
|
+
const preflightBudgetMs = remainingCheckMs();
|
|
1247
|
+
if (preflightBudgetMs <= 0)
|
|
1248
|
+
throw new Error("Pi Review format correction exceeded the Check timeout");
|
|
1249
|
+
let correctionPromptInvoked = false;
|
|
1250
|
+
let correctionStarted = performance.now();
|
|
1251
|
+
let correctionResponse;
|
|
1252
|
+
try {
|
|
1253
|
+
const beforeCorrection = await client.state(preflightBudgetMs);
|
|
1254
|
+
if (remainingCheckMs() <= 0)
|
|
1255
|
+
throw new Error("Pi Review format correction exceeded the Check timeout");
|
|
1256
|
+
if (beforeCorrection.sessionId !== activeReviewSessionId)
|
|
1257
|
+
throw new Error("Pi Review Check session changed before format correction");
|
|
1258
|
+
checkCancellationNow("Pi run cancelled before format correction");
|
|
1259
|
+
const correctionPrompt = [
|
|
1260
|
+
"Follow the complete original Check instructions, including Task-specific Review instructions, caller instructions, and Review/Close contract. Reuse the exact original instructions below.",
|
|
1261
|
+
instructions,
|
|
1262
|
+
"Format correction for the same independent Pi Check session.",
|
|
1263
|
+
"Your previous settled answer was not one structured JSON object.",
|
|
1264
|
+
"Return exactly one JSON object matching the Review contract below.",
|
|
1265
|
+
"Do not include prose, Markdown fences, prefixes, suffixes, or a JSON substring embedded in text.",
|
|
1266
|
+
"Keep the same frozen Task Run, candidate snapshot, reviewer identity, evidence boundary, and Review/Close guards.",
|
|
1267
|
+
"Use only the evidence already bound to this Check. Do not call tools or assume tools are available; if the bound evidence is insufficient, return needs-changes.",
|
|
1268
|
+
].join("\n\n");
|
|
1269
|
+
const promptBudgetMs = remainingCheckMs();
|
|
1270
|
+
if (promptBudgetMs <= 0)
|
|
1271
|
+
throw new Error("Pi Review format correction exceeded the Check timeout");
|
|
1272
|
+
const correction = await client.prompt(correctionPrompt, promptBudgetMs, controller.signal, () => {
|
|
1273
|
+
correctionPromptInvoked = true;
|
|
1274
|
+
}, checkDeadlineAt, () => {
|
|
1275
|
+
checkCancellationNow("Pi run cancelled before format correction");
|
|
1276
|
+
correctionStarted = performance.now();
|
|
1277
|
+
});
|
|
1278
|
+
correctionResponse = assistantText(correction.event);
|
|
1279
|
+
const correctionResponseEvidence = persistReviewResponse(2, correctionResponse);
|
|
1280
|
+
const finalStateBudgetMs = remainingCheckMs();
|
|
1281
|
+
if (finalStateBudgetMs <= 0)
|
|
1282
|
+
throw new Error("Pi Review format correction exceeded the Check timeout");
|
|
1283
|
+
const afterCorrection = await client.state(finalStateBudgetMs);
|
|
1284
|
+
if (remainingCheckMs() <= 0)
|
|
1285
|
+
throw new Error("Pi Review format correction exceeded the Check timeout");
|
|
1286
|
+
if (afterCorrection.sessionId !== activeReviewSessionId)
|
|
1287
|
+
throw new Error("Pi Review Check session changed after format correction");
|
|
1288
|
+
response = correctionResponse;
|
|
1289
|
+
text = response.text;
|
|
1290
|
+
redacted = response.redacted;
|
|
1291
|
+
appendAttempt(reviewAttemptSummary(2, "format-correction", correctionStarted, response, correction.firstEventMs, undefined, correctionResponseEvidence?.ref ?? null));
|
|
1292
|
+
record.outcome =
|
|
1293
|
+
response.stopReason === "stop" && text && !record.tool_errors
|
|
1294
|
+
? "settled"
|
|
1295
|
+
: "needs_review";
|
|
1296
|
+
if (response.stopReason && response.stopReason !== "stop")
|
|
1297
|
+
record.reason = `Pi stopReason=${response.stopReason}${response.errorMessage ? `; ${response.errorMessage}` : ""}`;
|
|
1298
|
+
record.review_format_correction = {
|
|
1299
|
+
attempted: true,
|
|
1300
|
+
outcome: isStructuredJsonObject(response.rawText)
|
|
1301
|
+
? "accepted"
|
|
1302
|
+
: "rejected",
|
|
1303
|
+
};
|
|
1304
|
+
persistResult();
|
|
1305
|
+
}
|
|
1306
|
+
catch (error) {
|
|
1307
|
+
const correctionError = error instanceof Error ? error.message : String(error);
|
|
1308
|
+
record.review_format_correction = {
|
|
1309
|
+
attempted: correctionPromptInvoked,
|
|
1310
|
+
outcome: controller.signal.aborted || correctionError.includes("cancelled")
|
|
1311
|
+
? "cancelled"
|
|
1312
|
+
: correctionError.includes("timed out")
|
|
1313
|
+
? correctionPromptInvoked
|
|
1314
|
+
? "timed-out"
|
|
1315
|
+
: "deadline-exceeded"
|
|
1316
|
+
: "failed",
|
|
1317
|
+
};
|
|
1318
|
+
if (correctionPromptInvoked) {
|
|
1319
|
+
appendAttempt(reviewAttemptSummary(2, "format-correction", correctionStarted, correctionResponse
|
|
1320
|
+
? {
|
|
1321
|
+
...correctionResponse,
|
|
1322
|
+
errorMessage: safeErrorSummary(correctionError),
|
|
1323
|
+
}
|
|
1324
|
+
: undefined, null, correctionResponse ? undefined : correctionError, reviewResponseEvidence.find((item) => item.ref ===
|
|
1325
|
+
`pi-bridge/review-responses/${runId}-attempt-2.txt`)?.ref ?? null));
|
|
1326
|
+
}
|
|
1327
|
+
throw error;
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
312
1330
|
}
|
|
313
1331
|
catch (error) {
|
|
314
1332
|
const reason = error instanceof Error ? error.message : String(error);
|
|
315
|
-
record.outcome =
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
1333
|
+
record.outcome =
|
|
1334
|
+
controller.signal.aborted || reason.includes("cancelled")
|
|
1335
|
+
? "cancelled"
|
|
1336
|
+
: reason.includes("timed out")
|
|
1337
|
+
? "timed_out"
|
|
1338
|
+
: reason.includes("exited")
|
|
1339
|
+
? "interrupted"
|
|
1340
|
+
: "failed";
|
|
1341
|
+
record.reason = review ? safeErrorSummary(reason) : safeProviderMessage(reason);
|
|
1342
|
+
if (!dispatch && !review) {
|
|
1343
|
+
try {
|
|
1344
|
+
await this.close();
|
|
1345
|
+
}
|
|
1346
|
+
catch (closeError) {
|
|
1347
|
+
closeFailed = true;
|
|
1348
|
+
record.reason = `${record.reason}; Pi termination failed: ${safeProviderMessage(closeError instanceof Error ? closeError.message : String(closeError))}`;
|
|
1349
|
+
}
|
|
323
1350
|
}
|
|
324
1351
|
}
|
|
325
1352
|
finally {
|
|
326
1353
|
detach();
|
|
327
1354
|
clearInterval(pollCancel);
|
|
328
1355
|
input.signal?.removeEventListener("abort", onAbort);
|
|
1356
|
+
let processExit = null;
|
|
1357
|
+
let reviewProcessExit = null;
|
|
1358
|
+
if (review) {
|
|
1359
|
+
try {
|
|
1360
|
+
reviewProcessExit = (await this.closeForReview()) ?? null;
|
|
1361
|
+
}
|
|
1362
|
+
catch (closeError) {
|
|
1363
|
+
closeFailed = true;
|
|
1364
|
+
const closeReason = closeError instanceof Error ? closeError.message : String(closeError);
|
|
1365
|
+
record.process_stop_error = review
|
|
1366
|
+
? safeErrorSummary(closeReason)
|
|
1367
|
+
: safeProviderMessage(closeReason);
|
|
1368
|
+
}
|
|
1369
|
+
if (record.result_sha256 === null) {
|
|
1370
|
+
// A cancelled Check may stop before the first response is persisted.
|
|
1371
|
+
record.result_file = null;
|
|
1372
|
+
}
|
|
1373
|
+
if (reviewProcessExit?.terminationVerified &&
|
|
1374
|
+
reviewProcessExit.exitObservedAt &&
|
|
1375
|
+
startRequestId &&
|
|
1376
|
+
record.session_id &&
|
|
1377
|
+
Number.isSafeInteger(record.process_id) &&
|
|
1378
|
+
record.process_id) {
|
|
1379
|
+
if (record.outcome === "settled" &&
|
|
1380
|
+
(reviewProcessExit.exitCode !== 0 ||
|
|
1381
|
+
reviewProcessExit.signalCode !== null)) {
|
|
1382
|
+
record.outcome = "interrupted";
|
|
1383
|
+
record.reason =
|
|
1384
|
+
"Pi reported a settled Review response but its manager-owned process exited abnormally";
|
|
1385
|
+
}
|
|
1386
|
+
const reviewAttemptsBytes = fs.readFileSync(path.join(dir, reviewAttemptsRef));
|
|
1387
|
+
const reviewAttemptsSha256 = createHash("sha256")
|
|
1388
|
+
.update(reviewAttemptsBytes)
|
|
1389
|
+
.digest("hex");
|
|
1390
|
+
record.review_attempts_sha256 = reviewAttemptsSha256;
|
|
1391
|
+
const receipt = {
|
|
1392
|
+
schemaVersion: 1,
|
|
1393
|
+
source: "pactile-pi-review",
|
|
1394
|
+
assurance: "manager-owned-child-exit",
|
|
1395
|
+
taskId: review.run.taskId,
|
|
1396
|
+
taskRunId: review.run.id,
|
|
1397
|
+
piRunId: runId,
|
|
1398
|
+
role: "check",
|
|
1399
|
+
sessionId: record.session_id,
|
|
1400
|
+
processId: record.process_id,
|
|
1401
|
+
startRequestId,
|
|
1402
|
+
startReceiptRef,
|
|
1403
|
+
stopReceiptId: `review-stop-${runId}`,
|
|
1404
|
+
terminal: record.outcome === "cancelled" ? "cancelled" : "exited",
|
|
1405
|
+
exitCode: reviewProcessExit.exitCode,
|
|
1406
|
+
signalCode: reviewProcessExit.signalCode,
|
|
1407
|
+
cancellationRequestId: record.cancellation_request_id ?? null,
|
|
1408
|
+
evidenceRef: runReceiptRef,
|
|
1409
|
+
progressEvidenceRef,
|
|
1410
|
+
progressEvidenceSha256: createHash("sha256")
|
|
1411
|
+
.update(fs.readFileSync(eventFile))
|
|
1412
|
+
.digest("hex"),
|
|
1413
|
+
resultRef: record.result_file,
|
|
1414
|
+
resultSha256: record.result_sha256 ?? null,
|
|
1415
|
+
reviewAttemptsRef,
|
|
1416
|
+
reviewAttemptsSha256,
|
|
1417
|
+
reviewResponses: reviewResponseEvidence,
|
|
1418
|
+
recordedAt: new Date().toISOString(),
|
|
1419
|
+
processExit: reviewProcessExit,
|
|
1420
|
+
};
|
|
1421
|
+
writeExclusiveJson(path.join(dir, reviewStopReceiptRef), receipt);
|
|
1422
|
+
record.review_stop_receipt_ref = reviewStopReceiptRef;
|
|
1423
|
+
record.process_stop_error = null;
|
|
1424
|
+
}
|
|
1425
|
+
else {
|
|
1426
|
+
closeFailed = true;
|
|
1427
|
+
record.process_stop_error ??=
|
|
1428
|
+
"Pi Check process termination was not verified; Review is not recordable";
|
|
1429
|
+
if (record.outcome === "settled" ||
|
|
1430
|
+
record.outcome === "needs_review") {
|
|
1431
|
+
record.outcome = "interrupted";
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
if (dispatch) {
|
|
1436
|
+
try {
|
|
1437
|
+
processExit = (await this.closeForDispatch()) ?? null;
|
|
1438
|
+
}
|
|
1439
|
+
catch (closeError) {
|
|
1440
|
+
record.process_stop_error = safeProviderMessage(closeError instanceof Error
|
|
1441
|
+
? closeError.message
|
|
1442
|
+
: String(closeError));
|
|
1443
|
+
}
|
|
1444
|
+
if (processExit)
|
|
1445
|
+
record.process_exit_receipt = processExit;
|
|
1446
|
+
if (record.result_sha256 === null) {
|
|
1447
|
+
// A cancelled prompt may never produce a result file.
|
|
1448
|
+
record.result_file = null;
|
|
1449
|
+
}
|
|
1450
|
+
if (processExit?.terminationVerified &&
|
|
1451
|
+
processExit.exitObservedAt &&
|
|
1452
|
+
startRequestId &&
|
|
1453
|
+
record.session_id &&
|
|
1454
|
+
Number.isSafeInteger(record.process_id) &&
|
|
1455
|
+
record.process_id) {
|
|
1456
|
+
if (record.outcome === "settled" &&
|
|
1457
|
+
(processExit.exitCode !== 0 || processExit.signalCode !== null)) {
|
|
1458
|
+
record.outcome = "interrupted";
|
|
1459
|
+
record.reason =
|
|
1460
|
+
"Pi reported a settled response but the manager-owned process exited abnormally";
|
|
1461
|
+
}
|
|
1462
|
+
const settleReceiptId = `settle-${runId}`;
|
|
1463
|
+
const receipt = {
|
|
1464
|
+
schemaVersion: 1,
|
|
1465
|
+
source: "pactile-pi-rpc",
|
|
1466
|
+
assurance: "manager-owned-child-exit",
|
|
1467
|
+
taskId: dispatch.taskId,
|
|
1468
|
+
taskRunId: dispatch.runId,
|
|
1469
|
+
piRunId: runId,
|
|
1470
|
+
role: "implement",
|
|
1471
|
+
sessionId: record.session_id,
|
|
1472
|
+
processId: record.process_id,
|
|
1473
|
+
startRequestId,
|
|
1474
|
+
settleReceiptId,
|
|
1475
|
+
terminal: record.outcome === "cancelled" ? "cancelled" : "exited",
|
|
1476
|
+
exitCode: processExit.exitCode,
|
|
1477
|
+
signalCode: processExit.signalCode,
|
|
1478
|
+
cancellationRequestId: parseJson(cancelFile)?.run_id === runId &&
|
|
1479
|
+
typeof parseJson(cancelFile)?.request_id === "string"
|
|
1480
|
+
? String(parseJson(cancelFile)?.request_id)
|
|
1481
|
+
: null,
|
|
1482
|
+
evidenceRef: runReceiptRef,
|
|
1483
|
+
progressEvidenceRef,
|
|
1484
|
+
resultRef: record.result_file,
|
|
1485
|
+
resultSha256: record.result_sha256 ?? null,
|
|
1486
|
+
recordedAt: new Date().toISOString(),
|
|
1487
|
+
processExit,
|
|
1488
|
+
};
|
|
1489
|
+
record.settle_receipt_id = settleReceiptId;
|
|
1490
|
+
record.process_stop_receipt = receipt;
|
|
1491
|
+
record.process_stop_error = null;
|
|
1492
|
+
}
|
|
1493
|
+
else {
|
|
1494
|
+
record.process_stop_error ??=
|
|
1495
|
+
"Pi child close was not verified; dispatch lease retained";
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
329
1498
|
if (closeFailed && alive(this.client?.pid)) {
|
|
330
1499
|
record.outcome = "interrupted";
|
|
331
1500
|
record.reason = `${record.reason ?? "Pi run interrupted"}; Pi process may still be active`;
|
|
@@ -334,10 +1503,52 @@ export class PiTaskBridge {
|
|
|
334
1503
|
record.elapsed_ms = Math.round(performance.now() - started);
|
|
335
1504
|
atomicJson(runFile, record);
|
|
336
1505
|
atomicJson(latestFile, record);
|
|
337
|
-
|
|
1506
|
+
const v2ChildClosed = processExit?.terminationVerified === true;
|
|
1507
|
+
if (review && reviewProcessExit?.terminationVerified) {
|
|
1508
|
+
fs.rmSync(lockFile, { force: true });
|
|
1509
|
+
}
|
|
1510
|
+
else if (!review &&
|
|
1511
|
+
((!dispatch && !(closeFailed && alive(this.client?.pid))) ||
|
|
1512
|
+
(dispatch && v2ChildClosed))) {
|
|
338
1513
|
fs.rmSync(lockFile, { force: true });
|
|
1514
|
+
}
|
|
339
1515
|
if (parseJson(cancelFile)?.run_id === runId)
|
|
340
1516
|
fs.rmSync(cancelFile, { force: true });
|
|
1517
|
+
if (dispatch &&
|
|
1518
|
+
record.process_stop_receipt &&
|
|
1519
|
+
startRequestId &&
|
|
1520
|
+
record.session_id &&
|
|
1521
|
+
record.process_id) {
|
|
1522
|
+
try {
|
|
1523
|
+
const settlement = persistPiV2StopAndRelease(dispatch, {
|
|
1524
|
+
runId,
|
|
1525
|
+
startRequestId,
|
|
1526
|
+
sessionId: record.session_id,
|
|
1527
|
+
processId: record.process_id,
|
|
1528
|
+
progressEvidenceRef,
|
|
1529
|
+
startReceiptRef,
|
|
1530
|
+
runReceiptRef,
|
|
1531
|
+
resultRef: record.result_file,
|
|
1532
|
+
resultSha256: record.result_sha256 ?? null,
|
|
1533
|
+
outcome: record.outcome,
|
|
1534
|
+
cancellationRequestId: record.process_stop_receipt.cancellationRequestId,
|
|
1535
|
+
processExit: record.process_stop_receipt.processExit,
|
|
1536
|
+
processStopReceipt: record.process_stop_receipt,
|
|
1537
|
+
});
|
|
1538
|
+
record.dispatch_stop_proof_ref =
|
|
1539
|
+
settlement.stopReceiptTaskRef || null;
|
|
1540
|
+
record.dispatch_lease_released = settlement.released;
|
|
1541
|
+
record.dispatch_lease_release_reason = settlement.reasonCode;
|
|
1542
|
+
}
|
|
1543
|
+
catch (settleError) {
|
|
1544
|
+
record.dispatch_lease_released = false;
|
|
1545
|
+
record.dispatch_lease_release_reason = safeProviderMessage(settleError instanceof Error
|
|
1546
|
+
? settleError.message
|
|
1547
|
+
: String(settleError));
|
|
1548
|
+
}
|
|
1549
|
+
atomicJson(runFile, record);
|
|
1550
|
+
atomicJson(latestFile, record);
|
|
1551
|
+
}
|
|
341
1552
|
this.locked = false;
|
|
342
1553
|
}
|
|
343
1554
|
return record;
|