@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
|
@@ -195,3 +195,35 @@ export function applyRuntimeLifecycleObservation(task, observation, recordedAt =
|
|
|
195
195
|
}
|
|
196
196
|
return { changed, mode };
|
|
197
197
|
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* A default assisted read-only review is a presentation checkpoint, not a
|
|
201
|
+
* second reasoning pass. Once runtime-owned read evidence exists and the model
|
|
202
|
+
* emits its final handoff, completing this one exact checkpoint preserves the
|
|
203
|
+
* substantive answer instead of forcing a token-heavy retry. Custom/manual
|
|
204
|
+
* plans and every source-change review remain explicit.
|
|
205
|
+
*/
|
|
206
|
+
export function applyAssistedReadOnlyFinalHandoff(
|
|
207
|
+
task,
|
|
208
|
+
{ durableReadEvidence = false, finalHandoffObserved = false } = {},
|
|
209
|
+
recordedAt = new Date().toISOString()
|
|
210
|
+
) {
|
|
211
|
+
const mode = runtimeLifecycleMode(task);
|
|
212
|
+
if (mode !== "assisted-readonly" || !durableReadEvidence || !finalHandoffObserved) {
|
|
213
|
+
return { changed: false, mode };
|
|
214
|
+
}
|
|
215
|
+
const scout = task.workPlan.find((step) => step.id === "scout");
|
|
216
|
+
const review = task.workPlan.find((step) => step.id === "review");
|
|
217
|
+
if (scout?.status !== "done" || review?.status !== "in-progress") {
|
|
218
|
+
return { changed: false, mode };
|
|
219
|
+
}
|
|
220
|
+
return {
|
|
221
|
+
changed: setStep(
|
|
222
|
+
review,
|
|
223
|
+
"done",
|
|
224
|
+
"Runtime observed a final handoff grounded in durable read-only evidence.",
|
|
225
|
+
recordedAt
|
|
226
|
+
),
|
|
227
|
+
mode
|
|
228
|
+
};
|
|
229
|
+
}
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
} from "./task-digest-state.js";
|
|
21
21
|
export { taskDigestMigrationArchiveStatus } from "./task-digest-state.js";
|
|
22
22
|
import { acceptanceReceiptWithoutWorkingTreeProof, normalizedTaskDigestFields, taskDigestContractValidationErrors } from "./task-digest-contract.js";
|
|
23
|
+
import { unicodeCodePointLength } from "./verification-intelligence.js";
|
|
23
24
|
import {
|
|
24
25
|
WORKING_TREE_DIGEST_ALGORITHM,
|
|
25
26
|
isCurrentWorkingTreeDigest,
|
|
@@ -42,11 +43,16 @@ import {
|
|
|
42
43
|
export { isGitWorkingTree } from "./workspace-evidence-roots.js";
|
|
43
44
|
|
|
44
45
|
export const TASK_CONTRACT_SCHEMA_VERSION = 2; export const DEFAULT_MAX_TASK_ATTEMPTS = 3;
|
|
46
|
+
export const OPERATOR_REQUEST_MAX_CHARS = 8_000;
|
|
47
|
+
export const TASK_SUMMARY_MAX_CHARS = 2_000;
|
|
48
|
+
export const TASK_EXPECTED_OUTPUT_MAX_CHARS = 2_000;
|
|
49
|
+
export const TASK_ACCEPTANCE_CRITERION_MAX_CHARS = 1_000;
|
|
50
|
+
export const TASK_ACCEPTANCE_CRITERIA_MAX = 12;
|
|
45
51
|
const TASK_OUTCOMES = ["pending", "completed", "blocked", "partial", "failed"], TERMINAL_TASK_OUTCOMES = new Set(["completed", "blocked", "partial", "failed"]);
|
|
46
52
|
const REVIEW_LENSES = ["correctness", "tests", "scope", "security", "docs", "release", "package"];
|
|
47
53
|
const TASK_CONTRACT_FIELDS = new Set([
|
|
48
54
|
"schemaVersion", "taskRunId", "taskId", "sessionId", "sessionName", "changeMode", "mutationPolicy", "attempt", "maxAttempts",
|
|
49
|
-
"previousAttempts", "summary", "riskLane", "intakeMode", "expectedOutput", "acceptanceCriteria", "criterionGraph", "scope", "outOfScope",
|
|
55
|
+
"previousAttempts", "summary", "operatorRequest", "operatorRequestDigest", "riskLane", "intakeMode", "expectedOutput", "acceptanceCriteria", "criterionGraph", "scope", "outOfScope",
|
|
50
56
|
"protectedPaths", "requiredContext", "contextManifest", "memoryCitations", "mcpCapabilities", "verifyGroup",
|
|
51
57
|
"verifyCommands", "workPlan", "reviewLenses", "orchestration", "acceptanceReceipt", "authoritySnapshot", "workingTreeDigestAlgorithm", "workingTreeDigestMigration", "baselineChangedFiles", "baselineFileDigests",
|
|
52
58
|
"observedChangedFiles", "finalWorkingTreeFiles", "finalFileDigests", "changedFiles", "verifyEvidence", "trace",
|
|
@@ -136,6 +142,10 @@ function stringArray(value) {
|
|
|
136
142
|
return Array.isArray(value) ? [...new Set(value.filter((item) => typeof item === "string" && item.trim()).map((item) => item.trim()))] : [];
|
|
137
143
|
}
|
|
138
144
|
|
|
145
|
+
function exactCommandArray(value) {
|
|
146
|
+
return Array.isArray(value) ? [...new Set(value.filter((item) => typeof item === "string" && item.trim()))] : [];
|
|
147
|
+
}
|
|
148
|
+
|
|
139
149
|
function stringRecord(value) {
|
|
140
150
|
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
141
151
|
return Object.fromEntries(Object.entries(value)
|
|
@@ -151,6 +161,10 @@ function validTimestamp(value) {
|
|
|
151
161
|
return typeof value === "string" && value.trim().length > 0 && Number.isFinite(Date.parse(value));
|
|
152
162
|
}
|
|
153
163
|
|
|
164
|
+
export function operatorRequestDigest(value) {
|
|
165
|
+
return `operator-request-v1:${crypto.createHash("sha256").update(String(value ?? "")).digest("hex")}`;
|
|
166
|
+
}
|
|
167
|
+
|
|
154
168
|
function normalizedTimestamp(value, fallback) {
|
|
155
169
|
return validTimestamp(value) ? value : fallback;
|
|
156
170
|
}
|
|
@@ -191,6 +205,16 @@ export function taskContractValidationErrors(input) {
|
|
|
191
205
|
if (typeof input.taskId === "string" && safeTaskId(input.taskId) !== input.taskId) errors.push("taskId must be normalized");
|
|
192
206
|
if (typeof input.summary === "string" && input.summary.trim().length < 10) errors.push("summary must be at least 10 characters");
|
|
193
207
|
if (typeof input.expectedOutput === "string" && input.expectedOutput.trim().length < 10) errors.push("expectedOutput must be at least 10 characters");
|
|
208
|
+
const hasOperatorRequest = input.operatorRequest !== undefined;
|
|
209
|
+
const hasOperatorRequestDigest = input.operatorRequestDigest !== undefined;
|
|
210
|
+
if (hasOperatorRequest !== hasOperatorRequestDigest) errors.push("operatorRequest and operatorRequestDigest must be present together");
|
|
211
|
+
if (hasOperatorRequest) {
|
|
212
|
+
if (typeof input.operatorRequest !== "string" || !input.operatorRequest.trim()) errors.push("operatorRequest must be a non-empty string");
|
|
213
|
+
else {
|
|
214
|
+
if (unicodeCodePointLength(input.operatorRequest) > OPERATOR_REQUEST_MAX_CHARS) errors.push(`operatorRequest must contain at most ${OPERATOR_REQUEST_MAX_CHARS} Unicode characters`);
|
|
215
|
+
if (input.operatorRequestDigest !== operatorRequestDigest(input.operatorRequest)) errors.push("operatorRequestDigest does not match operatorRequest");
|
|
216
|
+
}
|
|
217
|
+
}
|
|
194
218
|
for (const field of ["createdAt", "updatedAt"]) {
|
|
195
219
|
if (typeof input[field] === "string" && !validTimestamp(input[field])) errors.push(`${field} must be a valid timestamp`);
|
|
196
220
|
}
|
|
@@ -402,6 +426,8 @@ export function normalizeTaskContract(input, options = {}) {
|
|
|
402
426
|
maxAttempts: positiveInteger(input.maxAttempts, DEFAULT_MAX_TASK_ATTEMPTS, 10),
|
|
403
427
|
previousAttempts: Array.isArray(input.previousAttempts) ? input.previousAttempts.slice(-10).map((item) => pickFields(item, PREVIOUS_ATTEMPT_FIELDS)) : [],
|
|
404
428
|
summary: typeof input.summary === "string" ? input.summary : "",
|
|
429
|
+
operatorRequest: typeof input.operatorRequest === "string" && input.operatorRequest.trim() ? input.operatorRequest : undefined,
|
|
430
|
+
operatorRequestDigest: typeof input.operatorRequestDigest === "string" && input.operatorRequestDigest.trim() ? input.operatorRequestDigest : undefined,
|
|
405
431
|
riskLane: ["tiny", "normal", "high-risk"].includes(input.riskLane) ? input.riskLane : "normal",
|
|
406
432
|
intakeMode: ["model", "runtime"].includes(input.intakeMode) ? input.intakeMode : "model",
|
|
407
433
|
expectedOutput: typeof input.expectedOutput === "string" ? input.expectedOutput : "", acceptanceCriteria: stringArray(input.acceptanceCriteria),
|
|
@@ -413,7 +439,7 @@ export function normalizeTaskContract(input, options = {}) {
|
|
|
413
439
|
memoryCitations: Array.isArray(input.memoryCitations) ? input.memoryCitations.map((item) => pickFields(item, CITATION_FIELDS)) : [],
|
|
414
440
|
mcpCapabilities: stringArray(input.mcpCapabilities),
|
|
415
441
|
verifyGroup: typeof input.verifyGroup === "string" && input.verifyGroup.trim() ? input.verifyGroup.trim() : undefined,
|
|
416
|
-
verifyCommands:
|
|
442
|
+
verifyCommands: exactCommandArray(input.verifyCommands),
|
|
417
443
|
workPlan: Array.isArray(input.workPlan) ? input.workPlan.map((item) => pickFields(item, WORK_PLAN_FIELDS)) : [],
|
|
418
444
|
reviewLenses: stringArray(input.reviewLenses),
|
|
419
445
|
acceptanceReceipt: normalizeAcceptanceReceipt(legacyDigestContract
|
|
@@ -691,6 +717,10 @@ function changedPathsFromNameStatus(output, observeRename) {
|
|
|
691
717
|
}
|
|
692
718
|
|
|
693
719
|
const WORKING_TREE_STREAM_CHUNK_BYTES = 1024 * 1024;
|
|
720
|
+
// This path cannot collide with enumerated project evidence because `.pi` is
|
|
721
|
+
// intentionally excluded from workspace discovery. Its unavailable digest
|
|
722
|
+
// turns a partial inventory into an explicitly non-proof-capable snapshot.
|
|
723
|
+
const WORKING_TREE_ENUMERATION_EVIDENCE_PATH = ".pi/piagent-state/.working-tree-enumeration-unavailable";
|
|
694
724
|
function unavailableWorkingTreeDigest(file, reason, stat) {
|
|
695
725
|
return unavailableWorkingTreeHash(crypto.createHash("sha256")
|
|
696
726
|
.update(`${file}\0${reason}\0${stat?.mode ?? ""}\0${stat?.size ?? ""}\0${stat?.mtimeMs ?? ""}\0${stat?.ctimeMs ?? ""}`)
|
|
@@ -770,7 +800,18 @@ function streamedWorkingTreeFileDigest(root, file, hasHead, protectedProjectPath
|
|
|
770
800
|
export function workingTreeSnapshot(cwd, options = {}) {
|
|
771
801
|
const snapshot = {};
|
|
772
802
|
const isProtectedProjectPath = typeof options.isProtectedProjectPath === "function" ? options.isProtectedProjectPath : () => false;
|
|
773
|
-
|
|
803
|
+
let rootDetails;
|
|
804
|
+
try {
|
|
805
|
+
rootDetails = gitEvidenceRootDetails(cwd);
|
|
806
|
+
} catch (error) {
|
|
807
|
+
snapshot[WORKING_TREE_ENUMERATION_EVIDENCE_PATH] = unavailableWorkingTreeDigest(
|
|
808
|
+
WORKING_TREE_ENUMERATION_EVIDENCE_PATH,
|
|
809
|
+
`root-enumeration:${error?.code ?? "failed"}`
|
|
810
|
+
);
|
|
811
|
+
return snapshot;
|
|
812
|
+
}
|
|
813
|
+
const roots = rootDetails.roots;
|
|
814
|
+
const incompleteReasons = rootDetails.complete ? [] : ["root-enumeration-incomplete"];
|
|
774
815
|
for (const root of roots) {
|
|
775
816
|
const protectedAliases = new Set();
|
|
776
817
|
const observeRename = (source, destination) => {
|
|
@@ -778,13 +819,33 @@ export function workingTreeSnapshot(cwd, options = {}) {
|
|
|
778
819
|
if (paths.some((file) => file === ".pi/piagent-state" || file.startsWith(".pi/piagent-state/") || isProtectedProjectPath(prefixedGitPath(root.prefix, file))))
|
|
779
820
|
paths.forEach((file) => protectedAliases.add(file));
|
|
780
821
|
};
|
|
781
|
-
|
|
782
|
-
const
|
|
783
|
-
|
|
822
|
+
try {
|
|
823
|
+
const hasHead = gitHasHead(root.cwd); for (const file of workingTreeFilesForGitRoot(root.cwd, observeRename)) {
|
|
824
|
+
const projectPath = prefixedGitPath(root.prefix, file);
|
|
825
|
+
snapshot[projectPath] = streamedWorkingTreeFileDigest(root.cwd, file, hasHead, isProtectedProjectPath(projectPath) || protectedAliases.has(file) ? projectPath : null);
|
|
826
|
+
}
|
|
827
|
+
} catch {
|
|
828
|
+
incompleteReasons.push(`git-root-enumeration-incomplete:${root.prefix || "."}`);
|
|
784
829
|
}
|
|
785
|
-
}
|
|
830
|
+
}
|
|
831
|
+
if (!roots.some((root) => root.prefix === "") && roots.length > 0) {
|
|
832
|
+
const looseDetails = nonGitWorkspaceFileDetails(
|
|
833
|
+
cwd,
|
|
834
|
+
roots.map((root) => root.prefix),
|
|
835
|
+
options.maxNonGitWorkspaceFiles
|
|
836
|
+
);
|
|
837
|
+
for (const file of looseDetails.files) {
|
|
786
838
|
snapshot[file] = nonGitWorkspaceFileDigest(cwd, file, isProtectedProjectPath(file) ? file : null);
|
|
787
|
-
|
|
839
|
+
}
|
|
840
|
+
if (!looseDetails.complete) incompleteReasons.push("non-git-enumeration-incomplete");
|
|
841
|
+
}
|
|
842
|
+
if (incompleteReasons.length > 0) {
|
|
843
|
+
snapshot[WORKING_TREE_ENUMERATION_EVIDENCE_PATH] = unavailableWorkingTreeDigest(
|
|
844
|
+
WORKING_TREE_ENUMERATION_EVIDENCE_PATH,
|
|
845
|
+
incompleteReasons.sort().join(",")
|
|
846
|
+
);
|
|
847
|
+
}
|
|
848
|
+
return snapshot;
|
|
788
849
|
}
|
|
789
850
|
|
|
790
851
|
export function taskStateMigrationStatus(cwd) {
|
|
@@ -13,8 +13,22 @@ import {
|
|
|
13
13
|
} from "./failure-types.ts";
|
|
14
14
|
import { isCurrentWorkingTreeDigest } from "./working-tree-digest.js";
|
|
15
15
|
|
|
16
|
+
export const MAX_VERIFY_COMMANDS = 2;
|
|
17
|
+
export const MAX_VERIFY_COMMAND_CHARS = 900;
|
|
18
|
+
export const MAX_VERIFY_COMMAND_BYTES = 900;
|
|
19
|
+
export const MAX_VERIFY_COMMAND_TOTAL_BYTES = MAX_VERIFY_COMMANDS * MAX_VERIFY_COMMAND_BYTES;
|
|
20
|
+
export const MAX_VERIFY_COMMAND_TOTAL_CHARS = MAX_VERIFY_COMMANDS * MAX_VERIFY_COMMAND_CHARS;
|
|
21
|
+
|
|
22
|
+
export function unicodeCodePointLength(value) {
|
|
23
|
+
return Array.from(String(value ?? "")).length;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function utf8ByteLength(value) {
|
|
27
|
+
return Buffer.byteLength(String(value ?? ""), "utf8");
|
|
28
|
+
}
|
|
29
|
+
|
|
16
30
|
function uniqueStrings(values) {
|
|
17
|
-
return [...new Set((Array.isArray(values) ? values : []).filter((value) => typeof value === "string" && value.trim())
|
|
31
|
+
return [...new Set((Array.isArray(values) ? values : []).filter((value) => typeof value === "string" && value.trim()))];
|
|
18
32
|
}
|
|
19
33
|
|
|
20
34
|
export function meaningfulVerificationCommands(commands = []) {
|
|
@@ -27,6 +41,25 @@ export function meaningfulVerificationCommands(commands = []) {
|
|
|
27
41
|
});
|
|
28
42
|
}
|
|
29
43
|
|
|
44
|
+
export function verifierCommandBoundsError(commands = []) {
|
|
45
|
+
const supplied = (Array.isArray(commands) ? commands : []).filter((command) => typeof command === "string");
|
|
46
|
+
if (supplied.some((command) => /[\r\n]/.test(command))) {
|
|
47
|
+
return "Exact verify commands must be single-line values without CR or LF characters; move multiline logic into a checked-in script before task intake.";
|
|
48
|
+
}
|
|
49
|
+
const normalized = uniqueStrings(supplied);
|
|
50
|
+
if (normalized.length > MAX_VERIFY_COMMANDS) {
|
|
51
|
+
return `Verify plans support at most ${MAX_VERIFY_COMMANDS} exact commands; consolidate or shorten the project verifier before task intake.`;
|
|
52
|
+
}
|
|
53
|
+
const oversized = normalized.find((command) => unicodeCodePointLength(command) > MAX_VERIFY_COMMAND_CHARS);
|
|
54
|
+
if (oversized) return `An exact verify command exceeds ${MAX_VERIFY_COMMAND_CHARS} characters; shorten it or move verifier logic into a checked-in script before task intake.`;
|
|
55
|
+
const oversizedTraffic = normalized.find((command) => utf8ByteLength(command) > MAX_VERIFY_COMMAND_BYTES);
|
|
56
|
+
if (oversizedTraffic) return `An exact verify command exceeds ${MAX_VERIFY_COMMAND_BYTES} UTF-8 bytes; shorten it or move verifier logic into a checked-in script before task intake.`;
|
|
57
|
+
if (normalized.reduce((total, command) => total + utf8ByteLength(command), 0) > MAX_VERIFY_COMMAND_TOTAL_BYTES) {
|
|
58
|
+
return `Exact verify commands exceed the ${MAX_VERIFY_COMMAND_TOTAL_BYTES}-byte aggregate limit; consolidate or shorten the project verifier before task intake.`;
|
|
59
|
+
}
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
30
63
|
/** Return the last durably recorded observed execution for each exact command. */
|
|
31
64
|
export function latestObservedVerificationEvidence(evidence = []) {
|
|
32
65
|
const latest = new Map();
|
|
@@ -100,7 +133,10 @@ function parsedSignals(text, exitCode, structuredEvents) {
|
|
|
100
133
|
const lower = text.toLowerCase();
|
|
101
134
|
const signals = structuredEvents.map((event) => STRUCTURED_SIGNALS[event]).filter(Boolean);
|
|
102
135
|
const add = (signal, pattern) => { if (pattern.test(lower)) signals.push(signal); };
|
|
103
|
-
|
|
136
|
+
// Task scope is advisory. Only concrete filesystem-safety wording may be
|
|
137
|
+
// inferred from unstructured output; scope policy must arrive as a trusted
|
|
138
|
+
// structured event so ordinary assertion text cannot stop recovery.
|
|
139
|
+
add("scope-diagnostic", /\b(protected path|symlink escape)\b/);
|
|
104
140
|
add("policy-diagnostic", /\b(policy (?:blocked|denied|violation)|capability lock|guard blocked)\b/);
|
|
105
141
|
add("permission-diagnostic", /\b(eacces|eperm|permission denied|not authorized|unauthorized|forbidden)\b/);
|
|
106
142
|
add("provider-rate-limit", /\b(?:http\s*)?429\b|\brate[ -]?limit(?:ed|ing)?\b/);
|
|
@@ -191,7 +227,7 @@ const RECORDED_CATEGORY_HINTS = Object.freeze({
|
|
|
191
227
|
environment: "command not found in runtime environment",
|
|
192
228
|
"provider-network": "provider API request timed out",
|
|
193
229
|
"permission-policy": "permission denied by policy",
|
|
194
|
-
"scope-protected-path": "
|
|
230
|
+
"scope-protected-path": "protected path is forbidden",
|
|
195
231
|
"flaky-infrastructure": "EADDRINUSE: port is already in use"
|
|
196
232
|
});
|
|
197
233
|
|
|
@@ -202,16 +238,15 @@ export function classifyRecordedVerificationFailure(summary, exitCode = 1) {
|
|
|
202
238
|
return classifyVerificationFailure(hint ? `${text}\n${hint}` : text, exitCode);
|
|
203
239
|
}
|
|
204
240
|
|
|
205
|
-
const
|
|
241
|
+
const COMPLETION_POLICY_BOUNDARY = /(?:^read-only task has observed changes\b|^mutation-forbidden task has observed changes\b|\bprotected(?:\/read-only| or read-only)? paths?\b|\bread-only path\b)/i;
|
|
206
242
|
|
|
207
243
|
export function classifyCompletionGateFailure(missing = [], summary = "", exitCode = 1) {
|
|
208
244
|
const missingItems = Array.isArray(missing) ? missing.map((item) => String(item)) : [];
|
|
209
|
-
//
|
|
210
|
-
// completion failures.
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
structuredEvents: ["scope-violation"]
|
|
245
|
+
// Protected-path and read-only mutation boundaries are structural
|
|
246
|
+
// completion failures. Task scope is advisory and never enters this path.
|
|
247
|
+
if (missingItems.some((item) => COMPLETION_POLICY_BOUNDARY.test(item))) {
|
|
248
|
+
return classifyVerificationFailure("Completion gate rejected a protected-path or read-only boundary.", 1, {
|
|
249
|
+
structuredEvents: ["protected-path"]
|
|
215
250
|
});
|
|
216
251
|
}
|
|
217
252
|
if (missingItems.some((item) => /^critical acceptance evidence\b/i.test(item))) {
|
|
@@ -235,8 +270,8 @@ export function chooseVerificationScope(profileVerifyCommands = {}, changedFiles
|
|
|
235
270
|
|| /^(?:README|CHANGELOG|CONTRIBUTING|SECURITY)(?:\.|$)/i.test(file);
|
|
236
271
|
const hasDocs = files.some(isDocumentationFile);
|
|
237
272
|
const hasDocsOnly = files.length > 0 && files.every(isDocumentationFile);
|
|
238
|
-
const hasFrontend = files.some((file) => /(
|
|
239
|
-
const hasBackend = files.some((file) => /(
|
|
273
|
+
const hasFrontend = files.some((file) => /(^|[\/_-])(?:app|pages|components|frontend|web|client)(?=[\/_-]|$)|\.(?:tsx|jsx|css|scss|vue|svelte)$/i.test(file));
|
|
274
|
+
const hasBackend = files.some((file) => /(^|[\/_-])(?:api|server|backend|services)(?=[\/_-]|$)|\.(?:go|py|rs|java|kt|cs|rb|php|sql)$/i.test(file));
|
|
240
275
|
const selectedGroups = [];
|
|
241
276
|
if (hasDocsOnly && groups.docs) {
|
|
242
277
|
selectedGroups.push("docs");
|
|
@@ -310,5 +345,7 @@ export function selectVerificationPlan(profile, requestedGroup, changeMode, cwd,
|
|
|
310
345
|
error: `Verify group ${group ?? "(none)"} has no meaningful command. Configure .pi/piagent-profile.json before source changes.`
|
|
311
346
|
};
|
|
312
347
|
}
|
|
348
|
+
const boundsError = verifierCommandBoundsError(commands);
|
|
349
|
+
if (boundsError) return { group, commands, error: boundsError };
|
|
313
350
|
return { group, commands };
|
|
314
351
|
}
|
|
@@ -2,14 +2,35 @@ import { execFileSync } from "node:child_process";
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
// Parent workspaces commonly carry shared plans, verifier scripts, and agent
|
|
6
|
+
// configuration beside their child repositories. Keep the traversal bounded,
|
|
7
|
+
// but leave enough room for a real multi-repository workspace instead of
|
|
8
|
+
// silently dropping evidence at the former 2,000-file ceiling.
|
|
9
|
+
const WORKSPACE_EVIDENCE_MAX_FILES = 20_000;
|
|
10
|
+
// These names are not merely noisy build output: they carry repository
|
|
11
|
+
// ownership, Piagent authority, or provider credentials. Evidence discovery
|
|
12
|
+
// excludes them and the mutation guard protects them independently of task
|
|
13
|
+
// focus, so broad monorepo access cannot create an unaudited private-state gap.
|
|
14
|
+
export const WORKSPACE_PRIVATE_STATE_PATTERNS = Object.freeze([
|
|
15
|
+
"**/.git/**", "**/.hg/**", "**/.svn/**", "**/.pi/**",
|
|
16
|
+
"**/.aws/**", "**/.azure/**", "**/.docker/**", "**/.gnupg/**", "**/.kube/**", "**/.ssh/**",
|
|
17
|
+
"**/.git-credentials", "**/.netrc", "**/.npmrc", "**/.pypirc",
|
|
18
|
+
"**/.env", "**/.env.*",
|
|
19
|
+
"**/auth.json", "**/credential.json", "**/credentials.json", "**/secret.json", "**/secrets.json",
|
|
20
|
+
"**/token.json", "**/tokens.json"
|
|
21
|
+
]);
|
|
6
22
|
const WORKSPACE_EVIDENCE_SKIP_NAMES = new Set([
|
|
7
|
-
".git", ".hg", ".svn", ".pi", "node_modules", "dist", "build", ".next", "coverage"
|
|
23
|
+
".git", ".hg", ".svn", ".pi", "node_modules", "dist", "build", ".next", "coverage",
|
|
24
|
+
".cache", ".npm", ".pnpm-store", ".yarn", ".turbo", ".aws", ".azure", ".docker",
|
|
25
|
+
".gnupg", ".kube", ".ssh", ".ds_store", ".git-credentials", ".netrc", ".npmrc", ".pypirc"
|
|
8
26
|
]);
|
|
9
27
|
const WORKSPACE_EVIDENCE_PRIORITY_NAMES = new Map([["plans", 0]]);
|
|
10
28
|
|
|
11
|
-
function
|
|
12
|
-
|
|
29
|
+
function excludedWorkspaceEvidenceName(value) {
|
|
30
|
+
const name = String(value ?? "");
|
|
31
|
+
return WORKSPACE_EVIDENCE_SKIP_NAMES.has(name.toLowerCase())
|
|
32
|
+
|| /^\.env(?:\.|$)/i.test(name)
|
|
33
|
+
|| /^(?:auth|credentials?|secrets?|tokens?)\.json$/i.test(name);
|
|
13
34
|
}
|
|
14
35
|
|
|
15
36
|
export function gitOutput(cwd, args, options = {}) {
|
|
@@ -86,6 +107,15 @@ export function directChildGitEvidenceRoots(cwd) {
|
|
|
86
107
|
|
|
87
108
|
export function gitEvidenceRootDetails(cwd) {
|
|
88
109
|
if (isGitWorkingTree(cwd)) return { roots: [{ cwd, prefix: "" }], complete: true };
|
|
110
|
+
// A present Git marker combined with a failed Git probe is ambiguous (for
|
|
111
|
+
// example a broken worktree pointer or a transient command failure). Do not
|
|
112
|
+
// reinterpret that repository as an ordinary complete non-Git directory.
|
|
113
|
+
try {
|
|
114
|
+
fs.lstatSync(path.join(cwd, ".git"));
|
|
115
|
+
return { roots: [], complete: false };
|
|
116
|
+
} catch (error) {
|
|
117
|
+
if (error?.code !== "ENOENT") return { roots: [], complete: false };
|
|
118
|
+
}
|
|
89
119
|
return directChildGitEvidenceRootDetails(cwd);
|
|
90
120
|
}
|
|
91
121
|
|
|
@@ -93,9 +123,16 @@ export function gitEvidenceRoots(cwd) {
|
|
|
93
123
|
return gitEvidenceRootDetails(cwd).roots;
|
|
94
124
|
}
|
|
95
125
|
|
|
96
|
-
|
|
126
|
+
function workspaceEvidenceFileLimit(value) {
|
|
127
|
+
return Number.isInteger(value)
|
|
128
|
+
? Math.max(1, Math.min(WORKSPACE_EVIDENCE_MAX_FILES, value))
|
|
129
|
+
: WORKSPACE_EVIDENCE_MAX_FILES;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function nonGitWorkspaceFileDetails(cwd, gitRootPrefixes, maxFiles = WORKSPACE_EVIDENCE_MAX_FILES) {
|
|
97
133
|
const files = [];
|
|
98
134
|
let complete = true;
|
|
135
|
+
const limit = workspaceEvidenceFileLimit(maxFiles);
|
|
99
136
|
const gitRoots = new Set(gitRootPrefixes.filter(Boolean));
|
|
100
137
|
const orderedEntries = (entries) => [...entries].sort((left, right) => {
|
|
101
138
|
const leftPriority = WORKSPACE_EVIDENCE_PRIORITY_NAMES.get(left.name.toLowerCase()) ?? 10;
|
|
@@ -103,7 +140,7 @@ export function nonGitWorkspaceFileDetails(cwd, gitRootPrefixes) {
|
|
|
103
140
|
return leftPriority - rightPriority || left.name.localeCompare(right.name, "en-US");
|
|
104
141
|
});
|
|
105
142
|
function visit(directory, relativeDirectory) {
|
|
106
|
-
if (files.length >=
|
|
143
|
+
if (files.length >= limit) {
|
|
107
144
|
complete = false;
|
|
108
145
|
return;
|
|
109
146
|
}
|
|
@@ -115,15 +152,15 @@ export function nonGitWorkspaceFileDetails(cwd, gitRootPrefixes) {
|
|
|
115
152
|
return;
|
|
116
153
|
}
|
|
117
154
|
for (const entry of orderedEntries(entries)) {
|
|
118
|
-
if (files.length >=
|
|
155
|
+
if (files.length >= limit) {
|
|
119
156
|
complete = false;
|
|
120
157
|
return;
|
|
121
158
|
}
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
if (
|
|
159
|
+
// A parent workspace can own real project source in hidden directories
|
|
160
|
+
// (for example shared verifier scripts). Evidence discovery therefore
|
|
161
|
+
// excludes explicit private/cache/credential names instead of treating
|
|
162
|
+
// every dot-prefixed path as non-project state.
|
|
163
|
+
if (excludedWorkspaceEvidenceName(entry.name)) continue;
|
|
127
164
|
const relative = relativeDirectory ? `${relativeDirectory}/${entry.name}` : entry.name;
|
|
128
165
|
if (gitRoots.has(relative.split("/")[0])) continue;
|
|
129
166
|
const absolute = path.join(directory, entry.name);
|
|
@@ -141,8 +178,8 @@ export function nonGitWorkspaceFileDetails(cwd, gitRootPrefixes) {
|
|
|
141
178
|
return { files: files.sort(), complete };
|
|
142
179
|
}
|
|
143
180
|
|
|
144
|
-
export function nonGitWorkspaceFiles(cwd, gitRootPrefixes) {
|
|
145
|
-
return nonGitWorkspaceFileDetails(cwd, gitRootPrefixes).files;
|
|
181
|
+
export function nonGitWorkspaceFiles(cwd, gitRootPrefixes, maxFiles = WORKSPACE_EVIDENCE_MAX_FILES) {
|
|
182
|
+
return nonGitWorkspaceFileDetails(cwd, gitRootPrefixes, maxFiles).files;
|
|
146
183
|
}
|
|
147
184
|
|
|
148
185
|
export function pathWithinNonGitWorkspaceEvidenceRoot(candidate, gitRootPrefixes) {
|
|
@@ -150,8 +187,7 @@ export function pathWithinNonGitWorkspaceEvidenceRoot(candidate, gitRootPrefixes
|
|
|
150
187
|
const topSegment = segments[0];
|
|
151
188
|
return Boolean(
|
|
152
189
|
topSegment
|
|
153
|
-
&& !
|
|
154
|
-
&& !segments.some(hiddenWorkspaceEvidenceSegment)
|
|
190
|
+
&& !segments.some(excludedWorkspaceEvidenceName)
|
|
155
191
|
&& !gitRootPrefixes.includes(topSegment)
|
|
156
192
|
);
|
|
157
193
|
}
|
|
@@ -3,7 +3,27 @@
|
|
|
3
3
|
"name": "piagent-base-policy",
|
|
4
4
|
"protectedPaths": [
|
|
5
5
|
".git/**",
|
|
6
|
+
"**/.git/**",
|
|
7
|
+
"**/.hg/**",
|
|
8
|
+
"**/.svn/**",
|
|
9
|
+
"**/.pi/**",
|
|
10
|
+
"**/.aws/**",
|
|
11
|
+
"**/.azure/**",
|
|
12
|
+
"**/.docker/**",
|
|
13
|
+
"**/.gnupg/**",
|
|
14
|
+
"**/.kube/**",
|
|
15
|
+
"**/.ssh/**",
|
|
16
|
+
"**/.git-credentials",
|
|
17
|
+
"**/.netrc",
|
|
18
|
+
"**/.npmrc",
|
|
19
|
+
"**/.pypirc",
|
|
6
20
|
"**/auth.json",
|
|
21
|
+
"**/credential.json",
|
|
22
|
+
"**/credentials.json",
|
|
23
|
+
"**/secret.json",
|
|
24
|
+
"**/secrets.json",
|
|
25
|
+
"**/token.json",
|
|
26
|
+
"**/tokens.json",
|
|
7
27
|
"**/.env",
|
|
8
28
|
"**/.env.*",
|
|
9
29
|
"**/node_modules/**",
|
|
@@ -16,7 +36,27 @@
|
|
|
16
36
|
],
|
|
17
37
|
"shellProtectedPaths": [
|
|
18
38
|
".git/**",
|
|
39
|
+
"**/.git/**",
|
|
40
|
+
"**/.hg/**",
|
|
41
|
+
"**/.svn/**",
|
|
42
|
+
"**/.pi/**",
|
|
43
|
+
"**/.aws/**",
|
|
44
|
+
"**/.azure/**",
|
|
45
|
+
"**/.docker/**",
|
|
46
|
+
"**/.gnupg/**",
|
|
47
|
+
"**/.kube/**",
|
|
48
|
+
"**/.ssh/**",
|
|
49
|
+
"**/.git-credentials",
|
|
50
|
+
"**/.netrc",
|
|
51
|
+
"**/.npmrc",
|
|
52
|
+
"**/.pypirc",
|
|
19
53
|
"**/auth.json",
|
|
54
|
+
"**/credential.json",
|
|
55
|
+
"**/credentials.json",
|
|
56
|
+
"**/secret.json",
|
|
57
|
+
"**/secrets.json",
|
|
58
|
+
"**/token.json",
|
|
59
|
+
"**/tokens.json",
|
|
20
60
|
"**/.env",
|
|
21
61
|
"**/.env.*",
|
|
22
62
|
".pi/piagent-state/**",
|
|
@@ -232,7 +272,7 @@
|
|
|
232
272
|
},
|
|
233
273
|
"orchestrationPolicy": {
|
|
234
274
|
"defaultMode": "solo-first",
|
|
235
|
-
"maxConcurrentSubagents":
|
|
275
|
+
"maxConcurrentSubagents": 1,
|
|
236
276
|
"defaultReviewLenses": [
|
|
237
277
|
"correctness",
|
|
238
278
|
"tests",
|
|
@@ -240,7 +280,7 @@
|
|
|
240
280
|
],
|
|
241
281
|
"roleModelGuidance": {
|
|
242
282
|
"planner": "Use the strongest available model for decomposition, architecture, risk, and acceptance criteria.",
|
|
243
|
-
"worker": "
|
|
283
|
+
"worker": "Disabled by default. The parent model owns all implementation and mutation.",
|
|
244
284
|
"reviewer": "Use a model or thinking setting decorrelated from the worker when review quality matters.",
|
|
245
285
|
"watchdog": "Use a strong model only for final risk review, security, release, or high-impact changes."
|
|
246
286
|
},
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"CAP-11": "advisory",
|
|
35
35
|
"CAP-12": "on",
|
|
36
36
|
"CAP-13": "off",
|
|
37
|
-
"CAP-14": "
|
|
37
|
+
"CAP-14": "off",
|
|
38
38
|
"CAP-15": "off",
|
|
39
39
|
"CAP-16": "on",
|
|
40
40
|
"CAP-17": "on"
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"CAP-11": "advisory",
|
|
81
81
|
"CAP-12": "on",
|
|
82
82
|
"CAP-13": "strict",
|
|
83
|
-
"CAP-14": "
|
|
83
|
+
"CAP-14": "off",
|
|
84
84
|
"CAP-15": "off",
|
|
85
85
|
"CAP-16": "on",
|
|
86
86
|
"CAP-17": "on"
|
|
@@ -314,7 +314,7 @@
|
|
|
314
314
|
"layer": "L3",
|
|
315
315
|
"configKey": "helpers",
|
|
316
316
|
"configSources": ["manifest", "PIAGENT_HELPERS_MODE"],
|
|
317
|
-
"defaultMode": "
|
|
317
|
+
"defaultMode": "off",
|
|
318
318
|
"killSwitchMode": "off",
|
|
319
319
|
"dependencies": ["CAP-02", "CAP-05", "CAP-08"],
|
|
320
320
|
"modeMappings": [
|
|
@@ -26,13 +26,7 @@ Flow:
|
|
|
26
26
|
- BE scout: read-only.
|
|
27
27
|
- FE implementation: source-write.
|
|
28
28
|
- Auth/data migration/external provider: high-risk, ask before implementation.
|
|
29
|
-
4.
|
|
30
|
-
- `piagent-scout` maps backend contract read-only;
|
|
31
|
-
- `piagent-scout` maps frontend touchpoints read-only;
|
|
32
|
-
- builtin `context-builder` may create handoff context when the BE→FE mapping touches multiple journeys/forms/contracts;
|
|
33
|
-
- `piagent-planner` produces the FE implementation plan when the contract touches multiple layers;
|
|
34
|
-
- `piagent-reviewer` reviews diff/verification after implementation.
|
|
35
|
-
Continue single-agent if subagents are unavailable, the task is tiny, or requirements are unresolved.
|
|
29
|
+
4. Keep BE scouting and FE implementation in the parent by default. At most one fresh read-only `piagent-scout` may map either the BE contract or FE touchpoints only when runtime evidence proves two independent lanes and at least 30% projected total token savings after handoff/merge. Never launch two scouts, delegate FE writes/planning/review, inherit the parent transcript, or retry a deterministic helper failure.
|
|
36
30
|
5. Scout backend contract read-only:
|
|
37
31
|
- controller/route/handler;
|
|
38
32
|
- request/response DTO/schema;
|
|
@@ -53,7 +47,7 @@ Flow:
|
|
|
53
47
|
- route/page/component/form;
|
|
54
48
|
- tests/e2e.
|
|
55
49
|
9. Call `piagent_task_start` exactly once before FE source writes and reuse an active contract.
|
|
56
|
-
10. Implement FE
|
|
50
|
+
10. Implement FE in the parent; helper workers are disabled.
|
|
57
51
|
11. Run every exact command returned in `task.verifyCommands` after the latest mutation. For a normal default plan, review the final diff and mark only its review step done with `piagent_task_progress`.
|
|
58
52
|
12. Let runtime hooks record context, verification, trace, and final-gate state. Use recovery tools only if the runtime reports missing evidence.
|
|
59
53
|
13. Do not paste full test/build/tool logs into chat or final output. Summarize the signal and quote only relevant failing lines.
|
|
@@ -17,7 +17,7 @@ Rules:
|
|
|
17
17
|
4. Read `.pi/project-context.md`; if it is missing or still pending, recommend `/onboard-project run` before implementation.
|
|
18
18
|
5. Read required context.
|
|
19
19
|
6. Identify protected paths and external/high-risk actions.
|
|
20
|
-
7.
|
|
20
|
+
7. Keep planning in the parent. At most one fresh read-only helper may run only when runtime evidence proves two independent workstreams, no dependency on later parent output, and at least 30% projected total token savings after transfer/merge. Never delegate implementation, fork parent history, retry a deterministic helper failure, or add a helper only for review quality.
|
|
21
21
|
8. Produce a compact task tree/workPlan with exact files, commands, owner role, review lenses, and verify gates.
|
|
22
22
|
9. Do not implement yet unless the user explicitly asks.
|
|
23
23
|
|
|
@@ -23,18 +23,14 @@ Flow:
|
|
|
23
23
|
1. Use runtime automatic intake for a bounded platform change. If runtime pauses for broad, high-risk, or ambiguous scope, call `piagent_task_start` exactly once with project-relative path/glob scope and reuse an active contract.
|
|
24
24
|
2. Read the narrow current implementation, its tests, and the relevant policy/schema/docs. Runtime hooks inject bounded navigation context when useful.
|
|
25
25
|
3. Treat memory and generated context as advisory; verify against current files.
|
|
26
|
-
4.
|
|
27
|
-
- use `piagent-scout` for read-only code/package mapping;
|
|
28
|
-
- use `piagent-planner` for a plan when multiple modules are affected;
|
|
29
|
-
- use `piagent-reviewer` for diff, docs, and verification review;
|
|
30
|
-
- continue single-agent for small, localized edits.
|
|
26
|
+
4. Keep platform analysis, planning, implementation, and review in the parent. At most one fresh read-only helper may map a genuinely independent lane only when runtime evidence projects at least 30% lower total tokens after handoff/merge. Do not delegate writes, fork parent history, run parallel helpers, or retry deterministic runtime/scope failures.
|
|
31
27
|
5. If the task needs external repository context provided by the user, use a targeted read-only checkout when available.
|
|
32
28
|
6. Produce an implementation matrix only when multiple platform areas are affected:
|
|
33
29
|
|
|
34
30
|
| Area | Current behavior | Target behavior | Files/config | Verification |
|
|
35
31
|
|---|---|---|---|---|
|
|
36
32
|
|
|
37
|
-
7. Implement only the bounded target behavior
|
|
33
|
+
7. Implement only the bounded target behavior in the parent.
|
|
38
34
|
8. If runtime behavior changes, update README/docs and add or adjust a decision note when appropriate.
|
|
39
35
|
9. Run every exact command returned in `task.verifyCommands` after the latest mutation. For a normal default plan, review the final diff and mark only its review step done with `piagent_task_progress`.
|
|
40
36
|
10. Let runtime hooks record context, verification, trace, and final-gate state. Use recovery tools only if the runtime reports missing evidence.
|
|
@@ -14,13 +14,8 @@ Rules:
|
|
|
14
14
|
1. Read the current diff and the narrow surrounding source/tests. Runtime hooks inject bounded navigation context when useful.
|
|
15
15
|
2. Select review lenses from the actual change risk; do not load orchestration or diagnostic tools just to choose them.
|
|
16
16
|
3. Stay read-only unless explicitly asked to write a report.
|
|
17
|
-
4. Check protected paths,
|
|
18
|
-
5.
|
|
19
|
-
- correctness/edge cases;
|
|
20
|
-
- tests/verification;
|
|
21
|
-
- scope drift/protected paths;
|
|
22
|
-
- security/high-risk only when relevant.
|
|
23
|
-
If the user asks for a loop, use `/review-loop` semantics or equivalent parent-controlled max-round loop. Continue single-agent for tiny diffs or unavailable subagent tooling.
|
|
17
|
+
4. Check protected paths, unrelated change drift, acceptance behavior, and verify-command coverage. Treat task scope as an initial focus, not write authority.
|
|
18
|
+
5. Review directly in the parent. A non-trivial diff does not itself justify another model run. At most one fresh read-only reviewer is eligible only when a runtime estimate proves an independent lane and at least 30% projected total token savings after handoff/merge; never run parallel review or retry a deterministic helper failure. If the user explicitly asks for a loop, keep it parent-controlled and bounded.
|
|
24
19
|
6. Report findings by severity.
|
|
25
20
|
7. Do not call diagnostic Piagent tools for routine review and do not paste full diff/test/tool logs. Summarize the signal and quote only relevant lines.
|
|
26
21
|
|
|
@@ -15,7 +15,7 @@ Lean scouting flow:
|
|
|
15
15
|
|
|
16
16
|
1. Read targeted current files and return cited evidence. Runtime hooks inject bounded navigation context when useful and enforce read boundaries.
|
|
17
17
|
2. Create a read-only contract with `piagent_task_start` only for a broad governed scout that needs persisted scope or handoff. Tiny default scouts complete from observed reads; for a normal default scout, review the evidence and mark only the returned review step done with `piagent_task_progress`.
|
|
18
|
-
3. Use memory
|
|
18
|
+
3. Use memory or vendor documentation only when current source is insufficient. Keep scouting in the parent; a helper requires separate runtime proof of two independent lanes and at least 30% projected net token saving.
|
|
19
19
|
4. A low-confidence search may trigger one bounded finder pass. Stop broad search when the requested evidence is covered.
|
|
20
20
|
5. Do not call diagnostic Piagent tools for a routine scout. Load them only when the runtime reports a recovery need or the operator explicitly asks for diagnostics.
|
|
21
21
|
6. Do not edit source or paste full command output. Summarize relevant evidence and unknowns.
|