@piagent/platform 1.6.0 → 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 +49 -0
- package/README.md +17 -14
- package/README.vi.md +26 -1
- package/benchmarks/production-v1/spend-control.v1.json +27 -3
- package/benchmarks/production-v1/suite.json +5 -1
- package/catalog/capabilities.json +16 -16
- package/docs/authority-manifest.md +1 -1
- package/docs/auto-delegation-policy.md +38 -73
- package/docs/command-reference-vietnamese.md +23 -31
- package/docs/context-engine.md +5 -3
- package/docs/context-window-policy.md +37 -1
- package/docs/distribution-standard.md +7 -7
- package/docs/en/architecture.md +26 -13
- package/docs/languages.json +4 -4
- package/docs/operator-manual-vietnamese.md +21 -41
- package/docs/publishing-for-teams.md +5 -0
- package/docs/quality-benchmark.md +179 -23
- package/docs/release-install-policy.md +6 -6
- package/docs/runtime-harness-standard.md +3 -3
- package/docs/security-threat-model.md +1 -1
- 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 +1 -1
- package/docs/team-onboarding.md +2 -2
- package/docs/vi/architecture.md +26 -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/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 +2 -2
- 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 +6 -6
- package/evals/fs5-pilot-protocol.v5.json +7 -6
- 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 +1 -1
- package/packages/piagent-core/README.md +13 -4
- package/packages/piagent-core/benchmark/benchmark-codex-relative-efficiency.js +698 -0
- package/packages/piagent-core/benchmark/benchmark-core.js +4 -3
- package/packages/piagent-core/benchmark/benchmark-forensics.js +4 -2
- package/packages/piagent-core/benchmark/benchmark-preflight.js +3 -1
- package/packages/piagent-core/benchmark/benchmark-provider-free-evidence.js +345 -0
- package/packages/piagent-core/benchmark/benchmark-record-validation.js +74 -5
- package/packages/piagent-core/benchmark/benchmark-report.js +15 -1
- package/packages/piagent-core/benchmark/benchmark-runtime-causal.js +244 -0
- package/packages/piagent-core/benchmark/benchmark-stage-diagnostic.js +201 -4
- package/packages/piagent-core/benchmark/benchmark-suite.js +22 -1
- package/packages/piagent-core/benchmark/benchmark-summary-support.js +4 -0
- package/packages/piagent-core/benchmark/benchmark-usage.js +15 -4
- package/packages/piagent-core/capabilities/runtime-integrity.js +3 -0
- package/packages/piagent-core/extensions/acceptance-behavior-proof.js +806 -0
- package/packages/piagent-core/extensions/acceptance-receipt.js +89 -48
- package/packages/piagent-core/extensions/context-engine.js +1 -11
- 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/criterion-context-pack.js +16 -3
- package/packages/piagent-core/extensions/criterion-graph.js +4 -1
- package/packages/piagent-core/extensions/fallback-base-policy.ts +101 -0
- package/packages/piagent-core/extensions/guard-types.ts +4 -0
- package/packages/piagent-core/extensions/piagent-guard.ts +194 -226
- package/packages/piagent-core/extensions/task-lifecycle.js +32 -0
- package/packages/piagent-core/extensions/task-state.js +69 -8
- package/packages/piagent-core/extensions/verification-intelligence.js +49 -12
- package/packages/piagent-core/extensions/workspace-evidence-roots.js +52 -16
- package/packages/piagent-core/package.json +1 -1
- package/packages/piagent-core/policies/base-policy.json +42 -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/hooks/completion-hook.ts +7 -6
- package/packages/piagent-core/runtime/hooks/input-hook.ts +6 -0
- package/packages/piagent-core/runtime/hooks/session-start-hook.ts +100 -1
- package/packages/piagent-core/runtime/hooks/tool-result-hook.ts +58 -72
- package/packages/piagent-core/runtime/hooks/tool-result-value-helpers.ts +66 -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-subagent-tree.ts +27 -4
- package/packages/piagent-core/runtime/inspection/tool-failure-classification.ts +20 -3
- package/packages/piagent-core/runtime/inspection/webui-snapshot.ts +47 -11
- 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/efficiency-metrics.ts +3 -0
- package/packages/piagent-core/runtime/quality/edit-freshness-guard.ts +102 -0
- package/packages/piagent-core/runtime/quality/exact-output-contract.ts +20 -10
- package/packages/piagent-core/runtime/quality/performance-assurance.ts +3 -3
- package/packages/piagent-core/runtime/recovery/continuation-budget.ts +1 -1
- 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 +7 -13
- package/packages/piagent-core/runtime/recovery/task-evidence-coverage-refresh.ts +217 -0
- package/packages/piagent-core/runtime/registration/context-commands.ts +16 -2
- 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 +4 -4
- package/packages/piagent-core/runtime/registration/session-commands.ts +1 -1
- package/packages/piagent-core/runtime/registration/task-completion-tools.ts +12 -9
- package/packages/piagent-core/runtime/registration/task-evidence-tools.ts +21 -12
- package/packages/piagent-core/runtime/registration/task-start-guidance.ts +19 -13
- package/packages/piagent-core/runtime/registration/task-start-manifest.ts +54 -0
- package/packages/piagent-core/runtime/registration/task-start-tool.ts +57 -57
- 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 +11 -7
- 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/phase-tools.ts +8 -8
- package/packages/piagent-core/runtime/tools/tool-groups.ts +12 -2
- 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 +167 -39
- 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 +6 -4
- package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-CxeZqTte.js → MarkdownMessage-B4XehTCU.js} +1 -1
- package/packages/piagent-webui/dist/client/assets/{index-DynIMe8I.js → index-CgOEyRPG.js} +4 -4
- 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-session-stream.ts +37 -21
- 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-watchdog.ts +9 -2
- package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +8 -7
- 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/transcript-projection.ts +91 -5
- package/schemas/codex-relative-efficiency-v1.schema.json +155 -0
- package/schemas/piagent-webui/runtime-event-v2.schema.json +15 -5
- 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 +15 -0
- package/scripts/benchmark-runner-core.mjs +14 -6
- package/scripts/benchmark-runner-finalization.mjs +4 -0
- package/scripts/configure-subagents.sh +42 -34
- package/scripts/helper-evaluation.mjs +1 -1
- package/scripts/team-doctor.sh +1 -1
- package/scripts/verify-local.sh +5 -0
|
@@ -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";
|
|
@@ -110,6 +110,7 @@ import {
|
|
|
110
110
|
} from "../runtime/session/tool-result-compaction.ts";
|
|
111
111
|
import { RuntimeSessionState } from "../runtime/session/runtime-state.ts";
|
|
112
112
|
import type { ObservedTaskContext } from "../runtime/session/runtime-state.ts";
|
|
113
|
+
import { reuseCurrentTreeExactVerifier } from "../runtime/verification/exact-verifier-reuse.ts";
|
|
113
114
|
import {
|
|
114
115
|
PIAGENT_TOOL_GROUPS,
|
|
115
116
|
PIAGENT_TOOL_NAMES,
|
|
@@ -161,11 +162,14 @@ import { inspectTaskResumeState } from "../runtime/recovery/resume-state.ts";
|
|
|
161
162
|
import { SemanticRepairRuntime } from "../runtime/recovery/semantic-repair-runtime.ts";
|
|
162
163
|
import { defaultRolePolicy } from "../runtime/orchestration/role-policy.ts";
|
|
163
164
|
import { helpersMode } from "../runtime/orchestration/helper-lifecycle.ts";
|
|
165
|
+
import { evaluateDirectSubagentDispatch, recordDirectSubagentResult, reserveDirectSubagentDispatch, taskHelperUsageMode } from "../runtime/orchestration/subagent-tool-policy.ts";
|
|
164
166
|
import { buildLiveTaskStatus, formatLiveTaskStatus } from "../runtime/product/operator-projections.ts";
|
|
165
167
|
import { buildTaskEfficiencyMetrics } from "../runtime/product/efficiency-metrics.ts";
|
|
166
168
|
import { performanceReviewToolDecision, performanceReviewToolKind } from "../runtime/quality/performance-assurance.ts";
|
|
167
169
|
import { expectedModelMutationProof } from "../runtime/quality/model-mutation-proof.ts";
|
|
170
|
+
import { EditFreshnessGuard, editFreshnessModeFromEnvironment } from "../runtime/quality/edit-freshness-guard.ts";
|
|
168
171
|
import { captureVerifierFileSnapshot } from "../runtime/inspection/verifier-snapshot-store.ts";
|
|
172
|
+
import { fallbackBasePolicy } from "./fallback-base-policy.ts";
|
|
169
173
|
import { prefixCompletions, registerPiagentTool, registerRuntimeCommand, registerRuntimeTool } from "../runtime/registration/extension-registration.ts";
|
|
170
174
|
import { FRESH_COMMAND_ACTIONS, FRESH_COMMAND_HELP, ONBOARDING_COMMAND_ACTIONS, WORKFLOW_COMMAND_EXCLUSIONS } from "../runtime/registration/operator-catalogs.ts";
|
|
171
175
|
import { registerPiagentStatusCommand } from "../runtime/registration/runtime-model-status.ts";
|
|
@@ -341,11 +345,11 @@ const DEFAULT_RUNTIME_POLICY: Required<RuntimePolicySettings> = {
|
|
|
341
345
|
|
|
342
346
|
const DEFAULT_ORCHESTRATION_POLICY: ResolvedOrchestrationPolicy = {
|
|
343
347
|
defaultMode: "solo-first",
|
|
344
|
-
maxConcurrentSubagents:
|
|
348
|
+
maxConcurrentSubagents: 1,
|
|
345
349
|
defaultReviewLenses: ["correctness", "tests", "scope"],
|
|
346
350
|
roleModelGuidance: {
|
|
347
351
|
planner: "Use the strongest available model for decomposition, architecture, risk, and acceptance criteria.",
|
|
348
|
-
worker: "
|
|
352
|
+
worker: "Disabled by default. The parent model owns all implementation and mutation.",
|
|
349
353
|
reviewer: "Use a model or thinking setting decorrelated from the worker when review quality matters.",
|
|
350
354
|
watchdog: "Use a strong model only for final risk review, security, release, or high-impact changes."
|
|
351
355
|
},
|
|
@@ -357,9 +361,9 @@ const DEFAULT_ORCHESTRATION_POLICY: ResolvedOrchestrationPolicy = {
|
|
|
357
361
|
readBeforeTask: true
|
|
358
362
|
},
|
|
359
363
|
rules: [
|
|
360
|
-
"Default to
|
|
361
|
-
"Use
|
|
362
|
-
"
|
|
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.",
|
|
363
367
|
"Treat Field Guide memory as advisory; verify every durable fact against current repository files.",
|
|
364
368
|
"Keep review lenses explicit so cheap review work catches drift before release."
|
|
365
369
|
]
|
|
@@ -386,176 +390,7 @@ const SHELL_TOOL_NAMES = new Set(["bash", "shell", "exec"]);
|
|
|
386
390
|
const MAX_MCP_PROXY_ARGS_CHARS = 131_072;
|
|
387
391
|
const SESSION_PERMISSION_OVERRIDES = new Map<string, PermissionProfileMode>();
|
|
388
392
|
|
|
389
|
-
|
|
390
|
-
// exactly the case where something is already wrong, so it must never be the
|
|
391
|
-
// looser of the two: this copy had drifted behind the file and silently dropped
|
|
392
|
-
// `.pi/piagent-state/**` -- the guard's own state -- along with the `sudo ` and
|
|
393
|
-
// `chmod -R 777` blocks. A missing policy file is a reason to be stricter, not a
|
|
394
|
-
// quiet downgrade. `tests/policy-core.test.mjs` fails if this drifts again.
|
|
395
|
-
const DEFAULT_POLICY: BasePolicy = {
|
|
396
|
-
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],
|
|
397
|
-
shellProtectedPaths: [".git/**", "**/auth.json", "**/.env", "**/.env.*", ".pi/piagent-state/**", ".pi/settings.json", ".pi/piagent-profile.json", ".pi/piagent-profile.lock.json", CONTEXT_INDEX_FILE],
|
|
398
|
-
blockedCommandPatterns: ["rm -rf /", "rm -rf ~", "rm -rf $HOME", "git reset --hard", "git clean -fd", "sudo ", "chmod -R 777"],
|
|
399
|
-
requireConfirmationPatterns: ["deploy", "release", "publish", "migration", "gh pr merge", "git push"],
|
|
400
|
-
defaultRequiredContext: ["AGENTS.md", "README.md"],
|
|
401
|
-
permissionProfiles: {
|
|
402
|
-
defaultMode: "workspace-write",
|
|
403
|
-
allowedModes: ["read-only", "workspace-write", "trusted-full-access"]
|
|
404
|
-
},
|
|
405
|
-
execPolicy: {
|
|
406
|
-
defaultMode: "enforce",
|
|
407
|
-
bannedPrefixSuggestions: [
|
|
408
|
-
["python"],
|
|
409
|
-
["python3"],
|
|
410
|
-
["node"],
|
|
411
|
-
["node", "-e"],
|
|
412
|
-
["bash"],
|
|
413
|
-
["bash", "-lc"],
|
|
414
|
-
["sh"],
|
|
415
|
-
["sh", "-c"],
|
|
416
|
-
["zsh"],
|
|
417
|
-
["zsh", "-lc"],
|
|
418
|
-
["git"],
|
|
419
|
-
["sudo"],
|
|
420
|
-
["env"]
|
|
421
|
-
],
|
|
422
|
-
rules: [
|
|
423
|
-
{
|
|
424
|
-
id: "prompt-git-add-broad",
|
|
425
|
-
action: "prompt",
|
|
426
|
-
match: "regex",
|
|
427
|
-
value: "(?:^|\\s)git\\s+(?:-C\\s+\\S+\\s+)?add\\s+(?:(?:--all|-A)(?:\\s+(?:\\.|:/))?|--\\s+(?:\\.|:/)|(?:\\.|:/))(?:\\s|$)",
|
|
428
|
-
reason: "Broad git staging can include unrelated or sensitive changes; inspect git status/diff and confirm the exact scope first."
|
|
429
|
-
}
|
|
430
|
-
]
|
|
431
|
-
},
|
|
432
|
-
contextBudget: {
|
|
433
|
-
defaultMode: "enforce",
|
|
434
|
-
contextDeltaShadow: "sample",
|
|
435
|
-
maxContextFileChars: 50000,
|
|
436
|
-
maxMemoryFileChars: 20000,
|
|
437
|
-
maxManifestFiles: 80,
|
|
438
|
-
warnFragmentChars: 4000
|
|
439
|
-
},
|
|
440
|
-
toolRegistry: {
|
|
441
|
-
defaultMode: "advisory",
|
|
442
|
-
alwaysAllowedTools: [
|
|
443
|
-
"piagent_tools",
|
|
444
|
-
"piagent_context_engine",
|
|
445
|
-
"piagent_context",
|
|
446
|
-
"piagent_permission_status",
|
|
447
|
-
"piagent_exec_policy_check",
|
|
448
|
-
"piagent_context_budget",
|
|
449
|
-
"piagent_tool_policy_check",
|
|
450
|
-
"piagent_task_gate_check",
|
|
451
|
-
"piagent_usage_snapshot",
|
|
452
|
-
"piagent_orchestration_policy",
|
|
453
|
-
"piagent_memory_status",
|
|
454
|
-
"piagent_memory_note",
|
|
455
|
-
"piagent_memory_search",
|
|
456
|
-
"piagent_memory_citation_record",
|
|
457
|
-
"piagent_context_index_status",
|
|
458
|
-
"piagent_context_index_record",
|
|
459
|
-
"piagent_context_index_search",
|
|
460
|
-
"piagent_profile_options",
|
|
461
|
-
"piagent_profile_apply",
|
|
462
|
-
"piagent_profile_tech_options",
|
|
463
|
-
"piagent_profile_tech_apply",
|
|
464
|
-
"piagent_profile_tech_context_record",
|
|
465
|
-
"piagent_project_onboarding_record",
|
|
466
|
-
"piagent_task_start",
|
|
467
|
-
"piagent_task_progress",
|
|
468
|
-
"piagent_source_checkout",
|
|
469
|
-
"piagent_context_record",
|
|
470
|
-
"piagent_verify_record",
|
|
471
|
-
"piagent_trace_record"
|
|
472
|
-
],
|
|
473
|
-
toolCapabilities: {
|
|
474
|
-
bash: ["shell"],
|
|
475
|
-
shell: ["shell"],
|
|
476
|
-
exec: ["shell"],
|
|
477
|
-
read: ["filesystem-readonly"],
|
|
478
|
-
grep: ["filesystem-readonly"],
|
|
479
|
-
find: ["filesystem-readonly"],
|
|
480
|
-
ls: ["filesystem-readonly"],
|
|
481
|
-
write: ["filesystem-write"],
|
|
482
|
-
edit: ["filesystem-write"],
|
|
483
|
-
apply_patch: ["filesystem-write"],
|
|
484
|
-
browser: ["browser"],
|
|
485
|
-
github: ["github"]
|
|
486
|
-
}
|
|
487
|
-
},
|
|
488
|
-
externalActionPolicy: {
|
|
489
|
-
defaultMode: "enforce",
|
|
490
|
-
providerKeywords: [
|
|
491
|
-
"github",
|
|
492
|
-
"gitlab",
|
|
493
|
-
"bitbucket",
|
|
494
|
-
"vercel",
|
|
495
|
-
"netlify",
|
|
496
|
-
"cloudflare",
|
|
497
|
-
"aws",
|
|
498
|
-
"gcp",
|
|
499
|
-
"azure",
|
|
500
|
-
"slack",
|
|
501
|
-
"teams",
|
|
502
|
-
"jira",
|
|
503
|
-
"linear",
|
|
504
|
-
"notion",
|
|
505
|
-
"figma",
|
|
506
|
-
"stripe",
|
|
507
|
-
"supabase",
|
|
508
|
-
"firebase"
|
|
509
|
-
],
|
|
510
|
-
writeVerbs: [
|
|
511
|
-
"add",
|
|
512
|
-
"approve",
|
|
513
|
-
"archive",
|
|
514
|
-
"assign",
|
|
515
|
-
"close",
|
|
516
|
-
"comment",
|
|
517
|
-
"create",
|
|
518
|
-
"delete",
|
|
519
|
-
"deploy",
|
|
520
|
-
"dispatch",
|
|
521
|
-
"merge",
|
|
522
|
-
"open",
|
|
523
|
-
"post",
|
|
524
|
-
"publish",
|
|
525
|
-
"push",
|
|
526
|
-
"release",
|
|
527
|
-
"remove",
|
|
528
|
-
"reopen",
|
|
529
|
-
"run",
|
|
530
|
-
"send",
|
|
531
|
-
"submit",
|
|
532
|
-
"trigger",
|
|
533
|
-
"update",
|
|
534
|
-
"upload",
|
|
535
|
-
"write"
|
|
536
|
-
],
|
|
537
|
-
safeVerbs: [
|
|
538
|
-
"fetch",
|
|
539
|
-
"find",
|
|
540
|
-
"get",
|
|
541
|
-
"inspect",
|
|
542
|
-
"list",
|
|
543
|
-
"read",
|
|
544
|
-
"search",
|
|
545
|
-
"show",
|
|
546
|
-
"view"
|
|
547
|
-
]
|
|
548
|
-
},
|
|
549
|
-
finalGate: {
|
|
550
|
-
defaultMode: "enforce",
|
|
551
|
-
requireTaskContract: true,
|
|
552
|
-
requireContextManifest: true,
|
|
553
|
-
requireVerifyEvidence: true,
|
|
554
|
-
requireTrace: true,
|
|
555
|
-
requirePassingVerify: true
|
|
556
|
-
},
|
|
557
|
-
orchestrationPolicy: DEFAULT_ORCHESTRATION_POLICY
|
|
558
|
-
};
|
|
393
|
+
const DEFAULT_POLICY = fallbackBasePolicy(CONTEXT_INDEX_FILE, DEFAULT_ORCHESTRATION_POLICY);
|
|
559
394
|
|
|
560
395
|
// Which platform supplies the adapters is fixed by where this file is installed,
|
|
561
396
|
// so it is resolved once here rather than threaded through every profile load.
|
|
@@ -1016,7 +851,12 @@ function inspectPathInputsFromInput(
|
|
|
1016
851
|
const seen = new Set<string>();
|
|
1017
852
|
const walked = walkStringInputs(input, [], 0, includeFilesystemContentFields);
|
|
1018
853
|
for (const item of walked.items) {
|
|
1019
|
-
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;
|
|
1020
860
|
if (normalized === undefined) continue;
|
|
1021
861
|
const key = `${item.field}\0${normalized}`;
|
|
1022
862
|
if (seen.has(key)) continue;
|
|
@@ -1126,6 +966,7 @@ function evaluatePathLikeToolAccess(
|
|
|
1126
966
|
forceScopeAware?: boolean;
|
|
1127
967
|
forceWrite?: boolean;
|
|
1128
968
|
allowAmbiguousFilesystemContentFields?: boolean;
|
|
969
|
+
sourceCheckoutReadRoots?: string[];
|
|
1129
970
|
} = {}
|
|
1130
971
|
): { block: boolean; reason?: string } {
|
|
1131
972
|
const scopeAwareTool = options.forceScopeAware === true || ["read", "write", "edit", "grep", "find", "ls"].includes(toolName)
|
|
@@ -1167,9 +1008,13 @@ function evaluatePathLikeToolAccess(
|
|
|
1167
1008
|
}
|
|
1168
1009
|
|
|
1169
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;
|
|
1170
1015
|
if (scopeAwareTool && isFilesystemScopeField(item.field)) {
|
|
1171
1016
|
const boundary = inspectRepositoryPathBoundary(cwd, item.path);
|
|
1172
|
-
if (boundary.reason) return { block: true, reason: `Blocked ${toolName}: ${boundary.reason}` };
|
|
1017
|
+
if (boundary.reason && !sourceCheckoutReadRoot) return { block: true, reason: `Blocked ${toolName}: ${boundary.reason}` };
|
|
1173
1018
|
}
|
|
1174
1019
|
const resolvedPath = resolveRepositoryPathCandidate(cwd, item.path);
|
|
1175
1020
|
const readMatched = matchesProtectedPath(item.path, readProtectedPaths)
|
|
@@ -1181,7 +1026,6 @@ function evaluatePathLikeToolAccess(
|
|
|
1181
1026
|
};
|
|
1182
1027
|
}
|
|
1183
1028
|
|
|
1184
|
-
const fieldAccessMode = filesystemFieldAccessMode(toolName, item.field, writesFilesystem);
|
|
1185
1029
|
if (fieldAccessMode === "write") {
|
|
1186
1030
|
const readOnlyMatched = matchesProtectedPath(item.path, readOnlyPaths)
|
|
1187
1031
|
?? (resolvedPath ? matchesProtectedPath(resolvedPath, readOnlyPaths) : undefined);
|
|
@@ -1205,7 +1049,8 @@ function evaluatePathLikeToolAccess(
|
|
|
1205
1049
|
reason: `Blocked ${toolName} write outside resolved filesystem scope from ${item.field}: ${item.path}`
|
|
1206
1050
|
};
|
|
1207
1051
|
}
|
|
1208
|
-
} else if (scopeAwareTool && filesystemRead && isFilesystemScopeField(item.field)
|
|
1052
|
+
} else if (scopeAwareTool && filesystemRead && isFilesystemScopeField(item.field)
|
|
1053
|
+
&& !sourceCheckoutReadRoot && !matchesAnyPath(item.path, filesystemRead)) {
|
|
1209
1054
|
return {
|
|
1210
1055
|
block: true,
|
|
1211
1056
|
reason: `Blocked ${toolName} read outside resolved filesystem scope from ${item.field}: ${item.path}`
|
|
@@ -1236,6 +1081,21 @@ function evaluatePathLikeToolAccess(
|
|
|
1236
1081
|
return { block: false };
|
|
1237
1082
|
}
|
|
1238
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
|
+
|
|
1239
1099
|
function stateRoot(cwd: string): string {
|
|
1240
1100
|
return path.join(cwd, ".pi", "piagent-state");
|
|
1241
1101
|
}
|
|
@@ -1513,7 +1373,7 @@ function resolveOrchestrationPolicy(profile: ProjectProfile, policy: BasePolicy)
|
|
|
1513
1373
|
};
|
|
1514
1374
|
return {
|
|
1515
1375
|
defaultMode: normalizeOrchestrationMode(configured.defaultMode, defaultPolicy.defaultMode),
|
|
1516
|
-
maxConcurrentSubagents: boundedInteger(configured.maxConcurrentSubagents, defaultPolicy.maxConcurrentSubagents, 0,
|
|
1376
|
+
maxConcurrentSubagents: Math.min(1, boundedInteger(configured.maxConcurrentSubagents, defaultPolicy.maxConcurrentSubagents, 0, 1)),
|
|
1517
1377
|
defaultReviewLenses: normalizeReviewLenses(configured.defaultReviewLenses, defaultPolicy.defaultReviewLenses),
|
|
1518
1378
|
roleModelGuidance,
|
|
1519
1379
|
fieldGuide: {
|
|
@@ -1846,13 +1706,18 @@ function isReadOnlyTaskShellCommand(
|
|
|
1846
1706
|
if (/[<>]|`|\$\(/.test(command)) {
|
|
1847
1707
|
return false;
|
|
1848
1708
|
}
|
|
1849
|
-
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"]);
|
|
1850
1710
|
const safeGitSubcommands = new Set(["status", "diff", "log", "show", "ls-files", "rev-parse"]);
|
|
1851
1711
|
return segments.length > 0 && segments.every((segment) => {
|
|
1852
1712
|
const words = segment.words.filter(Boolean);
|
|
1853
1713
|
const executable = path.basename(words[0] ?? "");
|
|
1854
1714
|
if (executable === "sed" && words.some((word) => /^-.*i/.test(word))) return false;
|
|
1855
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;
|
|
1856
1721
|
if (safeCommands.has(executable)) return true;
|
|
1857
1722
|
if (executable === "git") return safeGitSubcommands.has(words[1] ?? "");
|
|
1858
1723
|
if (executable === "command") return words[1] === "-v";
|
|
@@ -1860,6 +1725,16 @@ function isReadOnlyTaskShellCommand(
|
|
|
1860
1725
|
});
|
|
1861
1726
|
}
|
|
1862
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
|
+
|
|
1863
1738
|
const PROJECT_MUTATING_EXECUTABLES = new Set([
|
|
1864
1739
|
"apply_patch", "bash", "chmod", "chown", "cp", "dd", "install", "ln", "make", "mkdir", "mv", "prename", "rename",
|
|
1865
1740
|
"node", "patch", "perl", "php", "python", "python3", "rm", "rmdir", "rsync", "ruby", "scp", "sh",
|
|
@@ -3714,7 +3589,9 @@ function evaluateTaskGate(
|
|
|
3714
3589
|
if (task.changedFiles.length === 0) missing.push("changed files");
|
|
3715
3590
|
if (changedFileEvidence.undeclared.length > 0) missing.push(`declared observed changes (${changedFileEvidence.undeclared.join(", ")})`);
|
|
3716
3591
|
if (changedFileEvidence.unsupportedClaims.length > 0) missing.push(`supported changed-file claims (${changedFileEvidence.unsupportedClaims.join(", ")})`);
|
|
3717
|
-
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
|
+
}
|
|
3718
3595
|
}
|
|
3719
3596
|
if ((task.changeMode === "read-only" || task.mutationPolicy === "forbidden") && changedFileEvidence.expected.length > 0) {
|
|
3720
3597
|
missing.push(`mutation-forbidden task has observed changes (${changedFileEvidence.expected.join(", ")})`);
|
|
@@ -3725,9 +3602,11 @@ function evaluateTaskGate(
|
|
|
3725
3602
|
currentWorkingTreeDigest
|
|
3726
3603
|
});
|
|
3727
3604
|
const semanticEnforcement = taskAuthorityDecision(task, "CAP-13", "block").allowed;
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
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`);
|
|
3731
3610
|
const acceptanceConflicts = acceptanceSemanticConflicts(task, {
|
|
3732
3611
|
cwd,
|
|
3733
3612
|
changedFiles: changedFileEvidence.expected
|
|
@@ -3735,7 +3614,11 @@ function evaluateTaskGate(
|
|
|
3735
3614
|
if (semanticEnforcement && acceptanceConflicts.length > 0) {
|
|
3736
3615
|
missing.push(`acceptance semantic conflicts (${acceptanceConflicts.join(", ")})`);
|
|
3737
3616
|
}
|
|
3738
|
-
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
|
+
}
|
|
3739
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.");
|
|
3740
3623
|
if (normalMissing.length > 0) {
|
|
3741
3624
|
warnings.push(`Acceptance criteria pending evidence: ${normalMissing.map((criterion) => `${criterion.id}:${criterion.obligation}`).join(", ")}`);
|
|
@@ -3915,6 +3798,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
3915
3798
|
const runtimeState = new RuntimeSessionState({
|
|
3916
3799
|
maxObservedContext: contextBudgetConfig(policy).maxManifestFiles
|
|
3917
3800
|
});
|
|
3801
|
+
const editFreshnessGuard = new EditFreshnessGuard(editFreshnessModeFromEnvironment(process.env.PIAGENT_EDIT_FRESHNESS));
|
|
3918
3802
|
const sourceMutationGuardBindings = createSourceMutationGuardBindings(policy, loadProfileFromContext);
|
|
3919
3803
|
const runtimeSnapshotCapture = new RuntimeSnapshotCapture(), runtimeVersions = readRuntimeVersionMetadata(PLATFORM_ROOT);
|
|
3920
3804
|
const solverShadow = solverMode === "off" ? undefined : new SolverShadowRuntime(solverMode);
|
|
@@ -4137,8 +4021,11 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4137
4021
|
failed?.exitCode ?? 1
|
|
4138
4022
|
);
|
|
4139
4023
|
const trajectory = trajectoryRuntime.status(ctx.cwd, task.taskRunId);
|
|
4140
|
-
|
|
4141
|
-
|
|
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({
|
|
4142
4029
|
featureEnabled: autoRecoveryEnabled && taskAuthorityDecision(task, "CAP-12", "model-turn").allowed,
|
|
4143
4030
|
task: {
|
|
4144
4031
|
taskId: task.taskId,
|
|
@@ -4157,6 +4044,9 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4157
4044
|
: true,
|
|
4158
4045
|
dependencyMutationAuthorized
|
|
4159
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;
|
|
4160
4050
|
}
|
|
4161
4051
|
|
|
4162
4052
|
function trajectoryRecoveryOptions(ctx: ExtensionContext, task: TaskContract, options: any): any {
|
|
@@ -4194,6 +4084,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4194
4084
|
resolveTaskAny: (cwd, reference) => resolveTaskContract(cwd, reference, undefined) as TaskContract | undefined,
|
|
4195
4085
|
bindTask: bindSessionTask,
|
|
4196
4086
|
writeTask,
|
|
4087
|
+
appendTrace,
|
|
4197
4088
|
capabilityState: (ctx) => verifyProjectCapabilityState(extensionDir, ctx.cwd, ctx.isProjectTrusted(), { allowRepin: true, forceFull: true, sessionId: ctx.sessionManager.getSessionId() }),
|
|
4198
4089
|
permissionProfile: (ctx, profile) => resolvePermissionProfile(profile, policy, permissionOverrideFromContext(ctx)),
|
|
4199
4090
|
legacyProjectWarning: legacyProjectStateWarning,
|
|
@@ -4237,6 +4128,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4237
4128
|
onAgentSettled: activityInspector.refresh,
|
|
4238
4129
|
beforeShutdown: (ctx) => {
|
|
4239
4130
|
sourceMutationGuardBindings.unbind(ctx);
|
|
4131
|
+
editFreshnessGuard.clear(ctx);
|
|
4240
4132
|
activityInspector.dispose(ctx);
|
|
4241
4133
|
sessionCapabilityDigests.delete(`${ctx.cwd}\0${ctx.sessionManager.getSessionId()}`);
|
|
4242
4134
|
}
|
|
@@ -4334,6 +4226,36 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4334
4226
|
telemetry,
|
|
4335
4227
|
activity: recordActivity,
|
|
4336
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
|
+
},
|
|
4337
4259
|
completeSemanticRepair: (ctx, event, metadata) => {
|
|
4338
4260
|
const task = activeSessionTask(ctx.cwd, ctx.sessionManager.getSessionId()) as TaskContract | undefined;
|
|
4339
4261
|
return task && (taskAuthorityDecision(task, "CAP-13", "mutate").allowed
|
|
@@ -4354,10 +4276,42 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4354
4276
|
activity: recordActivity,
|
|
4355
4277
|
beforeAuthorize: (event, ctx) => {
|
|
4356
4278
|
const toolInput = isPlainRecord(event.input) ? event.input : {};
|
|
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
|
+
}
|
|
4357
4305
|
const targetInspection = authorizationMutationTargetInspection(ctx.cwd, event.toolName, toolInput);
|
|
4358
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
|
+
}
|
|
4359
4314
|
if (!["edit", "write"].includes(event.toolName) && !targetInspection.exactLocalPatchExecutor) return;
|
|
4360
|
-
const task = activeSessionTask(ctx.cwd, ctx.sessionManager.getSessionId()) as TaskContract | undefined;
|
|
4361
4315
|
if (!task || task.trace.outcome !== "pending" || task.changeMode !== "source-change") return;
|
|
4362
4316
|
const semanticRepairEnabled = taskAuthorityDecision(task, "CAP-13", "mutate").allowed;
|
|
4363
4317
|
const semanticRepairExactlyOff = !taskAuthorityDecision(task, "CAP-13", "observe").allowed;
|
|
@@ -4374,11 +4328,10 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4374
4328
|
const currentDigest = workingTreeEvidenceDigest(currentSnapshot);
|
|
4375
4329
|
const expectedReviewPaths = taskDeltaFilesFromSnapshot(task, currentSnapshot);
|
|
4376
4330
|
const targets = targetInspection.targets;
|
|
4377
|
-
const
|
|
4378
|
-
&& targets.every((file) => taskScopeIncludesPath(task.scope, file));
|
|
4331
|
+
const boundedTargetMutation = targets.length > 0;
|
|
4379
4332
|
const verifierCurrent = allVerifyCommandsPassCurrentTree(task, currentDigest);
|
|
4380
4333
|
|
|
4381
|
-
if (phase.phase === "verify" &&
|
|
4334
|
+
if (phase.phase === "verify" && boundedTargetMutation && semanticRepairEnabled && semanticRepairRuntime.prepare({
|
|
4382
4335
|
ctx, task, event, currentDigest, currentDeltaPaths: expectedReviewPaths, targetPaths: targets,
|
|
4383
4336
|
verifierCurrent
|
|
4384
4337
|
})) return;
|
|
@@ -4388,7 +4341,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4388
4341
|
// model catches its own omission in the same task/run/session. The call
|
|
4389
4342
|
// still traverses every normal authorization check; verify -> repair is
|
|
4390
4343
|
// committed only by the tool-result hook after a successful tree change.
|
|
4391
|
-
if (phase.phase === "verify" && boundedRecoveryEnabled && verifierCurrent &&
|
|
4344
|
+
if (phase.phase === "verify" && boundedRecoveryEnabled && verifierCurrent && boundedTargetMutation) {
|
|
4392
4345
|
const authoredFileDigests = runtimeState.successfulModelMutationDigests(
|
|
4393
4346
|
{ taskId: task.taskId, taskRunId: task.taskRunId, sessionId: task.sessionId },
|
|
4394
4347
|
currentSnapshot
|
|
@@ -4436,7 +4389,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4436
4389
|
return reviewDecision;
|
|
4437
4390
|
}
|
|
4438
4391
|
|
|
4439
|
-
if (targets.length === 0
|
|
4392
|
+
if (targets.length === 0) return;
|
|
4440
4393
|
const result = trajectoryRuntime.sync(ctx.cwd, ctx.sessionManager.getSessionId(), task, {
|
|
4441
4394
|
sourceHook: "tool-call",
|
|
4442
4395
|
recoveryRequested: true,
|
|
@@ -4460,11 +4413,9 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4460
4413
|
const targetInspection = authorizationMutationTargetInspection(ctx.cwd, event.toolName, toolInput);
|
|
4461
4414
|
if (targetInspection.reason) return { block: true, reason: `Blocked ${event.toolName}: ${targetInspection.reason}.` };
|
|
4462
4415
|
const targets = targetInspection.targets;
|
|
4463
|
-
if ((["edit", "write"].includes(event.toolName) || targetInspection.patchCarrier) &&
|
|
4464
|
-
targets.length === 0 || targets.some((file) => !taskScopeIncludesPath(task.scope, file))
|
|
4465
|
-
)) {
|
|
4416
|
+
if ((["edit", "write"].includes(event.toolName) || targetInspection.patchCarrier) && targets.length === 0) {
|
|
4466
4417
|
runtimeState.denyPerformanceReviewTool(task.taskRunId);
|
|
4467
|
-
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.` };
|
|
4468
4419
|
}
|
|
4469
4420
|
const reviewDecision = performanceReviewToolDecision({
|
|
4470
4421
|
toolName: event.toolName,
|
|
@@ -4492,6 +4443,30 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4492
4443
|
},
|
|
4493
4444
|
afterAuthorized: (event, ctx, metadata) => {
|
|
4494
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
|
+
}
|
|
4495
4470
|
if (task?.trace.outcome === "pending") {
|
|
4496
4471
|
const snapshot = workingTreeSnapshot(ctx.cwd) as Record<string, string>;
|
|
4497
4472
|
const currentDigest = workingTreeEvidenceDigest(snapshot);
|
|
@@ -4639,6 +4614,12 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4639
4614
|
const execDecision = evaluateExecPolicy(command, profile, policy);
|
|
4640
4615
|
authorizedShellCommand = command;
|
|
4641
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
|
+
}
|
|
4642
4623
|
shellProjectMutation = isProjectMutatingShellCommand(command, execDecision.segments);
|
|
4643
4624
|
configuredVerifierShell = Boolean(sessionTask && commandMatchesVerifyPlan(command, sessionTask.verifyCommands));
|
|
4644
4625
|
const shellWriteCandidates = extractShellWritePathCandidates(command);
|
|
@@ -4730,7 +4711,8 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4730
4711
|
{
|
|
4731
4712
|
forceScopeAware: authorizationTargetInspection.patchCarrier || Boolean(preparedInput.proxyToolName),
|
|
4732
4713
|
forceWrite: authorizationTargetInspection.patchCarrier || preparedInput.proxyAction?.decision === "confirm",
|
|
4733
|
-
allowAmbiguousFilesystemContentFields: !usesKnownExternalProvider && !isPiagentTool(event.toolName)
|
|
4714
|
+
allowAmbiguousFilesystemContentFields: !usesKnownExternalProvider && !isPiagentTool(event.toolName),
|
|
4715
|
+
sourceCheckoutReadRoots: runtimeState.sourceCheckoutReadRoots(ctx)
|
|
4734
4716
|
}
|
|
4735
4717
|
);
|
|
4736
4718
|
if (pathDecision.block) {
|
|
@@ -4816,32 +4798,18 @@ export default function piagentGuard(pi: ExtensionAPI) {
|
|
|
4816
4798
|
+ " Call `piagent_task_start` once with explicit project-relative scope, then retry."
|
|
4817
4799
|
};
|
|
4818
4800
|
}
|
|
4819
|
-
if (
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
}
|
|
4830
|
-
}
|
|
4831
|
-
const outsideScope = mutationTargets.filter((file) => !taskScopeIncludesPath(sessionTask.scope, file));
|
|
4832
|
-
if (outsideScope.length > 0) {
|
|
4833
|
-
return {
|
|
4834
|
-
block: true,
|
|
4835
|
-
reason: `Task ${sessionTask.taskId} cannot mutate paths outside its declared scope: ${outsideScope.join(", ")}.`
|
|
4836
|
-
};
|
|
4837
|
-
}
|
|
4838
|
-
const outsideEvidenceRoot = mutationTargets.filter((file) => !pathWithinChangeEvidenceRoot(ctx.cwd, file));
|
|
4839
|
-
if (outsideEvidenceRoot.length > 0) {
|
|
4840
|
-
return {
|
|
4841
|
-
block: true,
|
|
4842
|
-
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/**.`
|
|
4843
|
-
};
|
|
4844
|
-
}
|
|
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
|
+
};
|
|
4845
4813
|
}
|
|
4846
4814
|
if (runtime.contextBudget !== "off" && (["write", "edit"].includes(event.toolName) || authorizationTargetInspection.patchCarrier)) {
|
|
4847
4815
|
const budgetTargets = authorizationTargetInspection.patchCarrier
|