@piagent/platform 1.5.5 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +166 -0
- package/README.md +25 -21
- package/README.vi.md +34 -5
- package/adapters/web-frontend/profile.json +2 -4
- package/architecture/layers.json +18 -3
- package/benchmarks/deep-logic-v1/SPECIALIST_PROTOCOL.vi.md +122 -0
- package/benchmarks/deep-logic-v1/grade.mjs +272 -10
- package/benchmarks/deep-logic-v1/project/apps/admin/src/billing-summary.js +3 -0
- package/benchmarks/deep-logic-v1/project/benchmark-contract/public-smoke.test.js +166 -0
- package/benchmarks/deep-logic-v1/project/package.json +1 -1
- package/benchmarks/deep-logic-v1/project/packages/billing/src/close-period.js +3 -0
- package/benchmarks/deep-logic-v1/project/packages/billing/src/plan-timeline.js +3 -0
- package/benchmarks/deep-logic-v1/project/test/smoke.test.js +7 -1
- package/benchmarks/deep-logic-v1/prompts/budgeted-context-graph.md +2 -0
- package/benchmarks/deep-logic-v1/prompts/fair-dependency-scheduler.md +2 -0
- package/benchmarks/deep-logic-v1/prompts/layered-policy-resolution.md +2 -0
- package/benchmarks/deep-logic-v1/prompts/resumable-stream-assembly.md +3 -1
- package/benchmarks/deep-logic-v1/prompts/revision-event-reconciliation.md +2 -0
- package/benchmarks/deep-logic-v1/prompts/temporal-usage-billing-close.md +123 -0
- package/benchmarks/deep-logic-v1/prompts/transactional-config-merge.md +2 -0
- package/benchmarks/deep-logic-v1/suite.json +51 -3
- package/benchmarks/deep-logic-v1/variant.mjs +22 -1
- package/benchmarks/production-v1/spend-control.v1.json +92 -0
- package/benchmarks/production-v1/suite.json +46 -3
- package/catalog/capabilities.json +18 -18
- package/docs/authority-manifest.md +1 -1
- package/docs/auto-delegation-policy.md +38 -73
- package/docs/command-reference-vietnamese.md +27 -35
- package/docs/context-engine.md +12 -4
- package/docs/context-window-policy.md +37 -1
- package/docs/distribution-standard.md +7 -7
- package/docs/en/architecture.md +27 -13
- package/docs/languages.json +4 -4
- package/docs/model-options.md +8 -5
- package/docs/operator-manual-vietnamese.md +27 -45
- package/docs/project-adapters.md +2 -2
- package/docs/publishing-for-teams.md +10 -0
- package/docs/quality-benchmark.md +427 -62
- package/docs/release-install-policy.md +10 -9
- package/docs/runtime-harness-standard.md +3 -3
- package/docs/runtime-policy-design.md +4 -4
- package/docs/runtime-quality-baseline.md +13 -6
- package/docs/security-threat-model.md +2 -2
- package/docs/subagent-orchestration-capabilities.md +19 -51
- package/docs/subagents-and-multiagent.md +21 -47
- package/docs/task-implementation-contract.md +3 -3
- package/docs/task-lifecycle-vietnamese.md +3 -3
- package/docs/team-onboarding.md +2 -2
- package/docs/usage-observability.md +7 -2
- package/docs/vi/architecture.md +27 -13
- package/docs-site/architecture.html +1 -1
- package/docs-site/benchmark.html +89 -95
- package/docs-site/commands.html +1 -1
- package/docs-site/content/benchmark.html +88 -94
- package/docs-site/content/en/benchmark.html +134 -90
- package/docs-site/content/en/index.html +19 -19
- package/docs-site/content/en/runtime.html +4 -4
- package/docs-site/content/en/whats-new.html +12 -1
- package/docs-site/content/en/workflows.html +2 -2
- package/docs-site/content/index.html +19 -19
- package/docs-site/content/runtime.html +4 -4
- package/docs-site/content/whats-new.html +12 -1
- package/docs-site/content/workflows.html +2 -2
- package/docs-site/context-engine.html +1 -1
- package/docs-site/en/architecture.html +1 -1
- package/docs-site/en/benchmark.html +135 -91
- package/docs-site/en/commands.html +1 -1
- package/docs-site/en/context-engine.html +1 -1
- package/docs-site/en/index.html +20 -20
- package/docs-site/en/mcp-approval.html +1 -1
- package/docs-site/en/mcp-auth.html +1 -1
- package/docs-site/en/mcp-servers.html +1 -1
- package/docs-site/en/mcp.html +1 -1
- package/docs-site/en/permissions.html +1 -1
- package/docs-site/en/profiles.html +1 -1
- package/docs-site/en/quickstart.html +1 -1
- package/docs-site/en/runtime.html +5 -5
- package/docs-site/en/scope.html +1 -1
- package/docs-site/en/security.html +1 -1
- package/docs-site/en/team.html +1 -1
- package/docs-site/en/whats-new.html +13 -2
- package/docs-site/en/workflows.html +3 -3
- package/docs-site/index.html +20 -20
- package/docs-site/mcp-approval.html +1 -1
- package/docs-site/mcp-auth.html +1 -1
- package/docs-site/mcp-servers.html +1 -1
- package/docs-site/mcp.html +1 -1
- package/docs-site/permissions.html +1 -1
- package/docs-site/profiles.html +1 -1
- package/docs-site/quickstart.html +1 -1
- package/docs-site/runtime.html +5 -5
- package/docs-site/scope.html +1 -1
- package/docs-site/security.html +1 -1
- package/docs-site/team.html +1 -1
- package/docs-site/whats-new.html +13 -2
- package/docs-site/workflows.html +3 -3
- package/evals/fixtures/benchmark-assurance-evidence.invalid.json +1 -1
- package/evals/fixtures/benchmark-assurance-evidence.valid.json +1 -1
- package/evals/fixtures/codex-relative-efficiency-v1.invalid.json +4 -0
- package/evals/fixtures/codex-relative-efficiency-v1.valid.json +94 -0
- package/evals/fixtures/helper-request.invalid.json +1 -1
- package/evals/fixtures/helper-request.valid.json +1 -1
- package/evals/fixtures/piagent-webui/session-live-state-v1.invalid.json +18 -0
- package/evals/fixtures/piagent-webui/session-live-state-v1.valid.json +27 -0
- package/evals/fixtures/piagent-webui/subagent-tree-v1.valid.json +7 -0
- package/evals/fixtures/task-authority-snapshot.valid.json +4 -4
- package/evals/fs4-readiness-matrix.v1.json +2 -2
- package/evals/fs5-causal-arm.v1.json +3 -3
- package/evals/fs5-pilot-protocol.v1.json +3 -3
- package/evals/fs5-pilot-protocol.v2.json +8 -8
- package/evals/fs5-pilot-protocol.v3.json +8 -8
- package/evals/fs5-pilot-protocol.v4.json +7 -6
- package/evals/fs5-pilot-protocol.v5.json +9 -7
- package/evals/private-holdout-v1/public-exposure.v1.json +1 -1
- package/evals/runtime-conformance-v1/README.md +43 -0
- package/evals/runtime-conformance-v1/evaluate-paid-canary.mjs +198 -0
- package/evals/runtime-conformance-v1/lane.json +57 -0
- package/evals/runtime-conformance-v1/paid-canary.json +31 -0
- package/evals/runtime-conformance-v1/runner.mjs +456 -0
- package/package.json +3 -1
- package/packages/piagent-core/README.md +13 -4
- package/packages/piagent-core/benchmark/benchmark-assurance.js +4 -1
- package/packages/piagent-core/benchmark/benchmark-bootstrap.js +91 -9
- package/packages/piagent-core/benchmark/benchmark-claim-restrictions.js +38 -7
- package/packages/piagent-core/benchmark/benchmark-cli.js +7 -7
- package/packages/piagent-core/benchmark/benchmark-codex-relative-efficiency.js +698 -0
- package/packages/piagent-core/benchmark/benchmark-codex.js +3 -1
- package/packages/piagent-core/benchmark/benchmark-comparison.js +271 -2
- package/packages/piagent-core/benchmark/benchmark-core.js +736 -664
- package/packages/piagent-core/benchmark/benchmark-execution-guard.js +7 -5
- package/packages/piagent-core/benchmark/benchmark-forensics.js +441 -4
- package/packages/piagent-core/benchmark/benchmark-host-readiness.js +639 -0
- package/packages/piagent-core/benchmark/benchmark-matrix.js +22 -4
- package/packages/piagent-core/benchmark/benchmark-normalized-cost.js +317 -0
- package/packages/piagent-core/benchmark/benchmark-preflight.js +5 -1
- package/packages/piagent-core/benchmark/benchmark-process.js +6 -5
- package/packages/piagent-core/benchmark/benchmark-provider-free-evidence.js +345 -0
- package/packages/piagent-core/benchmark/benchmark-provider-wire.js +126 -0
- package/packages/piagent-core/benchmark/benchmark-record-validation.js +252 -0
- package/packages/piagent-core/benchmark/benchmark-report.js +130 -12
- package/packages/piagent-core/benchmark/benchmark-resume-recovery.js +14 -0
- package/packages/piagent-core/benchmark/benchmark-runner-policy.js +26 -0
- package/packages/piagent-core/benchmark/benchmark-runtime-causal.js +244 -0
- package/packages/piagent-core/benchmark/benchmark-stage-diagnostic.js +979 -0
- package/packages/piagent-core/benchmark/benchmark-statistics.js +64 -0
- package/packages/piagent-core/benchmark/benchmark-stop-policy.js +8 -5
- package/packages/piagent-core/benchmark/benchmark-suite-identity.js +31 -0
- package/packages/piagent-core/benchmark/benchmark-suite-runtime.js +13 -9
- package/packages/piagent-core/benchmark/benchmark-suite.js +149 -2
- package/packages/piagent-core/benchmark/benchmark-summary-support.js +380 -0
- package/packages/piagent-core/benchmark/benchmark-timing-diagnostics.js +971 -0
- package/packages/piagent-core/benchmark/benchmark-usage.js +322 -0
- package/packages/piagent-core/benchmark/benchmark-workflow.js +166 -0
- package/packages/piagent-core/capabilities/runtime-integrity.js +3 -0
- package/packages/piagent-core/docs/context-efficiency-improvement-plan.md +1080 -0
- package/packages/piagent-core/extensions/acceptance-behavior-proof.js +806 -0
- package/packages/piagent-core/extensions/acceptance-contract-semantics.js +83 -69
- package/packages/piagent-core/extensions/acceptance-error-classes.js +160 -0
- package/packages/piagent-core/extensions/acceptance-executable-evidence.js +12 -9
- package/packages/piagent-core/extensions/acceptance-receipt.js +123 -59
- package/packages/piagent-core/extensions/acceptance-state-machine-evidence.js +518 -0
- package/packages/piagent-core/extensions/acceptance-test-binding-evidence.js +60 -0
- package/packages/piagent-core/extensions/context-efficiency-metrics.js +302 -0
- package/packages/piagent-core/extensions/context-efficiency-report.js +167 -0
- package/packages/piagent-core/extensions/context-engine.js +149 -156
- package/packages/piagent-core/extensions/context-evidence.js +48 -0
- package/packages/piagent-core/extensions/context-import-links.js +296 -0
- package/packages/piagent-core/extensions/context-index-policy.js +4 -0
- package/packages/piagent-core/extensions/context-query-paths.js +57 -0
- package/packages/piagent-core/extensions/context-telemetry-integrity.js +65 -0
- package/packages/piagent-core/extensions/criterion-context-pack.js +435 -7
- package/packages/piagent-core/extensions/criterion-graph.js +82 -9
- package/packages/piagent-core/extensions/fallback-base-policy.ts +101 -0
- package/packages/piagent-core/extensions/guard-types.ts +6 -0
- package/packages/piagent-core/extensions/javascript-regex-evidence.js +32 -0
- package/packages/piagent-core/extensions/piagent-guard.ts +420 -260
- package/packages/piagent-core/extensions/task-contract-view.js +17 -3
- package/packages/piagent-core/extensions/task-lifecycle.js +39 -2
- package/packages/piagent-core/extensions/task-state.js +109 -142
- package/packages/piagent-core/extensions/verification-intelligence.js +52 -13
- package/packages/piagent-core/extensions/workspace-evidence-roots.js +193 -0
- package/packages/piagent-core/package.json +1 -1
- package/packages/piagent-core/policies/base-policy.json +44 -2
- package/packages/piagent-core/policy/authority-manifest.v1.json +3 -3
- package/packages/piagent-core/prompts/be-to-fe.md +2 -8
- package/packages/piagent-core/prompts/plan.md +1 -1
- package/packages/piagent-core/prompts/platform-improve.md +2 -6
- package/packages/piagent-core/prompts/review.md +2 -7
- package/packages/piagent-core/prompts/scout.md +1 -1
- package/packages/piagent-core/prompts/task.md +4 -3
- package/packages/piagent-core/runtime/context/context-delivery.ts +103 -0
- package/packages/piagent-core/runtime/context/context-delta-shadow.ts +53 -0
- package/packages/piagent-core/runtime/context/context-efficiency-format.ts +47 -0
- package/packages/piagent-core/runtime/context/context-evidence-qualification.ts +32 -0
- package/packages/piagent-core/runtime/context/prefix-telemetry.ts +47 -0
- package/packages/piagent-core/runtime/context/repository-memory-hints.ts +27 -0
- package/packages/piagent-core/runtime/hooks/agent-prompt-telemetry.ts +56 -0
- package/packages/piagent-core/runtime/hooks/agent-start-hook.ts +162 -102
- package/packages/piagent-core/runtime/hooks/completion-hook.ts +12 -12
- package/packages/piagent-core/runtime/hooks/input-hook.ts +17 -1
- package/packages/piagent-core/runtime/hooks/session-hooks.ts +22 -2
- package/packages/piagent-core/runtime/hooks/session-start-hook.ts +107 -3
- package/packages/piagent-core/runtime/hooks/tool-result-content-guards.ts +95 -0
- package/packages/piagent-core/runtime/hooks/tool-result-hook.ts +134 -166
- package/packages/piagent-core/runtime/hooks/tool-result-value-helpers.ts +66 -0
- package/packages/piagent-core/runtime/inspection/activity-event-adapter.ts +4 -2
- package/packages/piagent-core/runtime/inspection/activity-recovery.ts +104 -0
- package/packages/piagent-core/runtime/inspection/bounded-emission-guard.ts +286 -0
- package/packages/piagent-core/runtime/inspection/runtime-event-store.ts +37 -7
- package/packages/piagent-core/runtime/inspection/task-compaction-history.ts +1 -1
- package/packages/piagent-core/runtime/inspection/task-handoff-history.ts +1 -1
- package/packages/piagent-core/runtime/inspection/task-subagent-tree.ts +27 -4
- package/packages/piagent-core/runtime/inspection/tool-failure-classification.ts +70 -0
- package/packages/piagent-core/runtime/inspection/webui-snapshot.ts +73 -9
- package/packages/piagent-core/runtime/model/authenticated-catalog.ts +17 -9
- package/packages/piagent-core/runtime/model/openai-codex-reasoning.ts +83 -0
- package/packages/piagent-core/runtime/model/provider-wire-fingerprint.ts +238 -0
- package/packages/piagent-core/runtime/orchestration/helper-lifecycle.ts +16 -12
- package/packages/piagent-core/runtime/orchestration/minimal-delegation-policy.ts +71 -0
- package/packages/piagent-core/runtime/orchestration/owned-work-budget.ts +28 -20
- package/packages/piagent-core/runtime/orchestration/role-policy.ts +27 -9
- package/packages/piagent-core/runtime/orchestration/subagent-tool-policy.ts +329 -0
- package/packages/piagent-core/runtime/policy/block-remedy.ts +0 -4
- package/packages/piagent-core/runtime/product/activity-inspector.ts +7 -2
- package/packages/piagent-core/runtime/product/efficiency-metrics.ts +7 -2
- package/packages/piagent-core/runtime/quality/edit-freshness-guard.ts +102 -0
- package/packages/piagent-core/runtime/quality/exact-output-contract.ts +22 -11
- package/packages/piagent-core/runtime/quality/performance-assurance-signals.ts +23 -0
- package/packages/piagent-core/runtime/quality/performance-assurance.ts +9 -5
- package/packages/piagent-core/runtime/recovery/continuation-budget.ts +1 -1
- package/packages/piagent-core/runtime/recovery/edit-recovery-context.ts +113 -0
- package/packages/piagent-core/runtime/recovery/edit-recovery-delivery.ts +42 -0
- package/packages/piagent-core/runtime/recovery/handoff-projection.ts +4 -1
- package/packages/piagent-core/runtime/recovery/pristine-task-policy-refresh.ts +91 -0
- package/packages/piagent-core/runtime/recovery/recovery-policy.ts +7 -4
- package/packages/piagent-core/runtime/recovery/resume-state.ts +112 -14
- package/packages/piagent-core/runtime/recovery/semantic-repair-handshake.ts +60 -13
- package/packages/piagent-core/runtime/recovery/semantic-repair-runtime.ts +55 -2
- package/packages/piagent-core/runtime/recovery/task-evidence-coverage-refresh.ts +217 -0
- package/packages/piagent-core/runtime/registration/context-commands.ts +82 -7
- package/packages/piagent-core/runtime/registration/knowledge-tools.ts +9 -5
- package/packages/piagent-core/runtime/registration/operator-request-intake.ts +49 -0
- package/packages/piagent-core/runtime/registration/policy-tools.ts +50 -15
- package/packages/piagent-core/runtime/registration/session-commands.ts +1 -1
- package/packages/piagent-core/runtime/registration/task-completion-tools.ts +61 -25
- package/packages/piagent-core/runtime/registration/task-evidence-tools.ts +21 -12
- package/packages/piagent-core/runtime/registration/task-start-guidance.ts +169 -0
- package/packages/piagent-core/runtime/registration/task-start-manifest.ts +54 -0
- package/packages/piagent-core/runtime/registration/task-start-retry-helpers.ts +37 -0
- package/packages/piagent-core/runtime/registration/task-start-tool.ts +108 -103
- package/packages/piagent-core/runtime/runtime-limits.ts +16 -2
- package/packages/piagent-core/runtime/session/adaptive-context-analysis.ts +461 -0
- package/packages/piagent-core/runtime/session/adaptive-context-governor.ts +464 -0
- package/packages/piagent-core/runtime/session/adaptive-context-ledger.ts +265 -0
- package/packages/piagent-core/runtime/session/message-signals.ts +7 -3
- package/packages/piagent-core/runtime/session/operator-request-carry.ts +44 -0
- package/packages/piagent-core/runtime/session/runtime-state.ts +161 -5
- package/packages/piagent-core/runtime/session/source-checkout-read-grants.ts +35 -0
- package/packages/piagent-core/runtime/session/system-prompt.ts +76 -14
- package/packages/piagent-core/runtime/session/task-contract-presentation.ts +39 -0
- package/packages/piagent-core/runtime/tools/apply-patch-tool.ts +500 -0
- package/packages/piagent-core/runtime/tools/phase-tools.ts +8 -8
- package/packages/piagent-core/runtime/tools/tool-groups.ts +12 -2
- package/packages/piagent-core/runtime/trajectory/trajectory-runtime.ts +2 -1
- package/packages/piagent-core/runtime/verification/exact-verifier-reuse.ts +101 -0
- package/packages/piagent-core/runtime/workflows/runtime-intake-compaction.ts +164 -0
- package/packages/piagent-core/runtime/workflows/task-intake.ts +207 -38
- package/packages/piagent-core/security/sensitive-data.js +13 -218
- package/packages/piagent-core/security/sensitive-project-file.js +42 -0
- package/packages/piagent-core/security/sensitive-source-expression.js +330 -0
- package/packages/piagent-core/security/sensitive-source-formats.js +394 -0
- package/packages/piagent-core/security/sensitive-text.js +234 -0
- package/packages/piagent-core/subagents/piagent-oracle.md +5 -3
- package/packages/piagent-core/subagents/piagent-planner.md +6 -4
- package/packages/piagent-core/subagents/piagent-reviewer.md +5 -3
- package/packages/piagent-core/subagents/piagent-scout.md +4 -2
- package/packages/piagent-core/subagents/piagent-worker.md +7 -5
- package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-rD9WJKxs.js → MarkdownMessage-B4XehTCU.js} +1 -1
- package/packages/piagent-webui/dist/client/assets/index-CgOEyRPG.js +176 -0
- package/packages/piagent-webui/dist/client/index.html +1 -1
- package/packages/piagent-webui/extension/piagent-webui.ts +3 -1
- package/packages/piagent-webui/extension/same-session-bridge.ts +1 -1
- package/packages/piagent-webui/extension/session-stream-adapter.ts +33 -10
- package/packages/piagent-webui/gateway/gateway-events.ts +109 -5
- package/packages/piagent-webui/gateway/gateway-service.ts +3 -0
- package/packages/piagent-webui/gateway/gateway-session-stream.ts +122 -20
- package/packages/piagent-webui/gateway/session-command-controller.ts +18 -3
- package/packages/piagent-webui/gateway/session-effective-options.ts +47 -0
- package/packages/piagent-webui/gateway/session-live-state.ts +21 -0
- package/packages/piagent-webui/gateway/session-operation-lifecycle.ts +237 -0
- package/packages/piagent-webui/gateway/session-operation-retry-control.ts +38 -0
- package/packages/piagent-webui/gateway/session-operation-start.ts +5 -1
- package/packages/piagent-webui/gateway/session-operation-watchdog.ts +165 -0
- package/packages/piagent-webui/gateway/session-runtime-factory.ts +45 -0
- package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +141 -125
- package/packages/piagent-webui/ownership/session-lease-store.ts +5 -2
- package/packages/piagent-webui/server/core-inspection-provider.ts +2 -1
- package/packages/piagent-webui/server/loopback-server.ts +8 -0
- package/packages/piagent-webui/server/transcript-projection.ts +113 -9
- package/packages/piagent-webui/shared/text-visibility.ts +10 -0
- package/schemas/codex-relative-efficiency-v1.schema.json +155 -0
- package/schemas/piagent-webui/catalog-v1.json +9 -0
- package/schemas/piagent-webui/gateway-protocol-v1.schema.json +53 -3
- package/schemas/piagent-webui/runtime-event-v2.schema.json +15 -5
- package/schemas/piagent-webui/session-live-state-v1.schema.json +76 -0
- package/schemas/piagent-webui/subagent-tree-v1.schema.json +11 -1
- package/schemas/project-profile.schema.json +1 -1
- package/schemas/task-contract.schema.json +27 -1
- package/scripts/benchmark-runner-core.mjs +367 -363
- package/scripts/benchmark-runner-finalization.mjs +313 -0
- package/scripts/benchmark-runner-host-readiness.mjs +94 -0
- package/scripts/benchmark-runner-support.mjs +200 -0
- package/scripts/benchmark-session.mjs +95 -41
- package/scripts/configure-model-scope.sh +4 -3
- package/scripts/configure-subagents.sh +42 -34
- package/scripts/helper-evaluation.mjs +2 -2
- package/scripts/install-global.sh +2 -2
- package/scripts/pi-usage-history.mjs +37 -10
- package/scripts/setup.sh +2 -2
- package/scripts/team-doctor.sh +1 -1
- package/scripts/verify-local.sh +10 -1
- package/templates/global/settings.json +2 -1
- package/packages/piagent-webui/dist/client/assets/index-BZRFw1u6.js +0 -174
|
@@ -67,7 +67,7 @@ import {
|
|
|
67
67
|
} from "./context-index-policy.js";
|
|
68
68
|
import {
|
|
69
69
|
DEFAULT_MAX_TASK_ATTEMPTS, activeSessionTask, bindSessionTask, createTaskRunId, hasGitEvidenceRoot, listTaskContracts,
|
|
70
|
-
|
|
70
|
+
priorTaskAttempts, repositoryFileManifest, repositoryFileManifestDetails, resolveTaskContract, safeTaskId, summarizeAttempt, taskContractValidationErrors, taskDigestMigrationArchiveStatus,
|
|
71
71
|
workPlanDependencyError, workingTreeSnapshot, workingTreeSnapshotHasUnavailableEvidence, writeTaskContract
|
|
72
72
|
} from "./task-state.js";
|
|
73
73
|
import { classifyRecordedVerificationFailure, classifyVerificationFailure, latestObservedVerification, meaningfulVerificationCommands, selectCompletionRecoveryClassification, selectVerificationPlan } from "./verification-intelligence.js";
|
|
@@ -84,12 +84,14 @@ import {
|
|
|
84
84
|
invalidateAcceptanceReceiptAfterMutation,
|
|
85
85
|
refreshAcceptanceReceipt
|
|
86
86
|
} from "./acceptance-receipt.js";
|
|
87
|
+
import { acceptanceLanguageAdapterForPath, isAcceptanceTestPath } from "./acceptance-language-adapters.js";
|
|
87
88
|
import { allVerifyCommandsPassCurrentTree, changedSnapshotFiles, compactTaskDetails, mergeObservedTaskContext, passingVerifyCommandsForDigest, taskDeltaFilesFromSnapshot } from "./task-contract-view.js";
|
|
88
89
|
import { applyRuntimeLifecycleObservation, runtimeLifecycleMode, workingTreeEvidenceDigest } from "./task-lifecycle.js";
|
|
89
90
|
import { completeTaskDigestRefresh } from "./task-digest-migration.js";
|
|
90
91
|
import { WORKING_TREE_DIGEST_ALGORITHM, isCurrentWorkingTreeDigest, workingTreeObservation, workingTreeSnapshotUsesCurrentAlgorithm } from "./working-tree-digest.js";
|
|
91
92
|
import { appendJsonlBounded } from "./state-retention.js";
|
|
92
93
|
import { ensurePrivateStateDirectory, resolveLocalStatePath } from "./local-state-path.js";
|
|
94
|
+
import { hasDurableContextEvidence } from "./context-evidence.js";
|
|
93
95
|
import { piApprovalBroker, type ApprovalActionDraft, type ApprovalUnavailableFallback } from "../runtime/inspection/approval-broker.ts";
|
|
94
96
|
import { inspectTaskControlState } from "../runtime/inspection/task-control-journal.ts";
|
|
95
97
|
import { createSourceMutationGuardBindings } from "../runtime/policy/source-mutation-guard-binding.ts";
|
|
@@ -108,6 +110,7 @@ import {
|
|
|
108
110
|
} from "../runtime/session/tool-result-compaction.ts";
|
|
109
111
|
import { RuntimeSessionState } from "../runtime/session/runtime-state.ts";
|
|
110
112
|
import type { ObservedTaskContext } from "../runtime/session/runtime-state.ts";
|
|
113
|
+
import { reuseCurrentTreeExactVerifier } from "../runtime/verification/exact-verifier-reuse.ts";
|
|
111
114
|
import {
|
|
112
115
|
PIAGENT_TOOL_GROUPS,
|
|
113
116
|
PIAGENT_TOOL_NAMES,
|
|
@@ -121,6 +124,7 @@ import {
|
|
|
121
124
|
automaticReviewLenses,
|
|
122
125
|
automaticReadOnlyTaskScope,
|
|
123
126
|
automaticTaskIntakeMode,
|
|
127
|
+
automaticTaskMutationPolicy,
|
|
124
128
|
automaticTaskRiskLane,
|
|
125
129
|
automaticTaskScope,
|
|
126
130
|
resolveTaskScopePatterns,
|
|
@@ -137,6 +141,8 @@ import { registerToolResultHook } from "../runtime/hooks/tool-result-hook.ts";
|
|
|
137
141
|
import { readRuntimeVersionMetadata, RuntimeSnapshotCapture } from "../runtime/model/runtime-snapshot.ts";
|
|
138
142
|
import { recordRuntimeSnapshotTelemetry } from "../runtime/model/snapshot-telemetry.ts";
|
|
139
143
|
import { captureAuthenticatedModelCatalogFromContext } from "../runtime/model/authenticated-catalog.ts";
|
|
144
|
+
import { normalizeOpenAiCodexReasoningPayload } from "../runtime/model/openai-codex-reasoning.ts";
|
|
145
|
+
import { buildOpenAiCodexWireFingerprint } from "../runtime/model/provider-wire-fingerprint.ts";
|
|
140
146
|
import { ModelRouteRuntime, readModelRouteEvents } from "../runtime/model/model-route-runtime.ts";
|
|
141
147
|
import { parentRoutingModeFromEnvironment, routingObjectiveFromEnvironment } from "../runtime/model/model-route-policy.ts";
|
|
142
148
|
import { ModelSelectionProvenanceTracker } from "../runtime/model/model-selection-provenance.ts";
|
|
@@ -147,6 +153,7 @@ import { SolverShadowRuntime, solverModeFromEnvironment } from "../runtime/solve
|
|
|
147
153
|
import { observeTrajectorySync } from "../runtime/trajectory/trajectory-observability.ts";
|
|
148
154
|
import { TrajectoryRuntime } from "../runtime/trajectory/trajectory-runtime.ts";
|
|
149
155
|
import { PhaseToolRuntime, phaseToolModeFromEnvironment } from "../runtime/tools/phase-tool-runtime.ts";
|
|
156
|
+
import { parseApplyPatchTargets, registerApplyPatchTool } from "../runtime/tools/apply-patch-tool.ts";
|
|
150
157
|
import { authorityReplacementState, ensureTaskAuthorityResumePolicy } from "../runtime/policy/authority-resume-policy.ts";
|
|
151
158
|
import { taskAuthorityDecision } from "../runtime/policy/task-authority-runtime.ts";
|
|
152
159
|
import { selectRecoveryDecision } from "../runtime/recovery/recovery-policy.ts";
|
|
@@ -155,11 +162,14 @@ import { inspectTaskResumeState } from "../runtime/recovery/resume-state.ts";
|
|
|
155
162
|
import { SemanticRepairRuntime } from "../runtime/recovery/semantic-repair-runtime.ts";
|
|
156
163
|
import { defaultRolePolicy } from "../runtime/orchestration/role-policy.ts";
|
|
157
164
|
import { helpersMode } from "../runtime/orchestration/helper-lifecycle.ts";
|
|
165
|
+
import { evaluateDirectSubagentDispatch, recordDirectSubagentResult, reserveDirectSubagentDispatch, taskHelperUsageMode } from "../runtime/orchestration/subagent-tool-policy.ts";
|
|
158
166
|
import { buildLiveTaskStatus, formatLiveTaskStatus } from "../runtime/product/operator-projections.ts";
|
|
159
167
|
import { buildTaskEfficiencyMetrics } from "../runtime/product/efficiency-metrics.ts";
|
|
160
168
|
import { performanceReviewToolDecision, performanceReviewToolKind } from "../runtime/quality/performance-assurance.ts";
|
|
161
169
|
import { expectedModelMutationProof } from "../runtime/quality/model-mutation-proof.ts";
|
|
170
|
+
import { EditFreshnessGuard, editFreshnessModeFromEnvironment } from "../runtime/quality/edit-freshness-guard.ts";
|
|
162
171
|
import { captureVerifierFileSnapshot } from "../runtime/inspection/verifier-snapshot-store.ts";
|
|
172
|
+
import { fallbackBasePolicy } from "./fallback-base-policy.ts";
|
|
163
173
|
import { prefixCompletions, registerPiagentTool, registerRuntimeCommand, registerRuntimeTool } from "../runtime/registration/extension-registration.ts";
|
|
164
174
|
import { FRESH_COMMAND_ACTIONS, FRESH_COMMAND_HELP, ONBOARDING_COMMAND_ACTIONS, WORKFLOW_COMMAND_EXCLUSIONS } from "../runtime/registration/operator-catalogs.ts";
|
|
165
175
|
import { registerPiagentStatusCommand } from "../runtime/registration/runtime-model-status.ts";
|
|
@@ -226,6 +236,7 @@ type TaskStartParameters = {
|
|
|
226
236
|
riskLane: "tiny" | "normal" | "high-risk";
|
|
227
237
|
intakeMode?: "model" | "runtime";
|
|
228
238
|
changeMode?: "source-change" | "read-only";
|
|
239
|
+
mutationPolicy?: "required" | "forbidden";
|
|
229
240
|
verifyGroup?: string;
|
|
230
241
|
maxAttempts?: number;
|
|
231
242
|
expectedOutput: string;
|
|
@@ -334,11 +345,11 @@ const DEFAULT_RUNTIME_POLICY: Required<RuntimePolicySettings> = {
|
|
|
334
345
|
|
|
335
346
|
const DEFAULT_ORCHESTRATION_POLICY: ResolvedOrchestrationPolicy = {
|
|
336
347
|
defaultMode: "solo-first",
|
|
337
|
-
maxConcurrentSubagents:
|
|
348
|
+
maxConcurrentSubagents: 1,
|
|
338
349
|
defaultReviewLenses: ["correctness", "tests", "scope"],
|
|
339
350
|
roleModelGuidance: {
|
|
340
351
|
planner: "Use the strongest available model for decomposition, architecture, risk, and acceptance criteria.",
|
|
341
|
-
worker: "
|
|
352
|
+
worker: "Disabled by default. The parent model owns all implementation and mutation.",
|
|
342
353
|
reviewer: "Use a model or thinking setting decorrelated from the worker when review quality matters.",
|
|
343
354
|
watchdog: "Use a strong model only for final risk review, security, release, or high-impact changes."
|
|
344
355
|
},
|
|
@@ -350,9 +361,9 @@ const DEFAULT_ORCHESTRATION_POLICY: ResolvedOrchestrationPolicy = {
|
|
|
350
361
|
readBeforeTask: true
|
|
351
362
|
},
|
|
352
363
|
rules: [
|
|
353
|
-
"Default to
|
|
354
|
-
"Use
|
|
355
|
-
"
|
|
364
|
+
"Default to the parent model working directly; a helper is an exceptional token optimization, not a reasoning phase.",
|
|
365
|
+
"Use at most one fresh read-only helper only when runtime evidence proves at least two independent lanes and at least 30% projected net token savings.",
|
|
366
|
+
"Never delegate implementation, inherit parent history, retry a deterministic helper failure, or run parallel helpers/writers.",
|
|
356
367
|
"Treat Field Guide memory as advisory; verify every durable fact against current repository files.",
|
|
357
368
|
"Keep review lenses explicit so cheap review work catches drift before release."
|
|
358
369
|
]
|
|
@@ -379,174 +390,7 @@ const SHELL_TOOL_NAMES = new Set(["bash", "shell", "exec"]);
|
|
|
379
390
|
const MAX_MCP_PROXY_ARGS_CHARS = 131_072;
|
|
380
391
|
const SESSION_PERMISSION_OVERRIDES = new Map<string, PermissionProfileMode>();
|
|
381
392
|
|
|
382
|
-
|
|
383
|
-
// exactly the case where something is already wrong, so it must never be the
|
|
384
|
-
// looser of the two: this copy had drifted behind the file and silently dropped
|
|
385
|
-
// `.pi/piagent-state/**` -- the guard's own state -- along with the `sudo ` and
|
|
386
|
-
// `chmod -R 777` blocks. A missing policy file is a reason to be stricter, not a
|
|
387
|
-
// quiet downgrade. `tests/policy-core.test.mjs` fails if this drifts again.
|
|
388
|
-
const DEFAULT_POLICY: BasePolicy = {
|
|
389
|
-
protectedPaths: [".git/**", "**/auth.json", "**/.env", "**/.env.*", "**/node_modules/**", "**/dist/**", ".pi/piagent-state/**", ".pi/settings.json", ".pi/piagent-profile.json", ".pi/piagent-profile.lock.json", CONTEXT_INDEX_FILE],
|
|
390
|
-
shellProtectedPaths: [".git/**", "**/auth.json", "**/.env", "**/.env.*", ".pi/piagent-state/**", ".pi/settings.json", ".pi/piagent-profile.json", ".pi/piagent-profile.lock.json", CONTEXT_INDEX_FILE],
|
|
391
|
-
blockedCommandPatterns: ["rm -rf /", "rm -rf ~", "rm -rf $HOME", "git reset --hard", "git clean -fd", "sudo ", "chmod -R 777"],
|
|
392
|
-
requireConfirmationPatterns: ["deploy", "release", "publish", "migration", "gh pr merge", "git push"],
|
|
393
|
-
defaultRequiredContext: ["AGENTS.md", "README.md"],
|
|
394
|
-
permissionProfiles: {
|
|
395
|
-
defaultMode: "workspace-write",
|
|
396
|
-
allowedModes: ["read-only", "workspace-write", "trusted-full-access"]
|
|
397
|
-
},
|
|
398
|
-
execPolicy: {
|
|
399
|
-
defaultMode: "enforce",
|
|
400
|
-
bannedPrefixSuggestions: [
|
|
401
|
-
["python"],
|
|
402
|
-
["python3"],
|
|
403
|
-
["node"],
|
|
404
|
-
["node", "-e"],
|
|
405
|
-
["bash"],
|
|
406
|
-
["bash", "-lc"],
|
|
407
|
-
["sh"],
|
|
408
|
-
["sh", "-c"],
|
|
409
|
-
["zsh"],
|
|
410
|
-
["zsh", "-lc"],
|
|
411
|
-
["git"],
|
|
412
|
-
["sudo"],
|
|
413
|
-
["env"]
|
|
414
|
-
],
|
|
415
|
-
rules: [
|
|
416
|
-
{
|
|
417
|
-
id: "prompt-git-add-broad",
|
|
418
|
-
action: "prompt",
|
|
419
|
-
match: "regex",
|
|
420
|
-
value: "(?:^|\\s)git\\s+(?:-C\\s+\\S+\\s+)?add\\s+(?:(?:--all|-A)(?:\\s+(?:\\.|:/))?|--\\s+(?:\\.|:/)|(?:\\.|:/))(?:\\s|$)",
|
|
421
|
-
reason: "Broad git staging can include unrelated or sensitive changes; inspect git status/diff and confirm the exact scope first."
|
|
422
|
-
}
|
|
423
|
-
]
|
|
424
|
-
},
|
|
425
|
-
contextBudget: {
|
|
426
|
-
defaultMode: "enforce",
|
|
427
|
-
maxContextFileChars: 50000,
|
|
428
|
-
maxMemoryFileChars: 20000,
|
|
429
|
-
maxManifestFiles: 80,
|
|
430
|
-
warnFragmentChars: 4000
|
|
431
|
-
},
|
|
432
|
-
toolRegistry: {
|
|
433
|
-
defaultMode: "advisory",
|
|
434
|
-
alwaysAllowedTools: [
|
|
435
|
-
"piagent_tools",
|
|
436
|
-
"piagent_context_engine",
|
|
437
|
-
"piagent_context",
|
|
438
|
-
"piagent_permission_status",
|
|
439
|
-
"piagent_exec_policy_check",
|
|
440
|
-
"piagent_context_budget",
|
|
441
|
-
"piagent_tool_policy_check",
|
|
442
|
-
"piagent_task_gate_check",
|
|
443
|
-
"piagent_usage_snapshot",
|
|
444
|
-
"piagent_orchestration_policy",
|
|
445
|
-
"piagent_memory_status",
|
|
446
|
-
"piagent_memory_note",
|
|
447
|
-
"piagent_memory_search",
|
|
448
|
-
"piagent_memory_citation_record",
|
|
449
|
-
"piagent_context_index_status",
|
|
450
|
-
"piagent_context_index_record",
|
|
451
|
-
"piagent_context_index_search",
|
|
452
|
-
"piagent_profile_options",
|
|
453
|
-
"piagent_profile_apply",
|
|
454
|
-
"piagent_profile_tech_options",
|
|
455
|
-
"piagent_profile_tech_apply",
|
|
456
|
-
"piagent_profile_tech_context_record",
|
|
457
|
-
"piagent_project_onboarding_record",
|
|
458
|
-
"piagent_task_start",
|
|
459
|
-
"piagent_task_progress",
|
|
460
|
-
"piagent_source_checkout",
|
|
461
|
-
"piagent_context_record",
|
|
462
|
-
"piagent_verify_record",
|
|
463
|
-
"piagent_trace_record"
|
|
464
|
-
],
|
|
465
|
-
toolCapabilities: {
|
|
466
|
-
bash: ["shell"],
|
|
467
|
-
shell: ["shell"],
|
|
468
|
-
exec: ["shell"],
|
|
469
|
-
read: ["filesystem-readonly"],
|
|
470
|
-
grep: ["filesystem-readonly"],
|
|
471
|
-
find: ["filesystem-readonly"],
|
|
472
|
-
ls: ["filesystem-readonly"],
|
|
473
|
-
write: ["filesystem-write"],
|
|
474
|
-
edit: ["filesystem-write"],
|
|
475
|
-
browser: ["browser"],
|
|
476
|
-
github: ["github"]
|
|
477
|
-
}
|
|
478
|
-
},
|
|
479
|
-
externalActionPolicy: {
|
|
480
|
-
defaultMode: "enforce",
|
|
481
|
-
providerKeywords: [
|
|
482
|
-
"github",
|
|
483
|
-
"gitlab",
|
|
484
|
-
"bitbucket",
|
|
485
|
-
"vercel",
|
|
486
|
-
"netlify",
|
|
487
|
-
"cloudflare",
|
|
488
|
-
"aws",
|
|
489
|
-
"gcp",
|
|
490
|
-
"azure",
|
|
491
|
-
"slack",
|
|
492
|
-
"teams",
|
|
493
|
-
"jira",
|
|
494
|
-
"linear",
|
|
495
|
-
"notion",
|
|
496
|
-
"figma",
|
|
497
|
-
"stripe",
|
|
498
|
-
"supabase",
|
|
499
|
-
"firebase"
|
|
500
|
-
],
|
|
501
|
-
writeVerbs: [
|
|
502
|
-
"add",
|
|
503
|
-
"approve",
|
|
504
|
-
"archive",
|
|
505
|
-
"assign",
|
|
506
|
-
"close",
|
|
507
|
-
"comment",
|
|
508
|
-
"create",
|
|
509
|
-
"delete",
|
|
510
|
-
"deploy",
|
|
511
|
-
"dispatch",
|
|
512
|
-
"merge",
|
|
513
|
-
"open",
|
|
514
|
-
"post",
|
|
515
|
-
"publish",
|
|
516
|
-
"push",
|
|
517
|
-
"release",
|
|
518
|
-
"remove",
|
|
519
|
-
"reopen",
|
|
520
|
-
"run",
|
|
521
|
-
"send",
|
|
522
|
-
"submit",
|
|
523
|
-
"trigger",
|
|
524
|
-
"update",
|
|
525
|
-
"upload",
|
|
526
|
-
"write"
|
|
527
|
-
],
|
|
528
|
-
safeVerbs: [
|
|
529
|
-
"fetch",
|
|
530
|
-
"find",
|
|
531
|
-
"get",
|
|
532
|
-
"inspect",
|
|
533
|
-
"list",
|
|
534
|
-
"read",
|
|
535
|
-
"search",
|
|
536
|
-
"show",
|
|
537
|
-
"view"
|
|
538
|
-
]
|
|
539
|
-
},
|
|
540
|
-
finalGate: {
|
|
541
|
-
defaultMode: "enforce",
|
|
542
|
-
requireTaskContract: true,
|
|
543
|
-
requireContextManifest: true,
|
|
544
|
-
requireVerifyEvidence: true,
|
|
545
|
-
requireTrace: true,
|
|
546
|
-
requirePassingVerify: true
|
|
547
|
-
},
|
|
548
|
-
orchestrationPolicy: DEFAULT_ORCHESTRATION_POLICY
|
|
549
|
-
};
|
|
393
|
+
const DEFAULT_POLICY = fallbackBasePolicy(CONTEXT_INDEX_FILE, DEFAULT_ORCHESTRATION_POLICY);
|
|
550
394
|
|
|
551
395
|
// Which platform supplies the adapters is fixed by where this file is installed,
|
|
552
396
|
// so it is resolved once here rather than threaded through every profile load.
|
|
@@ -1007,7 +851,12 @@ function inspectPathInputsFromInput(
|
|
|
1007
851
|
const seen = new Set<string>();
|
|
1008
852
|
const walked = walkStringInputs(input, [], 0, includeFilesystemContentFields);
|
|
1009
853
|
for (const item of walked.items) {
|
|
1010
|
-
const
|
|
854
|
+
const relative = normalizeRelative(cwd, item.value);
|
|
855
|
+
// `path.relative(root, root)` is the empty string. Treat that canonical
|
|
856
|
+
// project-root spelling as `.` so a capability such as `**/*` admits
|
|
857
|
+
// `ls .` and an absolute path equal to cwd. The empty candidate matched no
|
|
858
|
+
// scope and made fresh helpers unable to inspect even their own project.
|
|
859
|
+
const normalized = relative === "" ? "." : relative;
|
|
1011
860
|
if (normalized === undefined) continue;
|
|
1012
861
|
const key = `${item.field}\0${normalized}`;
|
|
1013
862
|
if (seen.has(key)) continue;
|
|
@@ -1117,6 +966,7 @@ function evaluatePathLikeToolAccess(
|
|
|
1117
966
|
forceScopeAware?: boolean;
|
|
1118
967
|
forceWrite?: boolean;
|
|
1119
968
|
allowAmbiguousFilesystemContentFields?: boolean;
|
|
969
|
+
sourceCheckoutReadRoots?: string[];
|
|
1120
970
|
} = {}
|
|
1121
971
|
): { block: boolean; reason?: string } {
|
|
1122
972
|
const scopeAwareTool = options.forceScopeAware === true || ["read", "write", "edit", "grep", "find", "ls"].includes(toolName)
|
|
@@ -1158,9 +1008,13 @@ function evaluatePathLikeToolAccess(
|
|
|
1158
1008
|
}
|
|
1159
1009
|
|
|
1160
1010
|
for (const item of inspectedPaths) {
|
|
1011
|
+
const fieldAccessMode = filesystemFieldAccessMode(toolName, item.field, writesFilesystem);
|
|
1012
|
+
const sourceCheckoutReadRoot = fieldAccessMode === "read"
|
|
1013
|
+
? grantedSourceCheckoutRootForPath(cwd, item.path, options.sourceCheckoutReadRoots ?? [])
|
|
1014
|
+
: undefined;
|
|
1161
1015
|
if (scopeAwareTool && isFilesystemScopeField(item.field)) {
|
|
1162
1016
|
const boundary = inspectRepositoryPathBoundary(cwd, item.path);
|
|
1163
|
-
if (boundary.reason) return { block: true, reason: `Blocked ${toolName}: ${boundary.reason}` };
|
|
1017
|
+
if (boundary.reason && !sourceCheckoutReadRoot) return { block: true, reason: `Blocked ${toolName}: ${boundary.reason}` };
|
|
1164
1018
|
}
|
|
1165
1019
|
const resolvedPath = resolveRepositoryPathCandidate(cwd, item.path);
|
|
1166
1020
|
const readMatched = matchesProtectedPath(item.path, readProtectedPaths)
|
|
@@ -1172,7 +1026,6 @@ function evaluatePathLikeToolAccess(
|
|
|
1172
1026
|
};
|
|
1173
1027
|
}
|
|
1174
1028
|
|
|
1175
|
-
const fieldAccessMode = filesystemFieldAccessMode(toolName, item.field, writesFilesystem);
|
|
1176
1029
|
if (fieldAccessMode === "write") {
|
|
1177
1030
|
const readOnlyMatched = matchesProtectedPath(item.path, readOnlyPaths)
|
|
1178
1031
|
?? (resolvedPath ? matchesProtectedPath(resolvedPath, readOnlyPaths) : undefined);
|
|
@@ -1196,7 +1049,8 @@ function evaluatePathLikeToolAccess(
|
|
|
1196
1049
|
reason: `Blocked ${toolName} write outside resolved filesystem scope from ${item.field}: ${item.path}`
|
|
1197
1050
|
};
|
|
1198
1051
|
}
|
|
1199
|
-
} else if (scopeAwareTool && filesystemRead && isFilesystemScopeField(item.field)
|
|
1052
|
+
} else if (scopeAwareTool && filesystemRead && isFilesystemScopeField(item.field)
|
|
1053
|
+
&& !sourceCheckoutReadRoot && !matchesAnyPath(item.path, filesystemRead)) {
|
|
1200
1054
|
return {
|
|
1201
1055
|
block: true,
|
|
1202
1056
|
reason: `Blocked ${toolName} read outside resolved filesystem scope from ${item.field}: ${item.path}`
|
|
@@ -1227,6 +1081,21 @@ function evaluatePathLikeToolAccess(
|
|
|
1227
1081
|
return { block: false };
|
|
1228
1082
|
}
|
|
1229
1083
|
|
|
1084
|
+
function grantedSourceCheckoutRootForPath(cwd: string, candidate: string, roots: string[]): string | undefined {
|
|
1085
|
+
if (roots.length === 0) return undefined;
|
|
1086
|
+
const absolute = path.resolve(cwd, normalizePathCandidate(candidate));
|
|
1087
|
+
let canonical: string;
|
|
1088
|
+
try {
|
|
1089
|
+
canonical = fs.realpathSync.native(absolute);
|
|
1090
|
+
} catch {
|
|
1091
|
+
return undefined;
|
|
1092
|
+
}
|
|
1093
|
+
return roots.find((root) => {
|
|
1094
|
+
const relative = path.relative(root, canonical);
|
|
1095
|
+
return relative === "" || (relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative));
|
|
1096
|
+
});
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1230
1099
|
function stateRoot(cwd: string): string {
|
|
1231
1100
|
return path.join(cwd, ".pi", "piagent-state");
|
|
1232
1101
|
}
|
|
@@ -1395,6 +1264,7 @@ function evaluatePermissionProfileToolAccess(
|
|
|
1395
1264
|
function contextBudgetConfig(policy: BasePolicy): Required<ContextBudgetConfig> {
|
|
1396
1265
|
return {
|
|
1397
1266
|
defaultMode: policy.contextBudget?.defaultMode ?? DEFAULT_POLICY.contextBudget?.defaultMode ?? "enforce",
|
|
1267
|
+
contextDeltaShadow: policy.contextBudget?.contextDeltaShadow ?? DEFAULT_POLICY.contextBudget?.contextDeltaShadow ?? "off",
|
|
1398
1268
|
maxContextFileChars: policy.contextBudget?.maxContextFileChars ?? DEFAULT_POLICY.contextBudget?.maxContextFileChars ?? 50000,
|
|
1399
1269
|
maxMemoryFileChars: policy.contextBudget?.maxMemoryFileChars ?? DEFAULT_POLICY.contextBudget?.maxMemoryFileChars ?? 20000,
|
|
1400
1270
|
maxManifestFiles: policy.contextBudget?.maxManifestFiles ?? DEFAULT_POLICY.contextBudget?.maxManifestFiles ?? 80,
|
|
@@ -1503,7 +1373,7 @@ function resolveOrchestrationPolicy(profile: ProjectProfile, policy: BasePolicy)
|
|
|
1503
1373
|
};
|
|
1504
1374
|
return {
|
|
1505
1375
|
defaultMode: normalizeOrchestrationMode(configured.defaultMode, defaultPolicy.defaultMode),
|
|
1506
|
-
maxConcurrentSubagents: boundedInteger(configured.maxConcurrentSubagents, defaultPolicy.maxConcurrentSubagents, 0,
|
|
1376
|
+
maxConcurrentSubagents: Math.min(1, boundedInteger(configured.maxConcurrentSubagents, defaultPolicy.maxConcurrentSubagents, 0, 1)),
|
|
1507
1377
|
defaultReviewLenses: normalizeReviewLenses(configured.defaultReviewLenses, defaultPolicy.defaultReviewLenses),
|
|
1508
1378
|
roleModelGuidance,
|
|
1509
1379
|
fieldGuide: {
|
|
@@ -1544,9 +1414,10 @@ function normalizeWorkPlanSteps(values: unknown): WorkPlanStep[] {
|
|
|
1544
1414
|
function defaultWorkPlan(
|
|
1545
1415
|
summary: string,
|
|
1546
1416
|
riskLane: TaskContract["riskLane"],
|
|
1547
|
-
changeMode: TaskContract["changeMode"]
|
|
1417
|
+
changeMode: TaskContract["changeMode"],
|
|
1418
|
+
mutationPolicy: NonNullable<TaskContract["mutationPolicy"]> = changeMode === "read-only" ? "forbidden" : "required"
|
|
1548
1419
|
): WorkPlanStep[] {
|
|
1549
|
-
if (changeMode === "read-only") {
|
|
1420
|
+
if (changeMode === "read-only" || mutationPolicy === "forbidden") {
|
|
1550
1421
|
if (riskLane === "tiny") {
|
|
1551
1422
|
return [{
|
|
1552
1423
|
id: "scout",
|
|
@@ -1751,6 +1622,69 @@ function collectPatchTargetPaths(value: unknown, key = "", depth = 0): string[]
|
|
|
1751
1622
|
return paths;
|
|
1752
1623
|
}
|
|
1753
1624
|
|
|
1625
|
+
type ApplyPatchTargetInspection =
|
|
1626
|
+
| { carrier: false; exactLocalExecutor: false; targets: [] }
|
|
1627
|
+
| { carrier: true; exactLocalExecutor: boolean; targets: string[]; reason?: undefined }
|
|
1628
|
+
| { carrier: true; exactLocalExecutor: boolean; targets: []; reason: string };
|
|
1629
|
+
|
|
1630
|
+
function isApplyPatchToolName(toolName: string): boolean {
|
|
1631
|
+
const tokens = new Set(actionTokens(toolName));
|
|
1632
|
+
// Pi direct MCP tools are named <server-prefix>_<tool>, for example
|
|
1633
|
+
// filesystem_apply_patch; they never carry an mcp__ prefix. Treat every
|
|
1634
|
+
// apply+patch action carrier as this exact fail-closed protocol so a custom
|
|
1635
|
+
// prefix cannot bypass target extraction.
|
|
1636
|
+
return tokens.has("apply") && tokens.has("patch");
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
function inspectApplyPatchAuthorizationTargets(
|
|
1640
|
+
toolName: string,
|
|
1641
|
+
input: Record<string, unknown>
|
|
1642
|
+
): ApplyPatchTargetInspection {
|
|
1643
|
+
let carrier = toolName;
|
|
1644
|
+
let payload = input;
|
|
1645
|
+
const exactLocalExecutor = toolName === "apply_patch";
|
|
1646
|
+
if (normalizeActionToken(toolName) === "mcp") {
|
|
1647
|
+
const proxyTool = typeof input.tool === "string" ? input.tool.trim() : "";
|
|
1648
|
+
if (!isApplyPatchToolName(proxyTool)) return { carrier: false, exactLocalExecutor: false, targets: [] };
|
|
1649
|
+
carrier = proxyTool;
|
|
1650
|
+
if (typeof input.args !== "string" || input.args.length > MAX_MCP_PROXY_ARGS_CHARS) {
|
|
1651
|
+
return { carrier: true, exactLocalExecutor: false, targets: [], reason: "MCP apply_patch args must be a bounded JSON object string" };
|
|
1652
|
+
}
|
|
1653
|
+
try {
|
|
1654
|
+
const parsed = JSON.parse(input.args);
|
|
1655
|
+
if (!isPlainRecord(parsed)) return { carrier: true, exactLocalExecutor: false, targets: [], reason: "MCP apply_patch args must decode to a JSON object" };
|
|
1656
|
+
payload = parsed;
|
|
1657
|
+
} catch {
|
|
1658
|
+
return { carrier: true, exactLocalExecutor: false, targets: [], reason: "MCP apply_patch args must be valid JSON" };
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
if (!isApplyPatchToolName(carrier)) return { carrier: false, exactLocalExecutor: false, targets: [] };
|
|
1662
|
+
const keys = Object.keys(payload);
|
|
1663
|
+
if (keys.length !== 1 || keys[0] !== "patch" || typeof payload.patch !== "string") {
|
|
1664
|
+
return { carrier: true, exactLocalExecutor, targets: [], reason: "apply_patch input must contain only one string field named patch" };
|
|
1665
|
+
}
|
|
1666
|
+
try {
|
|
1667
|
+
return { carrier: true, exactLocalExecutor, targets: parseApplyPatchTargets(payload.patch) };
|
|
1668
|
+
} catch (error) {
|
|
1669
|
+
return {
|
|
1670
|
+
carrier: true,
|
|
1671
|
+
exactLocalExecutor,
|
|
1672
|
+
targets: [],
|
|
1673
|
+
reason: error instanceof Error ? error.message : "apply_patch target extraction failed"
|
|
1674
|
+
};
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
function authorizationMutationTargetInspection(
|
|
1679
|
+
cwd: string,
|
|
1680
|
+
toolName: string,
|
|
1681
|
+
input: Record<string, unknown>
|
|
1682
|
+
): { targets: string[]; patchCarrier: boolean; exactLocalPatchExecutor: boolean; reason?: string } {
|
|
1683
|
+
const patch = inspectApplyPatchAuthorizationTargets(toolName, input);
|
|
1684
|
+
if (patch.carrier) return { targets: patch.targets, patchCarrier: true, exactLocalPatchExecutor: patch.exactLocalExecutor, reason: patch.reason };
|
|
1685
|
+
return { targets: taskMutationTargets(cwd, toolName, input), patchCarrier: false, exactLocalPatchExecutor: false };
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1754
1688
|
function taskMutationIdentity(toolName: string, input: Record<string, unknown>): string {
|
|
1755
1689
|
if (normalizeActionToken(toolName) !== "mcp") return toolName;
|
|
1756
1690
|
const proxyTool = typeof input.tool === "string" ? input.tool.trim() : "";
|
|
@@ -1772,13 +1706,18 @@ function isReadOnlyTaskShellCommand(
|
|
|
1772
1706
|
if (/[<>]|`|\$\(/.test(command)) {
|
|
1773
1707
|
return false;
|
|
1774
1708
|
}
|
|
1775
|
-
const safeCommands = new Set(["pwd", "ls", "find", "rg", "grep", "cat", "sed", "head", "tail", "wc", "stat", "file", "test", "[", "which"]);
|
|
1709
|
+
const safeCommands = new Set(["pwd", "ls", "find", "rg", "grep", "cat", "sed", "head", "tail", "wc", "stat", "file", "test", "[", "which", "printf", "sort"]);
|
|
1776
1710
|
const safeGitSubcommands = new Set(["status", "diff", "log", "show", "ls-files", "rev-parse"]);
|
|
1777
1711
|
return segments.length > 0 && segments.every((segment) => {
|
|
1778
1712
|
const words = segment.words.filter(Boolean);
|
|
1779
1713
|
const executable = path.basename(words[0] ?? "");
|
|
1780
1714
|
if (executable === "sed" && words.some((word) => /^-.*i/.test(word))) return false;
|
|
1781
1715
|
if (executable === "find" && words.some((word) => ["-delete", "-exec", "-execdir", "-ok", "-okdir", "-fprint", "-fprint0", "-fprintf", "-fls"].includes(word))) return false;
|
|
1716
|
+
if (executable === "sort" && words.slice(1).some((word) => (
|
|
1717
|
+
word === "--output"
|
|
1718
|
+
|| word.startsWith("--output=")
|
|
1719
|
+
|| /^-[^-]*o/.test(word)
|
|
1720
|
+
))) return false;
|
|
1782
1721
|
if (safeCommands.has(executable)) return true;
|
|
1783
1722
|
if (executable === "git") return safeGitSubcommands.has(words[1] ?? "");
|
|
1784
1723
|
if (executable === "command") return words[1] === "-v";
|
|
@@ -1786,6 +1725,16 @@ function isReadOnlyTaskShellCommand(
|
|
|
1786
1725
|
});
|
|
1787
1726
|
}
|
|
1788
1727
|
|
|
1728
|
+
function shellTouchesGrantedSourceCheckout(cwd: string, command: string, roots: string[]): boolean {
|
|
1729
|
+
if (roots.length === 0) return false;
|
|
1730
|
+
for (const root of roots) {
|
|
1731
|
+
const relative = path.relative(cwd, root).split(path.sep).join("/");
|
|
1732
|
+
if (command.includes(root) || (relative && command.includes(relative))) return true;
|
|
1733
|
+
}
|
|
1734
|
+
return extractShellPathCandidates(command)
|
|
1735
|
+
.some((candidate) => Boolean(grantedSourceCheckoutRootForPath(cwd, normalizeRelative(cwd, candidate) ?? candidate, roots)));
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1789
1738
|
const PROJECT_MUTATING_EXECUTABLES = new Set([
|
|
1790
1739
|
"apply_patch", "bash", "chmod", "chown", "cp", "dd", "install", "ln", "make", "mkdir", "mv", "prename", "rename",
|
|
1791
1740
|
"node", "patch", "perl", "php", "python", "python3", "rm", "rmdir", "rsync", "ruby", "scp", "sh",
|
|
@@ -2102,7 +2051,7 @@ function flushObservedTaskContext(
|
|
|
2102
2051
|
const task = activeSessionTask(ctx.cwd, ctx.sessionManager.getSessionId()) as TaskContract | undefined;
|
|
2103
2052
|
if (!task || task.trace.outcome !== "pending") return task;
|
|
2104
2053
|
const added = mergeObservedTaskContext(task, pendingContext, maxManifestFiles, redactText);
|
|
2105
|
-
const lifecycle = task.changeMode === "read-only"
|
|
2054
|
+
const lifecycle = (task.changeMode === "read-only" || task.mutationPolicy === "forbidden") && hasDurableContextEvidence(task)
|
|
2106
2055
|
? applyRuntimeLifecycleObservation(task, "context-complete", nowIso())
|
|
2107
2056
|
: { changed: false, mode: runtimeLifecycleMode(task) };
|
|
2108
2057
|
if (added.length === 0 && !lifecycle.changed) return task;
|
|
@@ -2173,9 +2122,13 @@ function prepareToolInputForPolicy(
|
|
|
2173
2122
|
return { input, reason: "MCP proxy args must be valid JSON" };
|
|
2174
2123
|
}
|
|
2175
2124
|
if (!isPlainRecord(parsed)) return { input, reason: "MCP proxy args must decode to a JSON object" };
|
|
2176
|
-
const
|
|
2177
|
-
|
|
2178
|
-
|
|
2125
|
+
const exactPatch = inspectApplyPatchAuthorizationTargets(toolName, input);
|
|
2126
|
+
if (exactPatch.carrier && exactPatch.reason) return { input, reason: exactPatch.reason };
|
|
2127
|
+
const patchTargets = exactPatch.carrier
|
|
2128
|
+
? exactPatch.targets
|
|
2129
|
+
: actionTokens(proxyTool).includes("patch")
|
|
2130
|
+
? collectPatchTargetPaths(parsed)
|
|
2131
|
+
: [];
|
|
2179
2132
|
policyInput = patchTargets.length > 0
|
|
2180
2133
|
? { ...input, proxyArgs: parsed, proxyPatchTargets: { paths: patchTargets } }
|
|
2181
2134
|
: { ...input, proxyArgs: parsed };
|
|
@@ -3572,7 +3525,7 @@ function taskScopeIncludesPath(scope: string[], file: string): boolean {
|
|
|
3572
3525
|
}
|
|
3573
3526
|
|
|
3574
3527
|
function verifierCommandInstructions(commands: string[]): string[] {
|
|
3575
|
-
return commands.map((command, index) => `Verifier ${index + 1} (run as
|
|
3528
|
+
return commands.map((command, index) => `Verifier ${index + 1} (run as an exact standalone shell command): ${command}`);
|
|
3576
3529
|
}
|
|
3577
3530
|
|
|
3578
3531
|
function evaluateTaskGate(
|
|
@@ -3605,7 +3558,7 @@ function evaluateTaskGate(
|
|
|
3605
3558
|
if (task.attempt > task.maxAttempts) missing.push(`attempt within maxAttempts (${task.attempt}/${task.maxAttempts})`);
|
|
3606
3559
|
const plannedVerifyCommands = meaningfulVerificationCommands(task.verifyCommands);
|
|
3607
3560
|
if (task.changeMode === "source-change" && plannedVerifyCommands.length === 0) missing.push("meaningful verify command");
|
|
3608
|
-
if (finalGate.requireContextManifest && task
|
|
3561
|
+
if (finalGate.requireContextManifest && !hasDurableContextEvidence(task)) missing.push("context manifest");
|
|
3609
3562
|
if (task.changeMode === "source-change" && finalGate.requireVerifyEvidence && task.verifyEvidence.length === 0) missing.push("verify evidence");
|
|
3610
3563
|
if (task.verifyEvidence.some((evidence) => evidence.observed !== true)) {
|
|
3611
3564
|
warnings.push("Unobserved verify evidence is ignored by the passing verify gate.");
|
|
@@ -3632,14 +3585,16 @@ function evaluateTaskGate(
|
|
|
3632
3585
|
missing.push(`completed work plan (${incompleteSteps.map((step) => `${step.id}:${step.status}`).join(", ")})`);
|
|
3633
3586
|
}
|
|
3634
3587
|
const changedFileEvidence = taskChangedFileEvidence(cwd, task, currentDigests);
|
|
3635
|
-
if (task.changeMode === "source-change" && task.trace.outcome === "completed") {
|
|
3588
|
+
if (task.changeMode === "source-change" && task.trace.outcome === "completed" && task.mutationPolicy !== "forbidden") {
|
|
3636
3589
|
if (task.changedFiles.length === 0) missing.push("changed files");
|
|
3637
3590
|
if (changedFileEvidence.undeclared.length > 0) missing.push(`declared observed changes (${changedFileEvidence.undeclared.join(", ")})`);
|
|
3638
3591
|
if (changedFileEvidence.unsupportedClaims.length > 0) missing.push(`supported changed-file claims (${changedFileEvidence.unsupportedClaims.join(", ")})`);
|
|
3639
|
-
if (changedFileEvidence.outsideScope.length > 0)
|
|
3592
|
+
if (changedFileEvidence.outsideScope.length > 0) {
|
|
3593
|
+
warnings.push(`Task focus expanded beyond the initial scope (${changedFileEvidence.outsideScope.join(", ")}).`);
|
|
3594
|
+
}
|
|
3640
3595
|
}
|
|
3641
|
-
if (task.changeMode === "read-only" && changedFileEvidence.expected.length > 0) {
|
|
3642
|
-
missing.push(`
|
|
3596
|
+
if ((task.changeMode === "read-only" || task.mutationPolicy === "forbidden") && changedFileEvidence.expected.length > 0) {
|
|
3597
|
+
missing.push(`mutation-forbidden task has observed changes (${changedFileEvidence.expected.join(", ")})`);
|
|
3643
3598
|
}
|
|
3644
3599
|
const acceptance = refreshAcceptanceReceipt(task, {
|
|
3645
3600
|
cwd,
|
|
@@ -3647,9 +3602,11 @@ function evaluateTaskGate(
|
|
|
3647
3602
|
currentWorkingTreeDigest
|
|
3648
3603
|
});
|
|
3649
3604
|
const semanticEnforcement = taskAuthorityDecision(task, "CAP-13", "block").allowed;
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3605
|
+
const abstainedIds = new Set(acceptance.adapterAbstained.map((criterion) => criterion.id));
|
|
3606
|
+
const adapterCritical = acceptance.criticalMissing.filter((criterion) => abstainedIds.has(criterion.id));
|
|
3607
|
+
const linkedCritical = acceptance.criticalMissing.filter((criterion) => !abstainedIds.has(criterion.id));
|
|
3608
|
+
if (semanticEnforcement && linkedCritical.length > 0) missing.push(`critical acceptance evidence (${linkedCritical.map((criterion) => `${criterion.id}:${criterion.obligation}`).join(", ")})`);
|
|
3609
|
+
if (semanticEnforcement && adapterCritical.length > 0) missing.push(`critical acceptance evidence adapter-unresolved (${adapterCritical.map((criterion) => `${criterion.id}:${criterion.obligation}`).join(", ")}); remedy: add a direct-relative focused test or configure a deterministic adapter`);
|
|
3653
3610
|
const acceptanceConflicts = acceptanceSemanticConflicts(task, {
|
|
3654
3611
|
cwd,
|
|
3655
3612
|
changedFiles: changedFileEvidence.expected
|
|
@@ -3657,7 +3614,11 @@ function evaluateTaskGate(
|
|
|
3657
3614
|
if (semanticEnforcement && acceptanceConflicts.length > 0) {
|
|
3658
3615
|
missing.push(`acceptance semantic conflicts (${acceptanceConflicts.join(", ")})`);
|
|
3659
3616
|
}
|
|
3660
|
-
const
|
|
3617
|
+
const criticalIds = new Set(acceptance.criticalMissing.map((criterion) => criterion.id));
|
|
3618
|
+
const normalMissing = acceptance.missing.filter((criterion) => !criticalIds.has(criterion.id));
|
|
3619
|
+
if (semanticEnforcement && acceptance.adapterAbstained.length > 0) {
|
|
3620
|
+
warnings.push(`Acceptance proof adapter abstained; rigorous criteria remain pending and hand off without an automatic model retry: ${acceptance.adapterAbstained.map((criterion) => `${criterion.id}:${criterion.obligation}`).join(", ")}`);
|
|
3621
|
+
}
|
|
3661
3622
|
if (!semanticEnforcement && (acceptance.criticalMissing.length > 0 || acceptanceConflicts.length > 0)) warnings.push("Acceptance projection is advisory under the pinned task authority and cannot block completion.");
|
|
3662
3623
|
if (normalMissing.length > 0) {
|
|
3663
3624
|
warnings.push(`Acceptance criteria pending evidence: ${normalMissing.map((criterion) => `${criterion.id}:${criterion.obligation}`).join(", ")}`);
|
|
@@ -3837,6 +3798,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
3837
3798
|
const runtimeState = new RuntimeSessionState({
|
|
3838
3799
|
maxObservedContext: contextBudgetConfig(policy).maxManifestFiles
|
|
3839
3800
|
});
|
|
3801
|
+
const editFreshnessGuard = new EditFreshnessGuard(editFreshnessModeFromEnvironment(process.env.PIAGENT_EDIT_FRESHNESS));
|
|
3840
3802
|
const sourceMutationGuardBindings = createSourceMutationGuardBindings(policy, loadProfileFromContext);
|
|
3841
3803
|
const runtimeSnapshotCapture = new RuntimeSnapshotCapture(), runtimeVersions = readRuntimeVersionMetadata(PLATFORM_ROOT);
|
|
3842
3804
|
const solverShadow = solverMode === "off" ? undefined : new SolverShadowRuntime(solverMode);
|
|
@@ -3867,6 +3829,55 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
3867
3829
|
pi.on("thinking_level_select", (_event, ctx) => {
|
|
3868
3830
|
modelSelectionProvenance.observeThinkingSelection(ctx.sessionManager.getSessionId());
|
|
3869
3831
|
});
|
|
3832
|
+
pi.on("before_provider_request", (event, ctx) => {
|
|
3833
|
+
const normalized = normalizeOpenAiCodexReasoningPayload({
|
|
3834
|
+
payload: event.payload,
|
|
3835
|
+
provider: ctx.model?.provider,
|
|
3836
|
+
modelId: ctx.model?.id,
|
|
3837
|
+
hostThinkingLevel: pi.getThinkingLevel()
|
|
3838
|
+
});
|
|
3839
|
+
if (normalized.applicable) {
|
|
3840
|
+
telemetry(ctx, {
|
|
3841
|
+
event: "provider_request_reasoning",
|
|
3842
|
+
hostThinkingLevel: normalized.hostThinkingLevel,
|
|
3843
|
+
providerReasoningEffort: normalized.providerEffort,
|
|
3844
|
+
expectedProviderReasoningEffort: normalized.expectedProviderEffort,
|
|
3845
|
+
providerReasoningEffortMatches: normalized.providerEffort === normalized.expectedProviderEffort,
|
|
3846
|
+
providerReasoningNormalized: normalized.changed,
|
|
3847
|
+
providerReasoningReasonCode: normalized.reasonCode
|
|
3848
|
+
});
|
|
3849
|
+
}
|
|
3850
|
+
const wire = buildOpenAiCodexWireFingerprint({
|
|
3851
|
+
payload: normalized.changed ? normalized.payload : event.payload,
|
|
3852
|
+
provider: ctx.model?.provider,
|
|
3853
|
+
modelId: ctx.model?.id,
|
|
3854
|
+
workingDirectory: ctx.cwd,
|
|
3855
|
+
platformRoot: PLATFORM_ROOT
|
|
3856
|
+
});
|
|
3857
|
+
if (wire.applicable) {
|
|
3858
|
+
telemetry(ctx, {
|
|
3859
|
+
event: "provider_request_wire_surface",
|
|
3860
|
+
state: wire.state,
|
|
3861
|
+
reasonCode: wire.reasonCode,
|
|
3862
|
+
providerModelId: wire.modelId,
|
|
3863
|
+
instructionsHash: wire.instructionsHash,
|
|
3864
|
+
baseInstructionsHash: wire.baseInstructionsHash,
|
|
3865
|
+
instructionNormalization: wire.instructionNormalization,
|
|
3866
|
+
instructionChars: wire.instructionChars,
|
|
3867
|
+
orderedToolSurfaceHash: wire.orderedToolSurfaceHash,
|
|
3868
|
+
providerToolCount: wire.toolCount,
|
|
3869
|
+
deferredToolSurfaceHash: wire.deferredToolSurfaceHash,
|
|
3870
|
+
deferredToolCount: wire.deferredToolCount,
|
|
3871
|
+
deferredToolBatchCount: wire.deferredToolBatchCount,
|
|
3872
|
+
providerReasoningEffort: wire.reasoningEffort,
|
|
3873
|
+
providerTextVerbosity: wire.textVerbosity,
|
|
3874
|
+
providerToolChoiceKind: wire.toolChoiceKind,
|
|
3875
|
+
providerToolChoiceHash: wire.toolChoiceHash,
|
|
3876
|
+
requestPrefixFingerprint: wire.requestPrefixFingerprint
|
|
3877
|
+
});
|
|
3878
|
+
}
|
|
3879
|
+
return normalized.changed ? normalized.payload : undefined;
|
|
3880
|
+
});
|
|
3870
3881
|
|
|
3871
3882
|
function freshModelRouteBoundary(ctx: ExtensionContext): boolean {
|
|
3872
3883
|
try {
|
|
@@ -3928,7 +3939,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
3928
3939
|
let taskIdentity = runtimeState.taskIdentity(ctx);
|
|
3929
3940
|
if (!taskIdentity) {
|
|
3930
3941
|
const task = activeSessionTask(ctx.cwd, ctx.sessionManager.getSessionId()) as TaskContract | undefined;
|
|
3931
|
-
if (task) {
|
|
3942
|
+
if (task?.trace.outcome === "pending") {
|
|
3932
3943
|
runtimeState.cacheTaskIdentity(ctx, task);
|
|
3933
3944
|
taskIdentity = { taskId: task.taskId, taskRunId: task.taskRunId };
|
|
3934
3945
|
}
|
|
@@ -4010,8 +4021,11 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4010
4021
|
failed?.exitCode ?? 1
|
|
4011
4022
|
);
|
|
4012
4023
|
const trajectory = trajectoryRuntime.status(ctx.cwd, task.taskRunId);
|
|
4013
|
-
|
|
4014
|
-
|
|
4024
|
+
// Task scope is a retrieval/review hint, not filesystem authority. Recovery
|
|
4025
|
+
// may update a dependency or config file when repository evidence requires
|
|
4026
|
+
// it; protected-path and approval policy still run on the exact call.
|
|
4027
|
+
const dependencyMutationAuthorized = true;
|
|
4028
|
+
const selected = selectRecoveryDecision({
|
|
4015
4029
|
featureEnabled: autoRecoveryEnabled && taskAuthorityDecision(task, "CAP-12", "model-turn").allowed,
|
|
4016
4030
|
task: {
|
|
4017
4031
|
taskId: task.taskId,
|
|
@@ -4030,6 +4044,9 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4030
4044
|
: true,
|
|
4031
4045
|
dependencyMutationAuthorized
|
|
4032
4046
|
});
|
|
4047
|
+
return gate?.missing.some((item) => /^critical acceptance evidence adapter-unresolved\b/i.test(item))
|
|
4048
|
+
? { ...selected, action: "handoff", continuation: "none", nextPhase: null, sourceMutationAllowed: false, reasonCodes: ["unknown-diagnostic-exhausted"] }
|
|
4049
|
+
: selected;
|
|
4033
4050
|
}
|
|
4034
4051
|
|
|
4035
4052
|
function trajectoryRecoveryOptions(ctx: ExtensionContext, task: TaskContract, options: any): any {
|
|
@@ -4067,6 +4084,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4067
4084
|
resolveTaskAny: (cwd, reference) => resolveTaskContract(cwd, reference, undefined) as TaskContract | undefined,
|
|
4068
4085
|
bindTask: bindSessionTask,
|
|
4069
4086
|
writeTask,
|
|
4087
|
+
appendTrace,
|
|
4070
4088
|
capabilityState: (ctx) => verifyProjectCapabilityState(extensionDir, ctx.cwd, ctx.isProjectTrusted(), { allowRepin: true, forceFull: true, sessionId: ctx.sessionManager.getSessionId() }),
|
|
4071
4089
|
permissionProfile: (ctx, profile) => resolvePermissionProfile(profile, policy, permissionOverrideFromContext(ctx)),
|
|
4072
4090
|
legacyProjectWarning: legacyProjectStateWarning,
|
|
@@ -4080,7 +4098,8 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4080
4098
|
const snapshot = workingTreeSnapshot(ctx.cwd) as Record<string, string>;
|
|
4081
4099
|
const resume = workingTreeSnapshotHasUnavailableEvidence(snapshot)
|
|
4082
4100
|
? { openRepair: false }
|
|
4083
|
-
: { openRepair: taskAuthorityDecision(task, "CAP-13", "block").allowed
|
|
4101
|
+
: { openRepair: (taskAuthorityDecision(task, "CAP-13", "block").allowed
|
|
4102
|
+
|| taskAuthorityDecision(task, "CAP-12", "mutate").allowed) && semanticRepairRuntime.resume({
|
|
4084
4103
|
cwd: ctx.cwd,
|
|
4085
4104
|
task,
|
|
4086
4105
|
sessionId: ctx.sessionManager.getSessionId(),
|
|
@@ -4109,12 +4128,14 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4109
4128
|
onAgentSettled: activityInspector.refresh,
|
|
4110
4129
|
beforeShutdown: (ctx) => {
|
|
4111
4130
|
sourceMutationGuardBindings.unbind(ctx);
|
|
4131
|
+
editFreshnessGuard.clear(ctx);
|
|
4112
4132
|
activityInspector.dispose(ctx);
|
|
4113
4133
|
sessionCapabilityDigests.delete(`${ctx.cwd}\0${ctx.sessionManager.getSessionId()}`);
|
|
4114
4134
|
}
|
|
4115
4135
|
});
|
|
4116
4136
|
|
|
4117
4137
|
registerInputHook(pi, {
|
|
4138
|
+
state: runtimeState,
|
|
4118
4139
|
boilerplateCollapseChars: BOILERPLATE_COLLAPSE_CHARS,
|
|
4119
4140
|
activeTask: (ctx) => activeSessionTask(ctx.cwd, ctx.sessionManager.getSessionId()) as TaskContract | undefined, authorityPolicy: (ctx, task) => ensureTaskAuthorityResumePolicy(ctx.cwd, task, { authorityProfile: loadProfileFromContext(ctx).authorityProfile, environment: process.env }),
|
|
4120
4141
|
readProtectedPaths: (ctx) => effectiveProtectedPaths(policy, loadProfileFromContext(ctx)).readProtectedPaths,
|
|
@@ -4141,6 +4162,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4141
4162
|
registerAgentStartHook(pi, {
|
|
4142
4163
|
state: runtimeState,
|
|
4143
4164
|
autoContextEnabled,
|
|
4165
|
+
contextDeltaShadowMode: contextBudgetConfig(policy).contextDeltaShadow,
|
|
4144
4166
|
activeTask: (ctx) => activeSessionTask(ctx.cwd, ctx.sessionManager.getSessionId()) as TaskContract | undefined,
|
|
4145
4167
|
readProtectedPaths: (ctx) => effectiveProtectedPaths(policy, loadProfileFromContext(ctx)).readProtectedPaths,
|
|
4146
4168
|
contextExcludePatterns: (ctx) => contextIndexExcludePatterns(policy, loadProfileFromContext(ctx)),
|
|
@@ -4168,7 +4190,10 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4168
4190
|
completionProjection: completionTaskProjection,
|
|
4169
4191
|
evaluateGate: (cwd, task, currentDigests, currentDigest) => {
|
|
4170
4192
|
const gate = evaluateTaskGate(cwd, task, policy, { currentDigests, currentWorkingTreeDigest: currentDigest });
|
|
4171
|
-
const repairBlock = taskAuthorityDecision(task, "CAP-13", "block").allowed
|
|
4193
|
+
const repairBlock = (taskAuthorityDecision(task, "CAP-13", "block").allowed
|
|
4194
|
+
|| taskAuthorityDecision(task, "CAP-12", "mutate").allowed)
|
|
4195
|
+
? semanticRepairRuntime.completionBlock(cwd, task.taskRunId)
|
|
4196
|
+
: undefined;
|
|
4172
4197
|
return repairBlock ? { ...gate, decision: "fail", missing: [...new Set([...gate.missing, repairBlock])] } : gate;
|
|
4173
4198
|
},
|
|
4174
4199
|
writeTask,
|
|
@@ -4187,6 +4212,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4187
4212
|
state: runtimeState,
|
|
4188
4213
|
activeTask: (ctx) => activeSessionTask(ctx.cwd, ctx.sessionManager.getSessionId()) as TaskContract | undefined,
|
|
4189
4214
|
maxManifestFiles: contextBudgetConfig(policy).maxManifestFiles,
|
|
4215
|
+
flushObservedTaskContext,
|
|
4190
4216
|
readProtectedPaths: (ctx) => effectiveProtectedPaths(policy, loadProfileFromContext(ctx)).readProtectedPaths,
|
|
4191
4217
|
recordObservedBash: (observed) => bashResults.record(observed),
|
|
4192
4218
|
observedBashLedgerPath,
|
|
@@ -4200,9 +4226,42 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4200
4226
|
telemetry,
|
|
4201
4227
|
activity: recordActivity,
|
|
4202
4228
|
now: nowIso,
|
|
4229
|
+
recordSubagentResult: (ctx, event, result) => {
|
|
4230
|
+
const task = activeSessionTask(ctx.cwd, ctx.sessionManager.getSessionId()) as TaskContract | undefined;
|
|
4231
|
+
const written = recordDirectSubagentResult(task, event, result, {
|
|
4232
|
+
now: nowIso,
|
|
4233
|
+
persist: (candidate) => writeTask(ctx.cwd, candidate),
|
|
4234
|
+
trace: (payload) => appendTrace(ctx.cwd, payload),
|
|
4235
|
+
sessionTrace: (payload) => appendSessionTrace(pi, payload)
|
|
4236
|
+
});
|
|
4237
|
+
telemetry(ctx, {
|
|
4238
|
+
event: "helper_result_classified",
|
|
4239
|
+
recordedAt: nowIso(),
|
|
4240
|
+
taskRunId: written?.taskRunId,
|
|
4241
|
+
toolCallId: event.toolCallId,
|
|
4242
|
+
spawned: result.spawned,
|
|
4243
|
+
failed: result.failed,
|
|
4244
|
+
disposition: result.disposition,
|
|
4245
|
+
reasonCode: result.reasonCode,
|
|
4246
|
+
calls: result.calls,
|
|
4247
|
+
tokens: result.tokens
|
|
4248
|
+
});
|
|
4249
|
+
},
|
|
4250
|
+
observeEditFreshness: (ctx, event, metadata) => {
|
|
4251
|
+
if (!metadata.successful || !metadata.taskRunId) return;
|
|
4252
|
+
const protectedPaths = effectiveProtectedPaths(policy, loadProfileFromContext(ctx)).readProtectedPaths;
|
|
4253
|
+
const source = event.toolName === "read" ? "read" : ["edit", "write", "apply_patch"].includes(event.toolName) ? "mutation" : undefined;
|
|
4254
|
+
const candidates = source === "read" && metadata.targetPath ? [metadata.targetPath] : source === "mutation" ? metadata.mutationTargets : [];
|
|
4255
|
+
const safeTargets = candidates.filter((file) => !matchesProtectedPath(file, protectedPaths));
|
|
4256
|
+
const observed = source ? editFreshnessGuard.observe(ctx, metadata.taskRunId, safeTargets, source, nowIso()) : [];
|
|
4257
|
+
if (observed.length) telemetry(ctx, { event: "edit_freshness_snapshot_observed", taskRunId: metadata.taskRunId, source, paths: observed.map(redactText) });
|
|
4258
|
+
},
|
|
4203
4259
|
completeSemanticRepair: (ctx, event, metadata) => {
|
|
4204
4260
|
const task = activeSessionTask(ctx.cwd, ctx.sessionManager.getSessionId()) as TaskContract | undefined;
|
|
4205
|
-
return task && taskAuthorityDecision(task, "CAP-13", "mutate").allowed
|
|
4261
|
+
return task && (taskAuthorityDecision(task, "CAP-13", "mutate").allowed
|
|
4262
|
+
|| taskAuthorityDecision(task, "CAP-12", "mutate").allowed)
|
|
4263
|
+
? semanticRepairRuntime.complete(ctx, task, event, metadata)
|
|
4264
|
+
: undefined;
|
|
4206
4265
|
},
|
|
4207
4266
|
syncTrajectory: (ctx, contextObserved) => {
|
|
4208
4267
|
const task = activeSessionTask(ctx.cwd, ctx.sessionManager.getSessionId()) as TaskContract | undefined;
|
|
@@ -4216,10 +4275,51 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4216
4275
|
telemetry,
|
|
4217
4276
|
activity: recordActivity,
|
|
4218
4277
|
beforeAuthorize: (event, ctx) => {
|
|
4219
|
-
|
|
4278
|
+
const toolInput = isPlainRecord(event.input) ? event.input : {};
|
|
4220
4279
|
const task = activeSessionTask(ctx.cwd, ctx.sessionManager.getSessionId()) as TaskContract | undefined;
|
|
4280
|
+
const helperPreflight = evaluateDirectSubagentDispatch({
|
|
4281
|
+
toolName: event.toolName,
|
|
4282
|
+
toolInput,
|
|
4283
|
+
cwd: ctx.cwd,
|
|
4284
|
+
helpersMode: task ? taskHelperUsageMode(task) : helpersMode(),
|
|
4285
|
+
taskPending: task?.trace.outcome === "pending",
|
|
4286
|
+
dispatchAuthority: Boolean(task && taskAuthorityDecision(task, "CAP-14", "dispatch").allowed),
|
|
4287
|
+
durableSubagentState: task?.orchestration?.subagents
|
|
4288
|
+
});
|
|
4289
|
+
if (helperPreflight.applicable && helperPreflight.dispatch && !helperPreflight.allowed) {
|
|
4290
|
+
telemetry(ctx, {
|
|
4291
|
+
event: "helper_dispatch_blocked",
|
|
4292
|
+
recordedAt: nowIso(),
|
|
4293
|
+
taskRunId: task?.taskRunId,
|
|
4294
|
+
toolCallId: event.toolCallId,
|
|
4295
|
+
reasonCode: helperPreflight.reasonCode,
|
|
4296
|
+
role: helperPreflight.role,
|
|
4297
|
+
requestCount: helperPreflight.requestCount
|
|
4298
|
+
});
|
|
4299
|
+
return { block: true, reason: `Blocked ${event.toolName}: ${helperPreflight.reason} Do not retry this helper; continue in the parent.` };
|
|
4300
|
+
}
|
|
4301
|
+
const verifierReuse = reuseCurrentTreeExactVerifier({ cwd: ctx.cwd, task, toolName: event.toolName, toolInput, sessionEntries: ctx.sessionManager.getEntries() as unknown[] });
|
|
4302
|
+
if (verifierReuse.reused) {
|
|
4303
|
+
telemetry(ctx, { event: "verifier_evidence_reused", recordedAt: nowIso(), taskRunId: task?.taskRunId, toolCallId: event.toolCallId, reasonCode: verifierReuse.reasonCode, commandDigest: verifierReuse.commandDigest, workingTreeDigest: verifierReuse.workingTreeDigest }); return;
|
|
4304
|
+
}
|
|
4305
|
+
const targetInspection = authorizationMutationTargetInspection(ctx.cwd, event.toolName, toolInput);
|
|
4306
|
+
if (targetInspection.reason) return { block: true, reason: `Blocked ${event.toolName}: ${targetInspection.reason}.` };
|
|
4307
|
+
if (task?.trace.outcome === "pending" && task.changeMode === "source-change" && (event.toolName === "edit" || targetInspection.exactLocalPatchExecutor)) {
|
|
4308
|
+
const freshness = editFreshnessGuard.evaluate(ctx, task.taskRunId, targetInspection.targets);
|
|
4309
|
+
if (freshness.decision === "stale") {
|
|
4310
|
+
telemetry(ctx, { event: "edit_freshness_stale", taskRunId: task.taskRunId, toolCallId: event.toolCallId, enforce: freshness.enforce, paths: freshness.stalePaths.map(redactText) });
|
|
4311
|
+
if (freshness.enforce) return { block: true, reason: `Blocked ${event.toolName}: the previously observed source snapshot is stale for ${freshness.stalePaths.map(redactText).join(", ")}. Reread the affected file once; no patch hunk was started.` };
|
|
4312
|
+
}
|
|
4313
|
+
}
|
|
4314
|
+
if (!["edit", "write"].includes(event.toolName) && !targetInspection.exactLocalPatchExecutor) return;
|
|
4221
4315
|
if (!task || task.trace.outcome !== "pending" || task.changeMode !== "source-change") return;
|
|
4222
|
-
|
|
4316
|
+
const semanticRepairEnabled = taskAuthorityDecision(task, "CAP-13", "mutate").allowed;
|
|
4317
|
+
const semanticRepairExactlyOff = !taskAuthorityDecision(task, "CAP-13", "observe").allowed;
|
|
4318
|
+
const boundedRecoveryEnabled = autoRecoveryEnabled
|
|
4319
|
+
&& taskAuthorityDecision(task, "CAP-09", "block").allowed
|
|
4320
|
+
&& taskAuthorityDecision(task, "CAP-12", "mutate").allowed
|
|
4321
|
+
&& semanticRepairExactlyOff;
|
|
4322
|
+
if (!semanticRepairEnabled && !boundedRecoveryEnabled) return;
|
|
4223
4323
|
const phase = trajectoryRuntime.status(ctx.cwd, task.taskRunId);
|
|
4224
4324
|
if (!phase.enforcementSafe || !["verify", "review"].includes(String(phase.phase))) return;
|
|
4225
4325
|
|
|
@@ -4227,16 +4327,40 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4227
4327
|
if (workingTreeSnapshotHasUnavailableEvidence(currentSnapshot)) return;
|
|
4228
4328
|
const currentDigest = workingTreeEvidenceDigest(currentSnapshot);
|
|
4229
4329
|
const expectedReviewPaths = taskDeltaFilesFromSnapshot(task, currentSnapshot);
|
|
4230
|
-
const
|
|
4231
|
-
const
|
|
4232
|
-
const
|
|
4233
|
-
&& targets.every((file) => taskScopeIncludesPath(task.scope, file));
|
|
4330
|
+
const targets = targetInspection.targets;
|
|
4331
|
+
const boundedTargetMutation = targets.length > 0;
|
|
4332
|
+
const verifierCurrent = allVerifyCommandsPassCurrentTree(task, currentDigest);
|
|
4234
4333
|
|
|
4235
|
-
if (phase.phase === "verify" &&
|
|
4334
|
+
if (phase.phase === "verify" && boundedTargetMutation && semanticRepairEnabled && semanticRepairRuntime.prepare({
|
|
4236
4335
|
ctx, task, event, currentDigest, currentDeltaPaths: expectedReviewPaths, targetPaths: targets,
|
|
4237
|
-
verifierCurrent
|
|
4336
|
+
verifierCurrent
|
|
4238
4337
|
})) return;
|
|
4239
4338
|
|
|
4339
|
+
// A public verifier can pass before every explicit user criterion is
|
|
4340
|
+
// covered. CAP-12 therefore reserves one exact structured call when the
|
|
4341
|
+
// model catches its own omission in the same task/run/session. The call
|
|
4342
|
+
// still traverses every normal authorization check; verify -> repair is
|
|
4343
|
+
// committed only by the tool-result hook after a successful tree change.
|
|
4344
|
+
if (phase.phase === "verify" && boundedRecoveryEnabled && verifierCurrent && boundedTargetMutation) {
|
|
4345
|
+
const authoredFileDigests = runtimeState.successfulModelMutationDigests(
|
|
4346
|
+
{ taskId: task.taskId, taskRunId: task.taskRunId, sessionId: task.sessionId },
|
|
4347
|
+
currentSnapshot
|
|
4348
|
+
);
|
|
4349
|
+
if (semanticRepairRuntime.prepareBoundedRecovery({
|
|
4350
|
+
ctx,
|
|
4351
|
+
task,
|
|
4352
|
+
event,
|
|
4353
|
+
currentDigest,
|
|
4354
|
+
currentDeltaPaths: expectedReviewPaths,
|
|
4355
|
+
observedChangedPaths: task.observedChangedFiles,
|
|
4356
|
+
authoredFileDigests,
|
|
4357
|
+
targetPaths: targets,
|
|
4358
|
+
verifierCurrent
|
|
4359
|
+
})) return;
|
|
4360
|
+
}
|
|
4361
|
+
|
|
4362
|
+
if (!semanticRepairEnabled) return;
|
|
4363
|
+
|
|
4240
4364
|
let checkpoint = runtimeState.performanceReviewCheckpoint(task.taskRunId);
|
|
4241
4365
|
const checkpointReady = checkpoint?.workingTreeDigest === currentDigest
|
|
4242
4366
|
&& checkpoint.reviewSatisfied
|
|
@@ -4265,7 +4389,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4265
4389
|
return reviewDecision;
|
|
4266
4390
|
}
|
|
4267
4391
|
|
|
4268
|
-
if (targets.length === 0
|
|
4392
|
+
if (targets.length === 0) return;
|
|
4269
4393
|
const result = trajectoryRuntime.sync(ctx.cwd, ctx.sessionManager.getSessionId(), task, {
|
|
4270
4394
|
sourceHook: "tool-call",
|
|
4271
4395
|
recoveryRequested: true,
|
|
@@ -4286,12 +4410,12 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4286
4410
|
const currentDigest = workingTreeEvidenceDigest(workingTreeSnapshot(ctx.cwd) as Record<string, string>);
|
|
4287
4411
|
const phase = trajectoryRuntime.status(ctx.cwd, task.taskRunId);
|
|
4288
4412
|
const toolInput = isPlainRecord(event.input) ? event.input : {};
|
|
4289
|
-
const
|
|
4290
|
-
if (
|
|
4291
|
-
|
|
4292
|
-
)) {
|
|
4413
|
+
const targetInspection = authorizationMutationTargetInspection(ctx.cwd, event.toolName, toolInput);
|
|
4414
|
+
if (targetInspection.reason) return { block: true, reason: `Blocked ${event.toolName}: ${targetInspection.reason}.` };
|
|
4415
|
+
const targets = targetInspection.targets;
|
|
4416
|
+
if ((["edit", "write"].includes(event.toolName) || targetInspection.patchCarrier) && targets.length === 0) {
|
|
4293
4417
|
runtimeState.denyPerformanceReviewTool(task.taskRunId);
|
|
4294
|
-
return { block: true, reason: `Task ${task.taskId} semantic repair
|
|
4418
|
+
return { block: true, reason: `Task ${task.taskId} semantic repair requires an exact statically bounded target.` };
|
|
4295
4419
|
}
|
|
4296
4420
|
const reviewDecision = performanceReviewToolDecision({
|
|
4297
4421
|
toolName: event.toolName,
|
|
@@ -4319,6 +4443,30 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4319
4443
|
},
|
|
4320
4444
|
afterAuthorized: (event, ctx, metadata) => {
|
|
4321
4445
|
const task = activeSessionTask(ctx.cwd, ctx.sessionManager.getSessionId()) as TaskContract | undefined;
|
|
4446
|
+
const helperPreflight = evaluateDirectSubagentDispatch({
|
|
4447
|
+
toolName: event.toolName,
|
|
4448
|
+
toolInput: event.input,
|
|
4449
|
+
cwd: ctx.cwd,
|
|
4450
|
+
helpersMode: task ? taskHelperUsageMode(task) : helpersMode(),
|
|
4451
|
+
taskPending: task?.trace.outcome === "pending",
|
|
4452
|
+
dispatchAuthority: Boolean(task && taskAuthorityDecision(task, "CAP-14", "dispatch").allowed),
|
|
4453
|
+
durableSubagentState: task?.orchestration?.subagents
|
|
4454
|
+
});
|
|
4455
|
+
if (helperPreflight.applicable && helperPreflight.dispatch && helperPreflight.allowed) {
|
|
4456
|
+
const reserved = reserveDirectSubagentDispatch(task, metadata.toolCallId, {
|
|
4457
|
+
now: nowIso,
|
|
4458
|
+
persist: (candidate) => writeTask(ctx.cwd, candidate),
|
|
4459
|
+
trace: (payload) => appendTrace(ctx.cwd, payload)
|
|
4460
|
+
});
|
|
4461
|
+
telemetry(ctx, {
|
|
4462
|
+
event: "helper_dispatch_reserved",
|
|
4463
|
+
recordedAt: nowIso(),
|
|
4464
|
+
taskRunId: reserved?.taskRunId,
|
|
4465
|
+
toolCallId: metadata.toolCallId,
|
|
4466
|
+
role: helperPreflight.role,
|
|
4467
|
+
requestCount: helperPreflight.requestCount
|
|
4468
|
+
});
|
|
4469
|
+
}
|
|
4322
4470
|
if (task?.trace.outcome === "pending") {
|
|
4323
4471
|
const snapshot = workingTreeSnapshot(ctx.cwd) as Record<string, string>;
|
|
4324
4472
|
const currentDigest = workingTreeEvidenceDigest(snapshot);
|
|
@@ -4361,6 +4509,11 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4361
4509
|
authorize: async (event, ctx) => {
|
|
4362
4510
|
const preTask = activeSessionTask(ctx.cwd, ctx.sessionManager.getSessionId()) as TaskContract | undefined;
|
|
4363
4511
|
const preInput = isPlainRecord(event.input) ? event.input : {};
|
|
4512
|
+
const authorizationTargetInspection = authorizationMutationTargetInspection(ctx.cwd, event.toolName, preInput);
|
|
4513
|
+
if (authorizationTargetInspection.reason) {
|
|
4514
|
+
return { block: true, reason: `Blocked ${event.toolName}: ${authorizationTargetInspection.reason}.` };
|
|
4515
|
+
}
|
|
4516
|
+
const authorizationTargets = authorizationTargetInspection.targets;
|
|
4364
4517
|
const preSnapshot = preTask ? workingTreeSnapshot(ctx.cwd) as Record<string, string> : undefined;
|
|
4365
4518
|
const reservedFirstCall = Boolean(preTask && preSnapshot && !workingTreeSnapshotHasUnavailableEvidence(preSnapshot) && semanticRepairRuntime.reservedCallMatches({
|
|
4366
4519
|
cwd: ctx.cwd,
|
|
@@ -4369,7 +4522,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4369
4522
|
toolCallId: String(event.toolCallId),
|
|
4370
4523
|
toolName: event.toolName,
|
|
4371
4524
|
currentDigest: workingTreeEvidenceDigest(preSnapshot),
|
|
4372
|
-
targetPaths:
|
|
4525
|
+
targetPaths: authorizationTargets
|
|
4373
4526
|
}));
|
|
4374
4527
|
const phaseDecision = reservedFirstCall || (preTask?.workingTreeDigestMigration?.status === "verification-refresh-required" && SHELL_TOOL_NAMES.has(event.toolName)) ? undefined : phaseToolRuntime.toolDecision(ctx, event.toolName);
|
|
4375
4528
|
if (phaseDecision) return phaseDecision;
|
|
@@ -4427,10 +4580,11 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4427
4580
|
const taskContractRequired = governedTaskProfile
|
|
4428
4581
|
&& runtime.finalGate === "enforce"
|
|
4429
4582
|
&& finalGateConfig(policy).requireTaskContract;
|
|
4430
|
-
if (sessionTask?.trace.outcome === "pending" && sessionTask.changeMode === "read-only"
|
|
4583
|
+
if (sessionTask?.trace.outcome === "pending" && (sessionTask.changeMode === "read-only" || sessionTask.mutationPolicy === "forbidden")
|
|
4584
|
+
&& !SHELL_TOOL_NAMES.has(event.toolName) && isTaskMutationTool(event.toolName, toolInput)) {
|
|
4431
4585
|
return {
|
|
4432
4586
|
block: true,
|
|
4433
|
-
reason: `Task ${sessionTask.taskId} is read-only; ${event.toolName} cannot mutate project or external state in this task. Start a
|
|
4587
|
+
reason: `Task ${sessionTask.taskId} ${sessionTask.changeMode === "read-only" ? "is read-only" : "forbids source mutation"}; ${event.toolName} cannot mutate project or external state in this task. Start a mutation-required source task for implementation.`
|
|
4434
4588
|
};
|
|
4435
4589
|
}
|
|
4436
4590
|
const preparedInput = prepareToolInputForPolicy(event.toolName, toolInput, policy);
|
|
@@ -4460,6 +4614,12 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4460
4614
|
const execDecision = evaluateExecPolicy(command, profile, policy);
|
|
4461
4615
|
authorizedShellCommand = command;
|
|
4462
4616
|
authorizedShellSegments = execDecision.segments;
|
|
4617
|
+
if (shellTouchesGrantedSourceCheckout(ctx.cwd, command, runtimeState.sourceCheckoutReadRoots(ctx))) {
|
|
4618
|
+
return {
|
|
4619
|
+
block: true,
|
|
4620
|
+
reason: "Shared source checkouts are shell-inaccessible. Inspect the exact session-granted checkout with read, grep, find, or ls; cache mutation is never authorized."
|
|
4621
|
+
};
|
|
4622
|
+
}
|
|
4463
4623
|
shellProjectMutation = isProjectMutatingShellCommand(command, execDecision.segments);
|
|
4464
4624
|
configuredVerifierShell = Boolean(sessionTask && commandMatchesVerifyPlan(command, sessionTask.verifyCommands));
|
|
4465
4625
|
const shellWriteCandidates = extractShellWritePathCandidates(command);
|
|
@@ -4473,10 +4633,13 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4473
4633
|
&& !candidate.startsWith("../")
|
|
4474
4634
|
&& !candidate.startsWith(".pi/piagent-state/")
|
|
4475
4635
|
));
|
|
4476
|
-
|
|
4636
|
+
const mutationForbidden = sessionTask?.trace.outcome === "pending"
|
|
4637
|
+
&& (sessionTask.changeMode === "read-only" || sessionTask.mutationPolicy === "forbidden");
|
|
4638
|
+
const exactForbiddenTaskVerifier = mutationForbidden && sessionTask.changeMode === "source-change" && configuredVerifierShell;
|
|
4639
|
+
if (mutationForbidden && !exactForbiddenTaskVerifier && !isReadOnlyTaskShellCommand(command, execDecision.segments)) {
|
|
4477
4640
|
return {
|
|
4478
4641
|
block: true,
|
|
4479
|
-
reason: `Task ${sessionTask.taskId} is read-only; this shell command is not in the read-only inspection allowlist.`
|
|
4642
|
+
reason: `Task ${sessionTask.taskId} ${sessionTask.changeMode === "read-only" ? "is read-only; this shell command is not in the read-only inspection allowlist" : "forbids source mutation; this shell command is neither bounded inspection nor an exact configured verifier"}.`
|
|
4480
4643
|
};
|
|
4481
4644
|
}
|
|
4482
4645
|
if (execDecision.mode !== "off" && execDecision.decision === "forbid") {
|
|
@@ -4533,19 +4696,23 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4533
4696
|
// Shell inputs need command grammar, not field-by-field path guessing: an
|
|
4534
4697
|
// exclusion selector in `args` is not an accessed path. The semantic shell
|
|
4535
4698
|
// checks above already inspect the normalized command, including redirects.
|
|
4699
|
+
const policyPathInput = authorizationTargetInspection.patchCarrier
|
|
4700
|
+
? { paths: authorizationTargets }
|
|
4701
|
+
: preparedInput.input;
|
|
4536
4702
|
const pathDecision = SHELL_TOOL_NAMES.has(event.toolName) ? { block: false, reason: undefined } : evaluatePathLikeToolAccess(
|
|
4537
4703
|
ctx.cwd,
|
|
4538
4704
|
preparedInput.proxyToolName ?? event.toolName,
|
|
4539
|
-
|
|
4705
|
+
policyPathInput,
|
|
4540
4706
|
pathPolicy.writeProtectedPaths,
|
|
4541
4707
|
pathPolicy.readProtectedPaths,
|
|
4542
4708
|
pathPolicy.readOnlyPaths,
|
|
4543
4709
|
permissionProfile.mode === "trusted-full-access" ? undefined : capabilityState.filesystemRead,
|
|
4544
4710
|
permissionProfile.mode === "trusted-full-access" ? undefined : capabilityState.filesystemWrite,
|
|
4545
4711
|
{
|
|
4546
|
-
forceScopeAware: Boolean(preparedInput.proxyToolName),
|
|
4547
|
-
forceWrite: preparedInput.proxyAction?.decision === "confirm",
|
|
4548
|
-
allowAmbiguousFilesystemContentFields: !usesKnownExternalProvider && !isPiagentTool(event.toolName)
|
|
4712
|
+
forceScopeAware: authorizationTargetInspection.patchCarrier || Boolean(preparedInput.proxyToolName),
|
|
4713
|
+
forceWrite: authorizationTargetInspection.patchCarrier || preparedInput.proxyAction?.decision === "confirm",
|
|
4714
|
+
allowAmbiguousFilesystemContentFields: !usesKnownExternalProvider && !isPiagentTool(event.toolName),
|
|
4715
|
+
sourceCheckoutReadRoots: runtimeState.sourceCheckoutReadRoots(ctx)
|
|
4549
4716
|
}
|
|
4550
4717
|
);
|
|
4551
4718
|
if (pathDecision.block) {
|
|
@@ -4573,7 +4740,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4573
4740
|
|
|
4574
4741
|
const mutationTargets = SHELL_TOOL_NAMES.has(event.toolName)
|
|
4575
4742
|
? shellMutationTargets
|
|
4576
|
-
:
|
|
4743
|
+
: authorizationTargets;
|
|
4577
4744
|
const directProjectMutation = !SHELL_TOOL_NAMES.has(event.toolName) && (
|
|
4578
4745
|
WRITE_TOOL_NAMES.has(event.toolName)
|
|
4579
4746
|
|| mutationTargets.length > 0
|
|
@@ -4597,7 +4764,9 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4597
4764
|
opaqueCarrier: semanticOpaqueCarrier,
|
|
4598
4765
|
targetExtractionComplete: SHELL_TOOL_NAMES.has(event.toolName)
|
|
4599
4766
|
? !shellHasOpaqueWritePrimitive(authorizedShellCommand)
|
|
4600
|
-
:
|
|
4767
|
+
: authorizationTargetInspection.patchCarrier
|
|
4768
|
+
? authorizationTargetInspection.exactLocalPatchExecutor
|
|
4769
|
+
: !semanticOpaqueCarrier && preparedInput.proxyShellCarrier !== true,
|
|
4601
4770
|
recordedAt: nowIso()
|
|
4602
4771
|
})
|
|
4603
4772
|
: { handled: false, allowed: false, bypassPhase: false };
|
|
@@ -4629,37 +4798,25 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4629
4798
|
+ " Call `piagent_task_start` once with explicit project-relative scope, then retry."
|
|
4630
4799
|
};
|
|
4631
4800
|
}
|
|
4632
|
-
if (
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
}
|
|
4643
|
-
}
|
|
4644
|
-
const outsideScope = mutationTargets.filter((file) => !taskScopeIncludesPath(sessionTask.scope, file));
|
|
4645
|
-
if (outsideScope.length > 0) {
|
|
4646
|
-
return {
|
|
4647
|
-
block: true,
|
|
4648
|
-
reason: `Task ${sessionTask.taskId} cannot mutate paths outside its declared scope: ${outsideScope.join(", ")}.`
|
|
4649
|
-
};
|
|
4650
|
-
}
|
|
4651
|
-
const outsideEvidenceRoot = mutationTargets.filter((file) => !pathWithinChangeEvidenceRoot(ctx.cwd, file));
|
|
4652
|
-
if (outsideEvidenceRoot.length > 0) {
|
|
4653
|
-
return {
|
|
4654
|
-
block: true,
|
|
4655
|
-
reason: `Task ${sessionTask.taskId} cannot mutate paths outside a change evidence root: ${outsideEvidenceRoot.join(", ")}. In a parent workspace with separate repos, write under a child repo or a scoped workspace directory such as plans/**.`
|
|
4656
|
-
};
|
|
4657
|
-
}
|
|
4801
|
+
if (
|
|
4802
|
+
sessionTask?.trace.outcome === "pending"
|
|
4803
|
+
&& sessionTask.changeMode === "source-change"
|
|
4804
|
+
&& shellProjectMutation
|
|
4805
|
+
&& opaqueShellMutationNeedsBoundedTarget(authorizedShellCommand, authorizedShellSegments)
|
|
4806
|
+
&& !shellMutationTargetBounded
|
|
4807
|
+
&& !configuredVerifierShell
|
|
4808
|
+
) {
|
|
4809
|
+
return {
|
|
4810
|
+
block: true,
|
|
4811
|
+
reason: `Task ${sessionTask.taskId} cannot run an opaque shell mutation whose write target is not statically bounded. Use edit/write/apply_patch, an explicit project-local redirection, or the exact configured verifier.`
|
|
4812
|
+
};
|
|
4658
4813
|
}
|
|
4659
|
-
if (runtime.contextBudget !== "off" && ["write", "edit"].includes(event.toolName)) {
|
|
4660
|
-
const
|
|
4661
|
-
|
|
4662
|
-
|
|
4814
|
+
if (runtime.contextBudget !== "off" && (["write", "edit"].includes(event.toolName) || authorizationTargetInspection.patchCarrier)) {
|
|
4815
|
+
const budgetTargets = authorizationTargetInspection.patchCarrier
|
|
4816
|
+
? authorizationTargets
|
|
4817
|
+
: [extractLikelyPathFromInput(ctx.cwd, preInput)].filter((item): item is string => Boolean(item));
|
|
4818
|
+
const budget = contextBudgetConfig(policy);
|
|
4819
|
+
for (const relativePath of budgetTargets) {
|
|
4663
4820
|
const stats = candidateFileBudget(ctx.cwd, relativePath, budget);
|
|
4664
4821
|
if (runtime.contextBudget === "enforce" && stats.exists && stats.overLimit) {
|
|
4665
4822
|
return { block: true, reason: `Context budget blocked editing large file ${relativePath}: ${stats.chars} chars > ${budget.maxContextFileChars}` };
|
|
@@ -4688,13 +4845,16 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4688
4845
|
}
|
|
4689
4846
|
}
|
|
4690
4847
|
});
|
|
4848
|
+
// Register only after authorization is live. The same-name override is intentional:
|
|
4849
|
+
// guard target parsing, provenance, and atomicity require this exact tool contract.
|
|
4850
|
+
registerApplyPatchTool(pi, Type);
|
|
4691
4851
|
const registrationDeps = {
|
|
4692
4852
|
CONTEXT_INDEX_EDGE_KINDS, CONTEXT_INDEX_NODE_KINDS, DEFAULT_MAX_TASK_ATTEMPTS, FRESH_COMMAND_ACTIONS, FRESH_COMMAND_HELP,
|
|
4693
4853
|
ONBOARDING_COMMAND_ACTIONS, ORCHESTRATION_ROLES, PIAGENT_TOOL_NAMES, READ_ONLY_TOOL_NAMES, REVIEW_LENSES,
|
|
4694
4854
|
StringEnum, TECH_STACK_MANIFEST_FILE, TOOL_RESULT_CAPTURE_MAX_CHARS, TOOL_RESULT_COMPACT_CHAR_THRESHOLD, TOOL_RESULT_COMPACT_LINE_THRESHOLD,
|
|
4695
4855
|
TOOL_RESULT_PREVIEW_MAX_CHARS, Type, WORKFLOW_COMMAND_EXCLUSIONS, activateToolGroups, activeSessionTask,
|
|
4696
4856
|
activeTaskToolGroups, allVerifyCommandsPassCurrentTree, appendMemoryNote, appendSessionTrace, appendTrace,
|
|
4697
|
-
acceptanceBaselineGuidance, acceptanceProofGuidance, applyAcceptanceRecoveryProvenance, applyRuntimeLifecycleObservation, automaticAcceptanceCriteria, automaticReadOnlyTaskScope, automaticReviewLenses, automaticTaskIntakeMode, automaticTaskRiskLane, automaticTaskScope,
|
|
4857
|
+
acceptanceBaselineGuidance, acceptanceLanguageAdapterForPath, acceptanceProofGuidance, applyAcceptanceRecoveryProvenance, applyRuntimeLifecycleObservation, automaticAcceptanceCriteria, automaticReadOnlyTaskScope, automaticReviewLenses, automaticTaskIntakeMode, automaticTaskMutationPolicy, automaticTaskRiskLane, automaticTaskScope,
|
|
4698
4858
|
bashResults, bindSessionTask, buildAcceptanceReceipt, buildContextEfficiencyReport, buildContextIndexStatus, buildLiveTaskStatus, buildTaskEfficiencyMetrics,
|
|
4699
4859
|
buildContextIndexV2, buildContextPack, buildContextPreflight, buildProfileOptions, buildProfileTechOptions,
|
|
4700
4860
|
buildTestImpact, buildUsageSnapshot, candidateFileBudget, checkoutReferenceRepo, classifyContextTask,
|
|
@@ -4706,7 +4866,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4706
4866
|
extensionDir, externalActionPolicyConfig, extractDocument, finalGateConfig, findMatchingObservedBashResult,
|
|
4707
4867
|
formatContextPreflight, formatCount, formatLiveTaskStatus, formatPercent, formatTechOptionsText, formatTechSelectionSummary,
|
|
4708
4868
|
formatToolResultCaptureStatus, formatUsageSnapshot, fs, hasGitEvidenceRoot, hasOperatorSessionName, helpersMode,
|
|
4709
|
-
loadProfileFromContext, matchesAnyPath, matchesProtectedPath, mcpActions, mcpApprovalCache,
|
|
4869
|
+
isAcceptanceTestPath, loadProfileFromContext, matchesAnyPath, matchesProtectedPath, mcpActions, mcpApprovalCache,
|
|
4710
4870
|
memoryHandbookPath, memoryLocalDir, memorySummaryPath, normalizeProjectProfileName, normalizeRelative,
|
|
4711
4871
|
normalizeReviewLenses, normalizeTechSelections, normalizeWorkPlanSteps, nowIso, observedBashLedgerPath,
|
|
4712
4872
|
path, permissionOverrideFromContext, permissionProfilesConfig, policy, prefixCompletions,
|
|
@@ -4724,7 +4884,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4724
4884
|
techContextDirPath, techContextFilePath, techContextRelativePath, techOptionById, techStackPath,
|
|
4725
4885
|
telemetry, toolRegistryConfig, trajectoryRuntime, uniqueStrings, usageExactCommands,
|
|
4726
4886
|
validTaskScopePattern, validateNewWorkPlan, verifierCommandInstructions, verifyProjectCapabilityState, workingTreeEvidenceDigest,
|
|
4727
|
-
repositoryFileManifest, resolveTaskScopePatterns,
|
|
4887
|
+
repositoryFileManifest, repositoryFileManifestDetails, resolveTaskScopePatterns,
|
|
4728
4888
|
workingTreeSnapshot, workingTreeSnapshotHasUnavailableEvidence, writeContextIndex, writeProfileDocumentWithLock, writeProfileFromAdapter, writeProjectOnboarding,
|
|
4729
4889
|
writeTask, writeTechStackSelection
|
|
4730
4890
|
};
|