@piagent/platform 1.5.4 → 1.6.0
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 +136 -0
- package/README.md +9 -8
- package/README.vi.md +9 -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 +68 -0
- package/benchmarks/production-v1/suite.json +42 -3
- package/catalog/capabilities.json +6 -6
- package/docs/command-reference-vietnamese.md +6 -6
- package/docs/context-engine.md +7 -1
- package/docs/distribution-standard.md +7 -7
- package/docs/en/architecture.md +1 -0
- package/docs/languages.json +4 -4
- package/docs/model-options.md +8 -5
- package/docs/operator-manual-vietnamese.md +13 -11
- package/docs/project-adapters.md +2 -2
- package/docs/publishing-for-teams.md +9 -0
- package/docs/quality-benchmark.md +271 -62
- package/docs/release-install-policy.md +10 -9
- 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/task-lifecycle-vietnamese.md +2 -2
- package/docs/team-onboarding.md +2 -2
- package/docs/usage-observability.md +7 -2
- package/docs/vi/architecture.md +1 -0
- package/docs-site/architecture.html +1 -1
- package/docs-site/benchmark.html +1 -1
- package/docs-site/commands.html +1 -1
- package/docs-site/context-engine.html +1 -1
- package/docs-site/en/architecture.html +1 -1
- package/docs-site/en/benchmark.html +1 -1
- package/docs-site/en/commands.html +1 -1
- package/docs-site/en/context-engine.html +1 -1
- package/docs-site/en/index.html +1 -1
- package/docs-site/en/mcp-approval.html +1 -1
- package/docs-site/en/mcp-auth.html +1 -1
- package/docs-site/en/mcp-servers.html +1 -1
- package/docs-site/en/mcp.html +1 -1
- package/docs-site/en/permissions.html +1 -1
- package/docs-site/en/profiles.html +1 -1
- package/docs-site/en/quickstart.html +1 -1
- package/docs-site/en/runtime.html +1 -1
- 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 +1 -1
- package/docs-site/en/workflows.html +1 -1
- package/docs-site/index.html +1 -1
- package/docs-site/mcp-approval.html +1 -1
- package/docs-site/mcp-auth.html +1 -1
- package/docs-site/mcp-servers.html +1 -1
- package/docs-site/mcp.html +1 -1
- package/docs-site/permissions.html +1 -1
- package/docs-site/profiles.html +1 -1
- package/docs-site/quickstart.html +1 -1
- package/docs-site/runtime.html +1 -1
- 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 +1 -1
- package/docs-site/workflows.html +1 -1
- package/evals/fixtures/benchmark-assurance-evidence.invalid.json +1 -1
- package/evals/fixtures/benchmark-assurance-evidence.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/fs4-readiness-matrix.v1.json +2 -2
- package/evals/fs5-causal-arm.v1.json +2 -2
- package/evals/fs5-pilot-protocol.v1.json +2 -2
- package/evals/fs5-pilot-protocol.v4.json +6 -5
- package/evals/fs5-pilot-protocol.v5.json +7 -6
- package/evals/private-holdout-v1/public-exposure.v1.json +1 -1
- package/package.json +3 -1
- 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.js +3 -1
- package/packages/piagent-core/benchmark/benchmark-comparison.js +271 -2
- package/packages/piagent-core/benchmark/benchmark-core.js +732 -661
- package/packages/piagent-core/benchmark/benchmark-execution-guard.js +7 -5
- package/packages/piagent-core/benchmark/benchmark-forensics.js +439 -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 +3 -1
- package/packages/piagent-core/benchmark/benchmark-process.js +6 -5
- package/packages/piagent-core/benchmark/benchmark-provider-wire.js +126 -0
- package/packages/piagent-core/benchmark/benchmark-record-validation.js +183 -0
- package/packages/piagent-core/benchmark/benchmark-report.js +116 -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-stage-diagnostic.js +782 -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 +128 -2
- package/packages/piagent-core/benchmark/benchmark-summary-support.js +376 -0
- package/packages/piagent-core/benchmark/benchmark-timing-diagnostics.js +971 -0
- package/packages/piagent-core/benchmark/benchmark-usage.js +311 -0
- package/packages/piagent-core/benchmark/benchmark-workflow.js +166 -0
- package/packages/piagent-core/docs/context-efficiency-improvement-plan.md +1080 -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 +34 -11
- 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 +148 -145
- 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-telemetry-integrity.js +65 -0
- package/packages/piagent-core/extensions/criterion-context-pack.js +422 -7
- package/packages/piagent-core/extensions/criterion-graph.js +78 -8
- package/packages/piagent-core/extensions/guard-types.ts +3 -0
- package/packages/piagent-core/extensions/javascript-regex-evidence.js +32 -0
- package/packages/piagent-core/extensions/piagent-guard.ts +250 -43
- package/packages/piagent-core/extensions/policy-core.js +1 -1
- package/packages/piagent-core/extensions/task-contract-view.js +17 -3
- package/packages/piagent-core/extensions/task-lifecycle.js +7 -2
- package/packages/piagent-core/extensions/task-state.js +40 -134
- package/packages/piagent-core/extensions/verification-intelligence.js +3 -1
- package/packages/piagent-core/extensions/workspace-evidence-roots.js +157 -0
- package/packages/piagent-core/package.json +1 -1
- package/packages/piagent-core/policies/base-policy.json +3 -0
- 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 +5 -6
- package/packages/piagent-core/runtime/hooks/input-hook.ts +11 -1
- package/packages/piagent-core/runtime/hooks/session-hooks.ts +22 -2
- package/packages/piagent-core/runtime/hooks/session-start-hook.ts +7 -2
- package/packages/piagent-core/runtime/hooks/tool-result-content-guards.ts +95 -0
- package/packages/piagent-core/runtime/hooks/tool-result-hook.ts +84 -102
- package/packages/piagent-core/runtime/inspection/activity-event-adapter.ts +9 -3
- package/packages/piagent-core/runtime/inspection/activity-recovery.ts +104 -0
- package/packages/piagent-core/runtime/inspection/approval-broker.ts +29 -5
- 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/tool-failure-classification.ts +53 -0
- package/packages/piagent-core/runtime/inspection/webui-snapshot.ts +34 -6
- 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/role-policy.ts +1 -1
- package/packages/piagent-core/runtime/product/activity-inspector.ts +7 -2
- package/packages/piagent-core/runtime/product/efficiency-metrics.ts +4 -2
- package/packages/piagent-core/runtime/quality/exact-output-contract.ts +2 -1
- package/packages/piagent-core/runtime/quality/performance-assurance-signals.ts +23 -0
- package/packages/piagent-core/runtime/quality/performance-assurance.ts +6 -2
- 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/semantic-repair-handshake.ts +55 -2
- package/packages/piagent-core/runtime/recovery/semantic-repair-runtime.ts +55 -2
- package/packages/piagent-core/runtime/registration/context-commands.ts +66 -5
- package/packages/piagent-core/runtime/registration/policy-tools.ts +46 -11
- package/packages/piagent-core/runtime/registration/task-completion-tools.ts +49 -16
- package/packages/piagent-core/runtime/registration/task-start-guidance.ts +163 -0
- package/packages/piagent-core/runtime/registration/task-start-retry-helpers.ts +37 -0
- package/packages/piagent-core/runtime/registration/task-start-tool.ts +67 -62
- package/packages/piagent-core/runtime/session/runtime-state.ts +154 -2
- package/packages/piagent-core/runtime/session/system-prompt.ts +4 -4
- package/packages/piagent-core/runtime/tools/apply-patch-tool.ts +500 -0
- package/packages/piagent-core/runtime/trajectory/trajectory-runtime.ts +2 -1
- package/packages/piagent-core/runtime/workflows/task-intake.ts +43 -2
- 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-worker.md +1 -1
- package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-rD9WJKxs.js → MarkdownMessage-CxeZqTte.js} +1 -1
- package/packages/piagent-webui/dist/client/assets/index-DynIMe8I.js +176 -0
- package/packages/piagent-webui/dist/client/index.html +1 -1
- 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 +93 -7
- 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-start.ts +5 -1
- package/packages/piagent-webui/gateway/session-operation-watchdog.ts +158 -0
- package/packages/piagent-webui/gateway/session-runtime-factory.ts +45 -0
- package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +139 -120
- package/packages/piagent-webui/server/loopback-server.ts +8 -0
- package/packages/piagent-webui/server/transcript-projection.ts +22 -4
- package/packages/piagent-webui/shared/text-visibility.ts +10 -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/session-live-state-v1.schema.json +76 -0
- package/schemas/task-contract.schema.json +12 -1
- package/scripts/benchmark-runner-core.mjs +355 -359
- package/scripts/benchmark-runner-finalization.mjs +309 -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/helper-evaluation.mjs +1 -1
- package/scripts/install-global.sh +2 -2
- package/scripts/pi-usage-history.mjs +37 -10
- package/scripts/piagent-dashboard.mjs +13 -1
- package/scripts/setup.sh +2 -2
- package/scripts/verify-local.sh +5 -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
|
-
pathWithinChangeEvidenceRoot, priorTaskAttempts, repositoryFileManifest, resolveTaskContract, safeTaskId, summarizeAttempt, taskContractValidationErrors, taskDigestMigrationArchiveStatus,
|
|
70
|
+
pathWithinChangeEvidenceRoot, 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,13 +84,15 @@ 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";
|
|
93
|
-
import {
|
|
94
|
+
import { hasDurableContextEvidence } from "./context-evidence.js";
|
|
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";
|
|
96
98
|
import {
|
|
@@ -121,6 +123,7 @@ import {
|
|
|
121
123
|
automaticReviewLenses,
|
|
122
124
|
automaticReadOnlyTaskScope,
|
|
123
125
|
automaticTaskIntakeMode,
|
|
126
|
+
automaticTaskMutationPolicy,
|
|
124
127
|
automaticTaskRiskLane,
|
|
125
128
|
automaticTaskScope,
|
|
126
129
|
resolveTaskScopePatterns,
|
|
@@ -137,6 +140,8 @@ import { registerToolResultHook } from "../runtime/hooks/tool-result-hook.ts";
|
|
|
137
140
|
import { readRuntimeVersionMetadata, RuntimeSnapshotCapture } from "../runtime/model/runtime-snapshot.ts";
|
|
138
141
|
import { recordRuntimeSnapshotTelemetry } from "../runtime/model/snapshot-telemetry.ts";
|
|
139
142
|
import { captureAuthenticatedModelCatalogFromContext } from "../runtime/model/authenticated-catalog.ts";
|
|
143
|
+
import { normalizeOpenAiCodexReasoningPayload } from "../runtime/model/openai-codex-reasoning.ts";
|
|
144
|
+
import { buildOpenAiCodexWireFingerprint } from "../runtime/model/provider-wire-fingerprint.ts";
|
|
140
145
|
import { ModelRouteRuntime, readModelRouteEvents } from "../runtime/model/model-route-runtime.ts";
|
|
141
146
|
import { parentRoutingModeFromEnvironment, routingObjectiveFromEnvironment } from "../runtime/model/model-route-policy.ts";
|
|
142
147
|
import { ModelSelectionProvenanceTracker } from "../runtime/model/model-selection-provenance.ts";
|
|
@@ -147,6 +152,7 @@ import { SolverShadowRuntime, solverModeFromEnvironment } from "../runtime/solve
|
|
|
147
152
|
import { observeTrajectorySync } from "../runtime/trajectory/trajectory-observability.ts";
|
|
148
153
|
import { TrajectoryRuntime } from "../runtime/trajectory/trajectory-runtime.ts";
|
|
149
154
|
import { PhaseToolRuntime, phaseToolModeFromEnvironment } from "../runtime/tools/phase-tool-runtime.ts";
|
|
155
|
+
import { parseApplyPatchTargets, registerApplyPatchTool } from "../runtime/tools/apply-patch-tool.ts";
|
|
150
156
|
import { authorityReplacementState, ensureTaskAuthorityResumePolicy } from "../runtime/policy/authority-resume-policy.ts";
|
|
151
157
|
import { taskAuthorityDecision } from "../runtime/policy/task-authority-runtime.ts";
|
|
152
158
|
import { selectRecoveryDecision } from "../runtime/recovery/recovery-policy.ts";
|
|
@@ -226,6 +232,7 @@ type TaskStartParameters = {
|
|
|
226
232
|
riskLane: "tiny" | "normal" | "high-risk";
|
|
227
233
|
intakeMode?: "model" | "runtime";
|
|
228
234
|
changeMode?: "source-change" | "read-only";
|
|
235
|
+
mutationPolicy?: "required" | "forbidden";
|
|
229
236
|
verifyGroup?: string;
|
|
230
237
|
maxAttempts?: number;
|
|
231
238
|
expectedOutput: string;
|
|
@@ -424,6 +431,7 @@ const DEFAULT_POLICY: BasePolicy = {
|
|
|
424
431
|
},
|
|
425
432
|
contextBudget: {
|
|
426
433
|
defaultMode: "enforce",
|
|
434
|
+
contextDeltaShadow: "sample",
|
|
427
435
|
maxContextFileChars: 50000,
|
|
428
436
|
maxMemoryFileChars: 20000,
|
|
429
437
|
maxManifestFiles: 80,
|
|
@@ -472,6 +480,7 @@ const DEFAULT_POLICY: BasePolicy = {
|
|
|
472
480
|
ls: ["filesystem-readonly"],
|
|
473
481
|
write: ["filesystem-write"],
|
|
474
482
|
edit: ["filesystem-write"],
|
|
483
|
+
apply_patch: ["filesystem-write"],
|
|
475
484
|
browser: ["browser"],
|
|
476
485
|
github: ["github"]
|
|
477
486
|
}
|
|
@@ -1090,6 +1099,20 @@ function isPlainRecord(value: unknown): value is Record<string, unknown> {
|
|
|
1090
1099
|
return prototype === Object.prototype || prototype === null;
|
|
1091
1100
|
}
|
|
1092
1101
|
|
|
1102
|
+
function approvalUnavailableFallback(ctx: Pick<ExtensionContext, "ui">): ApprovalUnavailableFallback {
|
|
1103
|
+
// The symbol is a cross-package runtime marker, not an import, so piagent-core
|
|
1104
|
+
// remains independent of the WebUI package. A Gateway has no interactive
|
|
1105
|
+
// terminal behind `ctx.ui.confirm`; if brokering is unavailable it must fail
|
|
1106
|
+
// closed immediately instead of awaiting an impossible terminal response.
|
|
1107
|
+
try {
|
|
1108
|
+
return Reflect.get(ctx.ui as object, Symbol.for("piagent.webui.gateway-runtime-ui.v1")) === true
|
|
1109
|
+
? "deny"
|
|
1110
|
+
: "terminal-confirm";
|
|
1111
|
+
} catch {
|
|
1112
|
+
return "terminal-confirm";
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1093
1116
|
function evaluatePathLikeToolAccess(
|
|
1094
1117
|
cwd: string,
|
|
1095
1118
|
toolName: string,
|
|
@@ -1381,6 +1404,7 @@ function evaluatePermissionProfileToolAccess(
|
|
|
1381
1404
|
function contextBudgetConfig(policy: BasePolicy): Required<ContextBudgetConfig> {
|
|
1382
1405
|
return {
|
|
1383
1406
|
defaultMode: policy.contextBudget?.defaultMode ?? DEFAULT_POLICY.contextBudget?.defaultMode ?? "enforce",
|
|
1407
|
+
contextDeltaShadow: policy.contextBudget?.contextDeltaShadow ?? DEFAULT_POLICY.contextBudget?.contextDeltaShadow ?? "off",
|
|
1384
1408
|
maxContextFileChars: policy.contextBudget?.maxContextFileChars ?? DEFAULT_POLICY.contextBudget?.maxContextFileChars ?? 50000,
|
|
1385
1409
|
maxMemoryFileChars: policy.contextBudget?.maxMemoryFileChars ?? DEFAULT_POLICY.contextBudget?.maxMemoryFileChars ?? 20000,
|
|
1386
1410
|
maxManifestFiles: policy.contextBudget?.maxManifestFiles ?? DEFAULT_POLICY.contextBudget?.maxManifestFiles ?? 80,
|
|
@@ -1530,9 +1554,10 @@ function normalizeWorkPlanSteps(values: unknown): WorkPlanStep[] {
|
|
|
1530
1554
|
function defaultWorkPlan(
|
|
1531
1555
|
summary: string,
|
|
1532
1556
|
riskLane: TaskContract["riskLane"],
|
|
1533
|
-
changeMode: TaskContract["changeMode"]
|
|
1557
|
+
changeMode: TaskContract["changeMode"],
|
|
1558
|
+
mutationPolicy: NonNullable<TaskContract["mutationPolicy"]> = changeMode === "read-only" ? "forbidden" : "required"
|
|
1534
1559
|
): WorkPlanStep[] {
|
|
1535
|
-
if (changeMode === "read-only") {
|
|
1560
|
+
if (changeMode === "read-only" || mutationPolicy === "forbidden") {
|
|
1536
1561
|
if (riskLane === "tiny") {
|
|
1537
1562
|
return [{
|
|
1538
1563
|
id: "scout",
|
|
@@ -1737,6 +1762,69 @@ function collectPatchTargetPaths(value: unknown, key = "", depth = 0): string[]
|
|
|
1737
1762
|
return paths;
|
|
1738
1763
|
}
|
|
1739
1764
|
|
|
1765
|
+
type ApplyPatchTargetInspection =
|
|
1766
|
+
| { carrier: false; exactLocalExecutor: false; targets: [] }
|
|
1767
|
+
| { carrier: true; exactLocalExecutor: boolean; targets: string[]; reason?: undefined }
|
|
1768
|
+
| { carrier: true; exactLocalExecutor: boolean; targets: []; reason: string };
|
|
1769
|
+
|
|
1770
|
+
function isApplyPatchToolName(toolName: string): boolean {
|
|
1771
|
+
const tokens = new Set(actionTokens(toolName));
|
|
1772
|
+
// Pi direct MCP tools are named <server-prefix>_<tool>, for example
|
|
1773
|
+
// filesystem_apply_patch; they never carry an mcp__ prefix. Treat every
|
|
1774
|
+
// apply+patch action carrier as this exact fail-closed protocol so a custom
|
|
1775
|
+
// prefix cannot bypass target extraction.
|
|
1776
|
+
return tokens.has("apply") && tokens.has("patch");
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
function inspectApplyPatchAuthorizationTargets(
|
|
1780
|
+
toolName: string,
|
|
1781
|
+
input: Record<string, unknown>
|
|
1782
|
+
): ApplyPatchTargetInspection {
|
|
1783
|
+
let carrier = toolName;
|
|
1784
|
+
let payload = input;
|
|
1785
|
+
const exactLocalExecutor = toolName === "apply_patch";
|
|
1786
|
+
if (normalizeActionToken(toolName) === "mcp") {
|
|
1787
|
+
const proxyTool = typeof input.tool === "string" ? input.tool.trim() : "";
|
|
1788
|
+
if (!isApplyPatchToolName(proxyTool)) return { carrier: false, exactLocalExecutor: false, targets: [] };
|
|
1789
|
+
carrier = proxyTool;
|
|
1790
|
+
if (typeof input.args !== "string" || input.args.length > MAX_MCP_PROXY_ARGS_CHARS) {
|
|
1791
|
+
return { carrier: true, exactLocalExecutor: false, targets: [], reason: "MCP apply_patch args must be a bounded JSON object string" };
|
|
1792
|
+
}
|
|
1793
|
+
try {
|
|
1794
|
+
const parsed = JSON.parse(input.args);
|
|
1795
|
+
if (!isPlainRecord(parsed)) return { carrier: true, exactLocalExecutor: false, targets: [], reason: "MCP apply_patch args must decode to a JSON object" };
|
|
1796
|
+
payload = parsed;
|
|
1797
|
+
} catch {
|
|
1798
|
+
return { carrier: true, exactLocalExecutor: false, targets: [], reason: "MCP apply_patch args must be valid JSON" };
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
if (!isApplyPatchToolName(carrier)) return { carrier: false, exactLocalExecutor: false, targets: [] };
|
|
1802
|
+
const keys = Object.keys(payload);
|
|
1803
|
+
if (keys.length !== 1 || keys[0] !== "patch" || typeof payload.patch !== "string") {
|
|
1804
|
+
return { carrier: true, exactLocalExecutor, targets: [], reason: "apply_patch input must contain only one string field named patch" };
|
|
1805
|
+
}
|
|
1806
|
+
try {
|
|
1807
|
+
return { carrier: true, exactLocalExecutor, targets: parseApplyPatchTargets(payload.patch) };
|
|
1808
|
+
} catch (error) {
|
|
1809
|
+
return {
|
|
1810
|
+
carrier: true,
|
|
1811
|
+
exactLocalExecutor,
|
|
1812
|
+
targets: [],
|
|
1813
|
+
reason: error instanceof Error ? error.message : "apply_patch target extraction failed"
|
|
1814
|
+
};
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
function authorizationMutationTargetInspection(
|
|
1819
|
+
cwd: string,
|
|
1820
|
+
toolName: string,
|
|
1821
|
+
input: Record<string, unknown>
|
|
1822
|
+
): { targets: string[]; patchCarrier: boolean; exactLocalPatchExecutor: boolean; reason?: string } {
|
|
1823
|
+
const patch = inspectApplyPatchAuthorizationTargets(toolName, input);
|
|
1824
|
+
if (patch.carrier) return { targets: patch.targets, patchCarrier: true, exactLocalPatchExecutor: patch.exactLocalExecutor, reason: patch.reason };
|
|
1825
|
+
return { targets: taskMutationTargets(cwd, toolName, input), patchCarrier: false, exactLocalPatchExecutor: false };
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1740
1828
|
function taskMutationIdentity(toolName: string, input: Record<string, unknown>): string {
|
|
1741
1829
|
if (normalizeActionToken(toolName) !== "mcp") return toolName;
|
|
1742
1830
|
const proxyTool = typeof input.tool === "string" ? input.tool.trim() : "";
|
|
@@ -2088,7 +2176,7 @@ function flushObservedTaskContext(
|
|
|
2088
2176
|
const task = activeSessionTask(ctx.cwd, ctx.sessionManager.getSessionId()) as TaskContract | undefined;
|
|
2089
2177
|
if (!task || task.trace.outcome !== "pending") return task;
|
|
2090
2178
|
const added = mergeObservedTaskContext(task, pendingContext, maxManifestFiles, redactText);
|
|
2091
|
-
const lifecycle = task.changeMode === "read-only"
|
|
2179
|
+
const lifecycle = (task.changeMode === "read-only" || task.mutationPolicy === "forbidden") && hasDurableContextEvidence(task)
|
|
2092
2180
|
? applyRuntimeLifecycleObservation(task, "context-complete", nowIso())
|
|
2093
2181
|
: { changed: false, mode: runtimeLifecycleMode(task) };
|
|
2094
2182
|
if (added.length === 0 && !lifecycle.changed) return task;
|
|
@@ -2159,9 +2247,13 @@ function prepareToolInputForPolicy(
|
|
|
2159
2247
|
return { input, reason: "MCP proxy args must be valid JSON" };
|
|
2160
2248
|
}
|
|
2161
2249
|
if (!isPlainRecord(parsed)) return { input, reason: "MCP proxy args must decode to a JSON object" };
|
|
2162
|
-
const
|
|
2163
|
-
|
|
2164
|
-
|
|
2250
|
+
const exactPatch = inspectApplyPatchAuthorizationTargets(toolName, input);
|
|
2251
|
+
if (exactPatch.carrier && exactPatch.reason) return { input, reason: exactPatch.reason };
|
|
2252
|
+
const patchTargets = exactPatch.carrier
|
|
2253
|
+
? exactPatch.targets
|
|
2254
|
+
: actionTokens(proxyTool).includes("patch")
|
|
2255
|
+
? collectPatchTargetPaths(parsed)
|
|
2256
|
+
: [];
|
|
2165
2257
|
policyInput = patchTargets.length > 0
|
|
2166
2258
|
? { ...input, proxyArgs: parsed, proxyPatchTargets: { paths: patchTargets } }
|
|
2167
2259
|
: { ...input, proxyArgs: parsed };
|
|
@@ -3558,7 +3650,7 @@ function taskScopeIncludesPath(scope: string[], file: string): boolean {
|
|
|
3558
3650
|
}
|
|
3559
3651
|
|
|
3560
3652
|
function verifierCommandInstructions(commands: string[]): string[] {
|
|
3561
|
-
return commands.map((command, index) => `Verifier ${index + 1} (run as
|
|
3653
|
+
return commands.map((command, index) => `Verifier ${index + 1} (run as an exact standalone shell command): ${command}`);
|
|
3562
3654
|
}
|
|
3563
3655
|
|
|
3564
3656
|
function evaluateTaskGate(
|
|
@@ -3591,7 +3683,7 @@ function evaluateTaskGate(
|
|
|
3591
3683
|
if (task.attempt > task.maxAttempts) missing.push(`attempt within maxAttempts (${task.attempt}/${task.maxAttempts})`);
|
|
3592
3684
|
const plannedVerifyCommands = meaningfulVerificationCommands(task.verifyCommands);
|
|
3593
3685
|
if (task.changeMode === "source-change" && plannedVerifyCommands.length === 0) missing.push("meaningful verify command");
|
|
3594
|
-
if (finalGate.requireContextManifest && task
|
|
3686
|
+
if (finalGate.requireContextManifest && !hasDurableContextEvidence(task)) missing.push("context manifest");
|
|
3595
3687
|
if (task.changeMode === "source-change" && finalGate.requireVerifyEvidence && task.verifyEvidence.length === 0) missing.push("verify evidence");
|
|
3596
3688
|
if (task.verifyEvidence.some((evidence) => evidence.observed !== true)) {
|
|
3597
3689
|
warnings.push("Unobserved verify evidence is ignored by the passing verify gate.");
|
|
@@ -3618,14 +3710,14 @@ function evaluateTaskGate(
|
|
|
3618
3710
|
missing.push(`completed work plan (${incompleteSteps.map((step) => `${step.id}:${step.status}`).join(", ")})`);
|
|
3619
3711
|
}
|
|
3620
3712
|
const changedFileEvidence = taskChangedFileEvidence(cwd, task, currentDigests);
|
|
3621
|
-
if (task.changeMode === "source-change" && task.trace.outcome === "completed") {
|
|
3713
|
+
if (task.changeMode === "source-change" && task.trace.outcome === "completed" && task.mutationPolicy !== "forbidden") {
|
|
3622
3714
|
if (task.changedFiles.length === 0) missing.push("changed files");
|
|
3623
3715
|
if (changedFileEvidence.undeclared.length > 0) missing.push(`declared observed changes (${changedFileEvidence.undeclared.join(", ")})`);
|
|
3624
3716
|
if (changedFileEvidence.unsupportedClaims.length > 0) missing.push(`supported changed-file claims (${changedFileEvidence.unsupportedClaims.join(", ")})`);
|
|
3625
3717
|
if (changedFileEvidence.outsideScope.length > 0) missing.push(`changes within task scope (${changedFileEvidence.outsideScope.join(", ")})`);
|
|
3626
3718
|
}
|
|
3627
|
-
if (task.changeMode === "read-only" && changedFileEvidence.expected.length > 0) {
|
|
3628
|
-
missing.push(`
|
|
3719
|
+
if ((task.changeMode === "read-only" || task.mutationPolicy === "forbidden") && changedFileEvidence.expected.length > 0) {
|
|
3720
|
+
missing.push(`mutation-forbidden task has observed changes (${changedFileEvidence.expected.join(", ")})`);
|
|
3629
3721
|
}
|
|
3630
3722
|
const acceptance = refreshAcceptanceReceipt(task, {
|
|
3631
3723
|
cwd,
|
|
@@ -3853,6 +3945,55 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
3853
3945
|
pi.on("thinking_level_select", (_event, ctx) => {
|
|
3854
3946
|
modelSelectionProvenance.observeThinkingSelection(ctx.sessionManager.getSessionId());
|
|
3855
3947
|
});
|
|
3948
|
+
pi.on("before_provider_request", (event, ctx) => {
|
|
3949
|
+
const normalized = normalizeOpenAiCodexReasoningPayload({
|
|
3950
|
+
payload: event.payload,
|
|
3951
|
+
provider: ctx.model?.provider,
|
|
3952
|
+
modelId: ctx.model?.id,
|
|
3953
|
+
hostThinkingLevel: pi.getThinkingLevel()
|
|
3954
|
+
});
|
|
3955
|
+
if (normalized.applicable) {
|
|
3956
|
+
telemetry(ctx, {
|
|
3957
|
+
event: "provider_request_reasoning",
|
|
3958
|
+
hostThinkingLevel: normalized.hostThinkingLevel,
|
|
3959
|
+
providerReasoningEffort: normalized.providerEffort,
|
|
3960
|
+
expectedProviderReasoningEffort: normalized.expectedProviderEffort,
|
|
3961
|
+
providerReasoningEffortMatches: normalized.providerEffort === normalized.expectedProviderEffort,
|
|
3962
|
+
providerReasoningNormalized: normalized.changed,
|
|
3963
|
+
providerReasoningReasonCode: normalized.reasonCode
|
|
3964
|
+
});
|
|
3965
|
+
}
|
|
3966
|
+
const wire = buildOpenAiCodexWireFingerprint({
|
|
3967
|
+
payload: normalized.changed ? normalized.payload : event.payload,
|
|
3968
|
+
provider: ctx.model?.provider,
|
|
3969
|
+
modelId: ctx.model?.id,
|
|
3970
|
+
workingDirectory: ctx.cwd,
|
|
3971
|
+
platformRoot: PLATFORM_ROOT
|
|
3972
|
+
});
|
|
3973
|
+
if (wire.applicable) {
|
|
3974
|
+
telemetry(ctx, {
|
|
3975
|
+
event: "provider_request_wire_surface",
|
|
3976
|
+
state: wire.state,
|
|
3977
|
+
reasonCode: wire.reasonCode,
|
|
3978
|
+
providerModelId: wire.modelId,
|
|
3979
|
+
instructionsHash: wire.instructionsHash,
|
|
3980
|
+
baseInstructionsHash: wire.baseInstructionsHash,
|
|
3981
|
+
instructionNormalization: wire.instructionNormalization,
|
|
3982
|
+
instructionChars: wire.instructionChars,
|
|
3983
|
+
orderedToolSurfaceHash: wire.orderedToolSurfaceHash,
|
|
3984
|
+
providerToolCount: wire.toolCount,
|
|
3985
|
+
deferredToolSurfaceHash: wire.deferredToolSurfaceHash,
|
|
3986
|
+
deferredToolCount: wire.deferredToolCount,
|
|
3987
|
+
deferredToolBatchCount: wire.deferredToolBatchCount,
|
|
3988
|
+
providerReasoningEffort: wire.reasoningEffort,
|
|
3989
|
+
providerTextVerbosity: wire.textVerbosity,
|
|
3990
|
+
providerToolChoiceKind: wire.toolChoiceKind,
|
|
3991
|
+
providerToolChoiceHash: wire.toolChoiceHash,
|
|
3992
|
+
requestPrefixFingerprint: wire.requestPrefixFingerprint
|
|
3993
|
+
});
|
|
3994
|
+
}
|
|
3995
|
+
return normalized.changed ? normalized.payload : undefined;
|
|
3996
|
+
});
|
|
3856
3997
|
|
|
3857
3998
|
function freshModelRouteBoundary(ctx: ExtensionContext): boolean {
|
|
3858
3999
|
try {
|
|
@@ -3914,7 +4055,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
3914
4055
|
let taskIdentity = runtimeState.taskIdentity(ctx);
|
|
3915
4056
|
if (!taskIdentity) {
|
|
3916
4057
|
const task = activeSessionTask(ctx.cwd, ctx.sessionManager.getSessionId()) as TaskContract | undefined;
|
|
3917
|
-
if (task) {
|
|
4058
|
+
if (task?.trace.outcome === "pending") {
|
|
3918
4059
|
runtimeState.cacheTaskIdentity(ctx, task);
|
|
3919
4060
|
taskIdentity = { taskId: task.taskId, taskRunId: task.taskRunId };
|
|
3920
4061
|
}
|
|
@@ -4066,7 +4207,8 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4066
4207
|
const snapshot = workingTreeSnapshot(ctx.cwd) as Record<string, string>;
|
|
4067
4208
|
const resume = workingTreeSnapshotHasUnavailableEvidence(snapshot)
|
|
4068
4209
|
? { openRepair: false }
|
|
4069
|
-
: { openRepair: taskAuthorityDecision(task, "CAP-13", "block").allowed
|
|
4210
|
+
: { openRepair: (taskAuthorityDecision(task, "CAP-13", "block").allowed
|
|
4211
|
+
|| taskAuthorityDecision(task, "CAP-12", "mutate").allowed) && semanticRepairRuntime.resume({
|
|
4070
4212
|
cwd: ctx.cwd,
|
|
4071
4213
|
task,
|
|
4072
4214
|
sessionId: ctx.sessionManager.getSessionId(),
|
|
@@ -4101,6 +4243,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4101
4243
|
});
|
|
4102
4244
|
|
|
4103
4245
|
registerInputHook(pi, {
|
|
4246
|
+
state: runtimeState,
|
|
4104
4247
|
boilerplateCollapseChars: BOILERPLATE_COLLAPSE_CHARS,
|
|
4105
4248
|
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 }),
|
|
4106
4249
|
readProtectedPaths: (ctx) => effectiveProtectedPaths(policy, loadProfileFromContext(ctx)).readProtectedPaths,
|
|
@@ -4127,6 +4270,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4127
4270
|
registerAgentStartHook(pi, {
|
|
4128
4271
|
state: runtimeState,
|
|
4129
4272
|
autoContextEnabled,
|
|
4273
|
+
contextDeltaShadowMode: contextBudgetConfig(policy).contextDeltaShadow,
|
|
4130
4274
|
activeTask: (ctx) => activeSessionTask(ctx.cwd, ctx.sessionManager.getSessionId()) as TaskContract | undefined,
|
|
4131
4275
|
readProtectedPaths: (ctx) => effectiveProtectedPaths(policy, loadProfileFromContext(ctx)).readProtectedPaths,
|
|
4132
4276
|
contextExcludePatterns: (ctx) => contextIndexExcludePatterns(policy, loadProfileFromContext(ctx)),
|
|
@@ -4154,7 +4298,10 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4154
4298
|
completionProjection: completionTaskProjection,
|
|
4155
4299
|
evaluateGate: (cwd, task, currentDigests, currentDigest) => {
|
|
4156
4300
|
const gate = evaluateTaskGate(cwd, task, policy, { currentDigests, currentWorkingTreeDigest: currentDigest });
|
|
4157
|
-
const repairBlock = taskAuthorityDecision(task, "CAP-13", "block").allowed
|
|
4301
|
+
const repairBlock = (taskAuthorityDecision(task, "CAP-13", "block").allowed
|
|
4302
|
+
|| taskAuthorityDecision(task, "CAP-12", "mutate").allowed)
|
|
4303
|
+
? semanticRepairRuntime.completionBlock(cwd, task.taskRunId)
|
|
4304
|
+
: undefined;
|
|
4158
4305
|
return repairBlock ? { ...gate, decision: "fail", missing: [...new Set([...gate.missing, repairBlock])] } : gate;
|
|
4159
4306
|
},
|
|
4160
4307
|
writeTask,
|
|
@@ -4173,6 +4320,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4173
4320
|
state: runtimeState,
|
|
4174
4321
|
activeTask: (ctx) => activeSessionTask(ctx.cwd, ctx.sessionManager.getSessionId()) as TaskContract | undefined,
|
|
4175
4322
|
maxManifestFiles: contextBudgetConfig(policy).maxManifestFiles,
|
|
4323
|
+
flushObservedTaskContext,
|
|
4176
4324
|
readProtectedPaths: (ctx) => effectiveProtectedPaths(policy, loadProfileFromContext(ctx)).readProtectedPaths,
|
|
4177
4325
|
recordObservedBash: (observed) => bashResults.record(observed),
|
|
4178
4326
|
observedBashLedgerPath,
|
|
@@ -4188,7 +4336,10 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4188
4336
|
now: nowIso,
|
|
4189
4337
|
completeSemanticRepair: (ctx, event, metadata) => {
|
|
4190
4338
|
const task = activeSessionTask(ctx.cwd, ctx.sessionManager.getSessionId()) as TaskContract | undefined;
|
|
4191
|
-
return task && taskAuthorityDecision(task, "CAP-13", "mutate").allowed
|
|
4339
|
+
return task && (taskAuthorityDecision(task, "CAP-13", "mutate").allowed
|
|
4340
|
+
|| taskAuthorityDecision(task, "CAP-12", "mutate").allowed)
|
|
4341
|
+
? semanticRepairRuntime.complete(ctx, task, event, metadata)
|
|
4342
|
+
: undefined;
|
|
4192
4343
|
},
|
|
4193
4344
|
syncTrajectory: (ctx, contextObserved) => {
|
|
4194
4345
|
const task = activeSessionTask(ctx.cwd, ctx.sessionManager.getSessionId()) as TaskContract | undefined;
|
|
@@ -4202,10 +4353,19 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4202
4353
|
telemetry,
|
|
4203
4354
|
activity: recordActivity,
|
|
4204
4355
|
beforeAuthorize: (event, ctx) => {
|
|
4205
|
-
|
|
4356
|
+
const toolInput = isPlainRecord(event.input) ? event.input : {};
|
|
4357
|
+
const targetInspection = authorizationMutationTargetInspection(ctx.cwd, event.toolName, toolInput);
|
|
4358
|
+
if (targetInspection.reason) return { block: true, reason: `Blocked ${event.toolName}: ${targetInspection.reason}.` };
|
|
4359
|
+
if (!["edit", "write"].includes(event.toolName) && !targetInspection.exactLocalPatchExecutor) return;
|
|
4206
4360
|
const task = activeSessionTask(ctx.cwd, ctx.sessionManager.getSessionId()) as TaskContract | undefined;
|
|
4207
4361
|
if (!task || task.trace.outcome !== "pending" || task.changeMode !== "source-change") return;
|
|
4208
|
-
|
|
4362
|
+
const semanticRepairEnabled = taskAuthorityDecision(task, "CAP-13", "mutate").allowed;
|
|
4363
|
+
const semanticRepairExactlyOff = !taskAuthorityDecision(task, "CAP-13", "observe").allowed;
|
|
4364
|
+
const boundedRecoveryEnabled = autoRecoveryEnabled
|
|
4365
|
+
&& taskAuthorityDecision(task, "CAP-09", "block").allowed
|
|
4366
|
+
&& taskAuthorityDecision(task, "CAP-12", "mutate").allowed
|
|
4367
|
+
&& semanticRepairExactlyOff;
|
|
4368
|
+
if (!semanticRepairEnabled && !boundedRecoveryEnabled) return;
|
|
4209
4369
|
const phase = trajectoryRuntime.status(ctx.cwd, task.taskRunId);
|
|
4210
4370
|
if (!phase.enforcementSafe || !["verify", "review"].includes(String(phase.phase))) return;
|
|
4211
4371
|
|
|
@@ -4213,16 +4373,41 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4213
4373
|
if (workingTreeSnapshotHasUnavailableEvidence(currentSnapshot)) return;
|
|
4214
4374
|
const currentDigest = workingTreeEvidenceDigest(currentSnapshot);
|
|
4215
4375
|
const expectedReviewPaths = taskDeltaFilesFromSnapshot(task, currentSnapshot);
|
|
4216
|
-
const
|
|
4217
|
-
const targets = taskMutationTargets(ctx.cwd, event.toolName, toolInput);
|
|
4376
|
+
const targets = targetInspection.targets;
|
|
4218
4377
|
const boundedInScopeMutation = targets.length > 0
|
|
4219
4378
|
&& targets.every((file) => taskScopeIncludesPath(task.scope, file));
|
|
4379
|
+
const verifierCurrent = allVerifyCommandsPassCurrentTree(task, currentDigest);
|
|
4220
4380
|
|
|
4221
|
-
if (phase.phase === "verify" && boundedInScopeMutation && semanticRepairRuntime.prepare({
|
|
4381
|
+
if (phase.phase === "verify" && boundedInScopeMutation && semanticRepairEnabled && semanticRepairRuntime.prepare({
|
|
4222
4382
|
ctx, task, event, currentDigest, currentDeltaPaths: expectedReviewPaths, targetPaths: targets,
|
|
4223
|
-
verifierCurrent
|
|
4383
|
+
verifierCurrent
|
|
4224
4384
|
})) return;
|
|
4225
4385
|
|
|
4386
|
+
// A public verifier can pass before every explicit user criterion is
|
|
4387
|
+
// covered. CAP-12 therefore reserves one exact structured call when the
|
|
4388
|
+
// model catches its own omission in the same task/run/session. The call
|
|
4389
|
+
// still traverses every normal authorization check; verify -> repair is
|
|
4390
|
+
// committed only by the tool-result hook after a successful tree change.
|
|
4391
|
+
if (phase.phase === "verify" && boundedRecoveryEnabled && verifierCurrent && boundedInScopeMutation) {
|
|
4392
|
+
const authoredFileDigests = runtimeState.successfulModelMutationDigests(
|
|
4393
|
+
{ taskId: task.taskId, taskRunId: task.taskRunId, sessionId: task.sessionId },
|
|
4394
|
+
currentSnapshot
|
|
4395
|
+
);
|
|
4396
|
+
if (semanticRepairRuntime.prepareBoundedRecovery({
|
|
4397
|
+
ctx,
|
|
4398
|
+
task,
|
|
4399
|
+
event,
|
|
4400
|
+
currentDigest,
|
|
4401
|
+
currentDeltaPaths: expectedReviewPaths,
|
|
4402
|
+
observedChangedPaths: task.observedChangedFiles,
|
|
4403
|
+
authoredFileDigests,
|
|
4404
|
+
targetPaths: targets,
|
|
4405
|
+
verifierCurrent
|
|
4406
|
+
})) return;
|
|
4407
|
+
}
|
|
4408
|
+
|
|
4409
|
+
if (!semanticRepairEnabled) return;
|
|
4410
|
+
|
|
4226
4411
|
let checkpoint = runtimeState.performanceReviewCheckpoint(task.taskRunId);
|
|
4227
4412
|
const checkpointReady = checkpoint?.workingTreeDigest === currentDigest
|
|
4228
4413
|
&& checkpoint.reviewSatisfied
|
|
@@ -4272,8 +4457,10 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4272
4457
|
const currentDigest = workingTreeEvidenceDigest(workingTreeSnapshot(ctx.cwd) as Record<string, string>);
|
|
4273
4458
|
const phase = trajectoryRuntime.status(ctx.cwd, task.taskRunId);
|
|
4274
4459
|
const toolInput = isPlainRecord(event.input) ? event.input : {};
|
|
4275
|
-
const
|
|
4276
|
-
if (
|
|
4460
|
+
const targetInspection = authorizationMutationTargetInspection(ctx.cwd, event.toolName, toolInput);
|
|
4461
|
+
if (targetInspection.reason) return { block: true, reason: `Blocked ${event.toolName}: ${targetInspection.reason}.` };
|
|
4462
|
+
const targets = targetInspection.targets;
|
|
4463
|
+
if ((["edit", "write"].includes(event.toolName) || targetInspection.patchCarrier) && (
|
|
4277
4464
|
targets.length === 0 || targets.some((file) => !taskScopeIncludesPath(task.scope, file))
|
|
4278
4465
|
)) {
|
|
4279
4466
|
runtimeState.denyPerformanceReviewTool(task.taskRunId);
|
|
@@ -4347,6 +4534,11 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4347
4534
|
authorize: async (event, ctx) => {
|
|
4348
4535
|
const preTask = activeSessionTask(ctx.cwd, ctx.sessionManager.getSessionId()) as TaskContract | undefined;
|
|
4349
4536
|
const preInput = isPlainRecord(event.input) ? event.input : {};
|
|
4537
|
+
const authorizationTargetInspection = authorizationMutationTargetInspection(ctx.cwd, event.toolName, preInput);
|
|
4538
|
+
if (authorizationTargetInspection.reason) {
|
|
4539
|
+
return { block: true, reason: `Blocked ${event.toolName}: ${authorizationTargetInspection.reason}.` };
|
|
4540
|
+
}
|
|
4541
|
+
const authorizationTargets = authorizationTargetInspection.targets;
|
|
4350
4542
|
const preSnapshot = preTask ? workingTreeSnapshot(ctx.cwd) as Record<string, string> : undefined;
|
|
4351
4543
|
const reservedFirstCall = Boolean(preTask && preSnapshot && !workingTreeSnapshotHasUnavailableEvidence(preSnapshot) && semanticRepairRuntime.reservedCallMatches({
|
|
4352
4544
|
cwd: ctx.cwd,
|
|
@@ -4355,7 +4547,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4355
4547
|
toolCallId: String(event.toolCallId),
|
|
4356
4548
|
toolName: event.toolName,
|
|
4357
4549
|
currentDigest: workingTreeEvidenceDigest(preSnapshot),
|
|
4358
|
-
targetPaths:
|
|
4550
|
+
targetPaths: authorizationTargets
|
|
4359
4551
|
}));
|
|
4360
4552
|
const phaseDecision = reservedFirstCall || (preTask?.workingTreeDigestMigration?.status === "verification-refresh-required" && SHELL_TOOL_NAMES.has(event.toolName)) ? undefined : phaseToolRuntime.toolDecision(ctx, event.toolName);
|
|
4361
4553
|
if (phaseDecision) return phaseDecision;
|
|
@@ -4413,10 +4605,11 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4413
4605
|
const taskContractRequired = governedTaskProfile
|
|
4414
4606
|
&& runtime.finalGate === "enforce"
|
|
4415
4607
|
&& finalGateConfig(policy).requireTaskContract;
|
|
4416
|
-
if (sessionTask?.trace.outcome === "pending" && sessionTask.changeMode === "read-only"
|
|
4608
|
+
if (sessionTask?.trace.outcome === "pending" && (sessionTask.changeMode === "read-only" || sessionTask.mutationPolicy === "forbidden")
|
|
4609
|
+
&& !SHELL_TOOL_NAMES.has(event.toolName) && isTaskMutationTool(event.toolName, toolInput)) {
|
|
4417
4610
|
return {
|
|
4418
4611
|
block: true,
|
|
4419
|
-
reason: `Task ${sessionTask.taskId} is read-only; ${event.toolName} cannot mutate project or external state in this task. Start a
|
|
4612
|
+
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.`
|
|
4420
4613
|
};
|
|
4421
4614
|
}
|
|
4422
4615
|
const preparedInput = prepareToolInputForPolicy(event.toolName, toolInput, policy);
|
|
@@ -4459,10 +4652,13 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4459
4652
|
&& !candidate.startsWith("../")
|
|
4460
4653
|
&& !candidate.startsWith(".pi/piagent-state/")
|
|
4461
4654
|
));
|
|
4462
|
-
|
|
4655
|
+
const mutationForbidden = sessionTask?.trace.outcome === "pending"
|
|
4656
|
+
&& (sessionTask.changeMode === "read-only" || sessionTask.mutationPolicy === "forbidden");
|
|
4657
|
+
const exactForbiddenTaskVerifier = mutationForbidden && sessionTask.changeMode === "source-change" && configuredVerifierShell;
|
|
4658
|
+
if (mutationForbidden && !exactForbiddenTaskVerifier && !isReadOnlyTaskShellCommand(command, execDecision.segments)) {
|
|
4463
4659
|
return {
|
|
4464
4660
|
block: true,
|
|
4465
|
-
reason: `Task ${sessionTask.taskId} is read-only; this shell command is not in the read-only inspection allowlist.`
|
|
4661
|
+
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"}.`
|
|
4466
4662
|
};
|
|
4467
4663
|
}
|
|
4468
4664
|
if (execDecision.mode !== "off" && execDecision.decision === "forbid") {
|
|
@@ -4519,18 +4715,21 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4519
4715
|
// Shell inputs need command grammar, not field-by-field path guessing: an
|
|
4520
4716
|
// exclusion selector in `args` is not an accessed path. The semantic shell
|
|
4521
4717
|
// checks above already inspect the normalized command, including redirects.
|
|
4718
|
+
const policyPathInput = authorizationTargetInspection.patchCarrier
|
|
4719
|
+
? { paths: authorizationTargets }
|
|
4720
|
+
: preparedInput.input;
|
|
4522
4721
|
const pathDecision = SHELL_TOOL_NAMES.has(event.toolName) ? { block: false, reason: undefined } : evaluatePathLikeToolAccess(
|
|
4523
4722
|
ctx.cwd,
|
|
4524
4723
|
preparedInput.proxyToolName ?? event.toolName,
|
|
4525
|
-
|
|
4724
|
+
policyPathInput,
|
|
4526
4725
|
pathPolicy.writeProtectedPaths,
|
|
4527
4726
|
pathPolicy.readProtectedPaths,
|
|
4528
4727
|
pathPolicy.readOnlyPaths,
|
|
4529
4728
|
permissionProfile.mode === "trusted-full-access" ? undefined : capabilityState.filesystemRead,
|
|
4530
4729
|
permissionProfile.mode === "trusted-full-access" ? undefined : capabilityState.filesystemWrite,
|
|
4531
4730
|
{
|
|
4532
|
-
forceScopeAware: Boolean(preparedInput.proxyToolName),
|
|
4533
|
-
forceWrite: preparedInput.proxyAction?.decision === "confirm",
|
|
4731
|
+
forceScopeAware: authorizationTargetInspection.patchCarrier || Boolean(preparedInput.proxyToolName),
|
|
4732
|
+
forceWrite: authorizationTargetInspection.patchCarrier || preparedInput.proxyAction?.decision === "confirm",
|
|
4534
4733
|
allowAmbiguousFilesystemContentFields: !usesKnownExternalProvider && !isPiagentTool(event.toolName)
|
|
4535
4734
|
}
|
|
4536
4735
|
);
|
|
@@ -4559,7 +4758,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4559
4758
|
|
|
4560
4759
|
const mutationTargets = SHELL_TOOL_NAMES.has(event.toolName)
|
|
4561
4760
|
? shellMutationTargets
|
|
4562
|
-
:
|
|
4761
|
+
: authorizationTargets;
|
|
4563
4762
|
const directProjectMutation = !SHELL_TOOL_NAMES.has(event.toolName) && (
|
|
4564
4763
|
WRITE_TOOL_NAMES.has(event.toolName)
|
|
4565
4764
|
|| mutationTargets.length > 0
|
|
@@ -4583,7 +4782,9 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4583
4782
|
opaqueCarrier: semanticOpaqueCarrier,
|
|
4584
4783
|
targetExtractionComplete: SHELL_TOOL_NAMES.has(event.toolName)
|
|
4585
4784
|
? !shellHasOpaqueWritePrimitive(authorizedShellCommand)
|
|
4586
|
-
:
|
|
4785
|
+
: authorizationTargetInspection.patchCarrier
|
|
4786
|
+
? authorizationTargetInspection.exactLocalPatchExecutor
|
|
4787
|
+
: !semanticOpaqueCarrier && preparedInput.proxyShellCarrier !== true,
|
|
4587
4788
|
recordedAt: nowIso()
|
|
4588
4789
|
})
|
|
4589
4790
|
: { handled: false, allowed: false, bypassPhase: false };
|
|
@@ -4596,7 +4797,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4596
4797
|
});
|
|
4597
4798
|
if (phaseMutationDecision) return phaseMutationDecision;
|
|
4598
4799
|
if (pendingHumanApproval && !sessionTask) {
|
|
4599
|
-
const approval = await piApprovalBroker.request({ cwd: ctx.cwd, rawSessionId: ctx.sessionManager.getSessionId(), toolCallId: String(event.toolCallId), expectedTask: null, action: { ...pendingHumanApproval.action, preconditionClass: "runtime-only", treePrecondition: null }, terminalConfirm: () => ctx.ui.confirm(pendingHumanApproval!.prompt, pendingHumanApproval!.title) });
|
|
4800
|
+
const approval = await piApprovalBroker.request({ cwd: ctx.cwd, rawSessionId: ctx.sessionManager.getSessionId(), toolCallId: String(event.toolCallId), expectedTask: null, action: { ...pendingHumanApproval.action, preconditionClass: "runtime-only", treePrecondition: null }, terminalConfirm: () => ctx.ui.confirm(pendingHumanApproval!.prompt, pendingHumanApproval!.title), unavailableFallback: approvalUnavailableFallback(ctx) });
|
|
4600
4801
|
if (!approval.allowed) return { block: true, reason: pendingHumanApproval.action.kind === "external-provider-action" && !SHELL_TOOL_NAMES.has(event.toolName) ? `User denied external provider action: ${event.toolName}` : `User denied command: ${pendingHumanApproval.action.reason}` };
|
|
4601
4802
|
pendingHumanApproval = null;
|
|
4602
4803
|
}
|
|
@@ -4642,10 +4843,12 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4642
4843
|
};
|
|
4643
4844
|
}
|
|
4644
4845
|
}
|
|
4645
|
-
if (runtime.contextBudget !== "off" && ["write", "edit"].includes(event.toolName)) {
|
|
4646
|
-
const
|
|
4647
|
-
|
|
4648
|
-
|
|
4846
|
+
if (runtime.contextBudget !== "off" && (["write", "edit"].includes(event.toolName) || authorizationTargetInspection.patchCarrier)) {
|
|
4847
|
+
const budgetTargets = authorizationTargetInspection.patchCarrier
|
|
4848
|
+
? authorizationTargets
|
|
4849
|
+
: [extractLikelyPathFromInput(ctx.cwd, preInput)].filter((item): item is string => Boolean(item));
|
|
4850
|
+
const budget = contextBudgetConfig(policy);
|
|
4851
|
+
for (const relativePath of budgetTargets) {
|
|
4649
4852
|
const stats = candidateFileBudget(ctx.cwd, relativePath, budget);
|
|
4650
4853
|
if (runtime.contextBudget === "enforce" && stats.exists && stats.overLimit) {
|
|
4651
4854
|
return { block: true, reason: `Context budget blocked editing large file ${relativePath}: ${stats.chars} chars > ${budget.maxContextFileChars}` };
|
|
@@ -4659,6 +4862,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4659
4862
|
: { workspaceRevision: `workspace-rev.${crypto.createHash("sha256").update(currentTreeDigest).digest("hex")}`, indexRevision: null, preimageDigest: currentTreeDigest };
|
|
4660
4863
|
const approval = await piApprovalBroker.request({ cwd: ctx.cwd, rawSessionId: ctx.sessionManager.getSessionId(), toolCallId: String(event.toolCallId), expectedTask: sessionTask ? { taskId: sessionTask.taskId, taskRunId: sessionTask.taskRunId } : null,
|
|
4661
4864
|
action: { ...pendingHumanApproval.action, treePrecondition }, terminalConfirm: () => ctx.ui.confirm(pendingHumanApproval!.prompt, pendingHumanApproval!.title),
|
|
4865
|
+
unavailableFallback: approvalUnavailableFallback(ctx),
|
|
4662
4866
|
recheck: () => !treePrecondition || (() => { const current = workingTreeSnapshot(ctx.cwd);
|
|
4663
4867
|
return !workingTreeSnapshotHasUnavailableEvidence(current) && workingTreeEvidenceDigest(current) === treePrecondition.preimageDigest; })() });
|
|
4664
4868
|
if (!approval.allowed) return { block: true, reason: pendingHumanApproval.action.kind === "external-provider-action" && !SHELL_TOOL_NAMES.has(event.toolName) ? `User denied external provider action: ${event.toolName}` : `User denied command: ${pendingHumanApproval.action.reason}` };
|
|
@@ -4673,13 +4877,16 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4673
4877
|
}
|
|
4674
4878
|
}
|
|
4675
4879
|
});
|
|
4880
|
+
// Register only after authorization is live. The same-name override is intentional:
|
|
4881
|
+
// guard target parsing, provenance, and atomicity require this exact tool contract.
|
|
4882
|
+
registerApplyPatchTool(pi, Type);
|
|
4676
4883
|
const registrationDeps = {
|
|
4677
4884
|
CONTEXT_INDEX_EDGE_KINDS, CONTEXT_INDEX_NODE_KINDS, DEFAULT_MAX_TASK_ATTEMPTS, FRESH_COMMAND_ACTIONS, FRESH_COMMAND_HELP,
|
|
4678
4885
|
ONBOARDING_COMMAND_ACTIONS, ORCHESTRATION_ROLES, PIAGENT_TOOL_NAMES, READ_ONLY_TOOL_NAMES, REVIEW_LENSES,
|
|
4679
4886
|
StringEnum, TECH_STACK_MANIFEST_FILE, TOOL_RESULT_CAPTURE_MAX_CHARS, TOOL_RESULT_COMPACT_CHAR_THRESHOLD, TOOL_RESULT_COMPACT_LINE_THRESHOLD,
|
|
4680
4887
|
TOOL_RESULT_PREVIEW_MAX_CHARS, Type, WORKFLOW_COMMAND_EXCLUSIONS, activateToolGroups, activeSessionTask,
|
|
4681
4888
|
activeTaskToolGroups, allVerifyCommandsPassCurrentTree, appendMemoryNote, appendSessionTrace, appendTrace,
|
|
4682
|
-
acceptanceBaselineGuidance, acceptanceProofGuidance, applyAcceptanceRecoveryProvenance, applyRuntimeLifecycleObservation, automaticAcceptanceCriteria, automaticReadOnlyTaskScope, automaticReviewLenses, automaticTaskIntakeMode, automaticTaskRiskLane, automaticTaskScope,
|
|
4889
|
+
acceptanceBaselineGuidance, acceptanceLanguageAdapterForPath, acceptanceProofGuidance, applyAcceptanceRecoveryProvenance, applyRuntimeLifecycleObservation, automaticAcceptanceCriteria, automaticReadOnlyTaskScope, automaticReviewLenses, automaticTaskIntakeMode, automaticTaskMutationPolicy, automaticTaskRiskLane, automaticTaskScope,
|
|
4683
4890
|
bashResults, bindSessionTask, buildAcceptanceReceipt, buildContextEfficiencyReport, buildContextIndexStatus, buildLiveTaskStatus, buildTaskEfficiencyMetrics,
|
|
4684
4891
|
buildContextIndexV2, buildContextPack, buildContextPreflight, buildProfileOptions, buildProfileTechOptions,
|
|
4685
4892
|
buildTestImpact, buildUsageSnapshot, candidateFileBudget, checkoutReferenceRepo, classifyContextTask,
|
|
@@ -4691,7 +4898,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4691
4898
|
extensionDir, externalActionPolicyConfig, extractDocument, finalGateConfig, findMatchingObservedBashResult,
|
|
4692
4899
|
formatContextPreflight, formatCount, formatLiveTaskStatus, formatPercent, formatTechOptionsText, formatTechSelectionSummary,
|
|
4693
4900
|
formatToolResultCaptureStatus, formatUsageSnapshot, fs, hasGitEvidenceRoot, hasOperatorSessionName, helpersMode,
|
|
4694
|
-
loadProfileFromContext, matchesAnyPath, matchesProtectedPath, mcpActions, mcpApprovalCache,
|
|
4901
|
+
isAcceptanceTestPath, loadProfileFromContext, matchesAnyPath, matchesProtectedPath, mcpActions, mcpApprovalCache,
|
|
4695
4902
|
memoryHandbookPath, memoryLocalDir, memorySummaryPath, normalizeProjectProfileName, normalizeRelative,
|
|
4696
4903
|
normalizeReviewLenses, normalizeTechSelections, normalizeWorkPlanSteps, nowIso, observedBashLedgerPath,
|
|
4697
4904
|
path, permissionOverrideFromContext, permissionProfilesConfig, policy, prefixCompletions,
|
|
@@ -4709,7 +4916,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4709
4916
|
techContextDirPath, techContextFilePath, techContextRelativePath, techOptionById, techStackPath,
|
|
4710
4917
|
telemetry, toolRegistryConfig, trajectoryRuntime, uniqueStrings, usageExactCommands,
|
|
4711
4918
|
validTaskScopePattern, validateNewWorkPlan, verifierCommandInstructions, verifyProjectCapabilityState, workingTreeEvidenceDigest,
|
|
4712
|
-
repositoryFileManifest, resolveTaskScopePatterns,
|
|
4919
|
+
repositoryFileManifest, repositoryFileManifestDetails, resolveTaskScopePatterns,
|
|
4713
4920
|
workingTreeSnapshot, workingTreeSnapshotHasUnavailableEvidence, writeContextIndex, writeProfileDocumentWithLock, writeProfileFromAdapter, writeProjectOnboarding,
|
|
4714
4921
|
writeTask, writeTechStackSelection
|
|
4715
4922
|
};
|
|
@@ -664,7 +664,7 @@ function commandRuleMatches(rule, segment, words) {
|
|
|
664
664
|
return arrayStartsWith(words, prefix);
|
|
665
665
|
}
|
|
666
666
|
const raw = Array.isArray(rule.value) ? rule.value.join(" ") : rule.value;
|
|
667
|
-
if (rule.match === "contains") return segment.toLowerCase().includes(raw.toLowerCase());
|
|
667
|
+
if (rule.match === "contains") return !(rule.ignoreSearchArguments && ["grep", "egrep", "fgrep", "rg"].includes(commandBasename(stripWrapper(words)[0] ?? ""))) && segment.toLowerCase().includes(raw.toLowerCase());
|
|
668
668
|
try {
|
|
669
669
|
return new RegExp(raw, "i").test(segment);
|
|
670
670
|
} catch {
|