@piagent/platform 1.5.5 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +117 -0
- package/README.md +9 -8
- package/README.vi.md +9 -5
- package/adapters/web-frontend/profile.json +2 -4
- package/architecture/layers.json +18 -3
- package/benchmarks/deep-logic-v1/SPECIALIST_PROTOCOL.vi.md +122 -0
- package/benchmarks/deep-logic-v1/grade.mjs +272 -10
- package/benchmarks/deep-logic-v1/project/apps/admin/src/billing-summary.js +3 -0
- package/benchmarks/deep-logic-v1/project/benchmark-contract/public-smoke.test.js +166 -0
- package/benchmarks/deep-logic-v1/project/package.json +1 -1
- package/benchmarks/deep-logic-v1/project/packages/billing/src/close-period.js +3 -0
- package/benchmarks/deep-logic-v1/project/packages/billing/src/plan-timeline.js +3 -0
- package/benchmarks/deep-logic-v1/project/test/smoke.test.js +7 -1
- package/benchmarks/deep-logic-v1/prompts/budgeted-context-graph.md +2 -0
- package/benchmarks/deep-logic-v1/prompts/fair-dependency-scheduler.md +2 -0
- package/benchmarks/deep-logic-v1/prompts/layered-policy-resolution.md +2 -0
- package/benchmarks/deep-logic-v1/prompts/resumable-stream-assembly.md +3 -1
- package/benchmarks/deep-logic-v1/prompts/revision-event-reconciliation.md +2 -0
- package/benchmarks/deep-logic-v1/prompts/temporal-usage-billing-close.md +123 -0
- package/benchmarks/deep-logic-v1/prompts/transactional-config-merge.md +2 -0
- package/benchmarks/deep-logic-v1/suite.json +51 -3
- package/benchmarks/deep-logic-v1/variant.mjs +22 -1
- package/benchmarks/production-v1/spend-control.v1.json +68 -0
- package/benchmarks/production-v1/suite.json +42 -3
- package/catalog/capabilities.json +6 -6
- package/docs/command-reference-vietnamese.md +6 -6
- package/docs/context-engine.md +7 -1
- package/docs/distribution-standard.md +7 -7
- package/docs/en/architecture.md +1 -0
- package/docs/languages.json +4 -4
- package/docs/model-options.md +8 -5
- package/docs/operator-manual-vietnamese.md +13 -11
- package/docs/project-adapters.md +2 -2
- package/docs/publishing-for-teams.md +5 -0
- package/docs/quality-benchmark.md +271 -62
- package/docs/release-install-policy.md +10 -9
- package/docs/runtime-policy-design.md +4 -4
- package/docs/runtime-quality-baseline.md +13 -6
- package/docs/security-threat-model.md +2 -2
- package/docs/task-lifecycle-vietnamese.md +2 -2
- package/docs/team-onboarding.md +2 -2
- package/docs/usage-observability.md +7 -2
- package/docs/vi/architecture.md +1 -0
- package/docs-site/architecture.html +1 -1
- package/docs-site/benchmark.html +1 -1
- package/docs-site/commands.html +1 -1
- package/docs-site/context-engine.html +1 -1
- package/docs-site/en/architecture.html +1 -1
- package/docs-site/en/benchmark.html +1 -1
- package/docs-site/en/commands.html +1 -1
- package/docs-site/en/context-engine.html +1 -1
- package/docs-site/en/index.html +1 -1
- package/docs-site/en/mcp-approval.html +1 -1
- package/docs-site/en/mcp-auth.html +1 -1
- package/docs-site/en/mcp-servers.html +1 -1
- package/docs-site/en/mcp.html +1 -1
- package/docs-site/en/permissions.html +1 -1
- package/docs-site/en/profiles.html +1 -1
- package/docs-site/en/quickstart.html +1 -1
- package/docs-site/en/runtime.html +1 -1
- package/docs-site/en/scope.html +1 -1
- package/docs-site/en/security.html +1 -1
- package/docs-site/en/team.html +1 -1
- package/docs-site/en/whats-new.html +1 -1
- package/docs-site/en/workflows.html +1 -1
- package/docs-site/index.html +1 -1
- package/docs-site/mcp-approval.html +1 -1
- package/docs-site/mcp-auth.html +1 -1
- package/docs-site/mcp-servers.html +1 -1
- package/docs-site/mcp.html +1 -1
- package/docs-site/permissions.html +1 -1
- package/docs-site/profiles.html +1 -1
- package/docs-site/quickstart.html +1 -1
- package/docs-site/runtime.html +1 -1
- package/docs-site/scope.html +1 -1
- package/docs-site/security.html +1 -1
- package/docs-site/team.html +1 -1
- package/docs-site/whats-new.html +1 -1
- package/docs-site/workflows.html +1 -1
- package/evals/fixtures/benchmark-assurance-evidence.invalid.json +1 -1
- package/evals/fixtures/benchmark-assurance-evidence.valid.json +1 -1
- package/evals/fixtures/piagent-webui/session-live-state-v1.invalid.json +18 -0
- package/evals/fixtures/piagent-webui/session-live-state-v1.valid.json +27 -0
- package/evals/fs4-readiness-matrix.v1.json +2 -2
- package/evals/fs5-causal-arm.v1.json +2 -2
- package/evals/fs5-pilot-protocol.v1.json +2 -2
- package/evals/fs5-pilot-protocol.v4.json +6 -5
- package/evals/fs5-pilot-protocol.v5.json +7 -6
- package/evals/private-holdout-v1/public-exposure.v1.json +1 -1
- package/package.json +3 -1
- package/packages/piagent-core/benchmark/benchmark-assurance.js +4 -1
- package/packages/piagent-core/benchmark/benchmark-bootstrap.js +91 -9
- package/packages/piagent-core/benchmark/benchmark-claim-restrictions.js +38 -7
- package/packages/piagent-core/benchmark/benchmark-cli.js +7 -7
- package/packages/piagent-core/benchmark/benchmark-codex.js +3 -1
- package/packages/piagent-core/benchmark/benchmark-comparison.js +271 -2
- package/packages/piagent-core/benchmark/benchmark-core.js +732 -661
- package/packages/piagent-core/benchmark/benchmark-execution-guard.js +7 -5
- package/packages/piagent-core/benchmark/benchmark-forensics.js +439 -4
- package/packages/piagent-core/benchmark/benchmark-host-readiness.js +639 -0
- package/packages/piagent-core/benchmark/benchmark-matrix.js +22 -4
- package/packages/piagent-core/benchmark/benchmark-normalized-cost.js +317 -0
- package/packages/piagent-core/benchmark/benchmark-preflight.js +3 -1
- package/packages/piagent-core/benchmark/benchmark-process.js +6 -5
- package/packages/piagent-core/benchmark/benchmark-provider-wire.js +126 -0
- package/packages/piagent-core/benchmark/benchmark-record-validation.js +183 -0
- package/packages/piagent-core/benchmark/benchmark-report.js +116 -12
- package/packages/piagent-core/benchmark/benchmark-resume-recovery.js +14 -0
- package/packages/piagent-core/benchmark/benchmark-runner-policy.js +26 -0
- package/packages/piagent-core/benchmark/benchmark-stage-diagnostic.js +782 -0
- package/packages/piagent-core/benchmark/benchmark-statistics.js +64 -0
- package/packages/piagent-core/benchmark/benchmark-stop-policy.js +8 -5
- package/packages/piagent-core/benchmark/benchmark-suite-identity.js +31 -0
- package/packages/piagent-core/benchmark/benchmark-suite-runtime.js +13 -9
- package/packages/piagent-core/benchmark/benchmark-suite.js +128 -2
- package/packages/piagent-core/benchmark/benchmark-summary-support.js +376 -0
- package/packages/piagent-core/benchmark/benchmark-timing-diagnostics.js +971 -0
- package/packages/piagent-core/benchmark/benchmark-usage.js +311 -0
- package/packages/piagent-core/benchmark/benchmark-workflow.js +166 -0
- package/packages/piagent-core/docs/context-efficiency-improvement-plan.md +1080 -0
- package/packages/piagent-core/extensions/acceptance-contract-semantics.js +83 -69
- package/packages/piagent-core/extensions/acceptance-error-classes.js +160 -0
- package/packages/piagent-core/extensions/acceptance-executable-evidence.js +12 -9
- package/packages/piagent-core/extensions/acceptance-receipt.js +34 -11
- package/packages/piagent-core/extensions/acceptance-state-machine-evidence.js +518 -0
- package/packages/piagent-core/extensions/acceptance-test-binding-evidence.js +60 -0
- package/packages/piagent-core/extensions/context-efficiency-metrics.js +302 -0
- package/packages/piagent-core/extensions/context-efficiency-report.js +167 -0
- package/packages/piagent-core/extensions/context-engine.js +148 -145
- package/packages/piagent-core/extensions/context-evidence.js +48 -0
- package/packages/piagent-core/extensions/context-import-links.js +296 -0
- package/packages/piagent-core/extensions/context-telemetry-integrity.js +65 -0
- package/packages/piagent-core/extensions/criterion-context-pack.js +422 -7
- package/packages/piagent-core/extensions/criterion-graph.js +78 -8
- package/packages/piagent-core/extensions/guard-types.ts +2 -0
- package/packages/piagent-core/extensions/javascript-regex-evidence.js +32 -0
- package/packages/piagent-core/extensions/piagent-guard.ts +233 -41
- package/packages/piagent-core/extensions/task-contract-view.js +17 -3
- package/packages/piagent-core/extensions/task-lifecycle.js +7 -2
- package/packages/piagent-core/extensions/task-state.js +40 -134
- package/packages/piagent-core/extensions/verification-intelligence.js +3 -1
- package/packages/piagent-core/extensions/workspace-evidence-roots.js +157 -0
- package/packages/piagent-core/package.json +1 -1
- package/packages/piagent-core/policies/base-policy.json +2 -0
- package/packages/piagent-core/runtime/context/context-delivery.ts +103 -0
- package/packages/piagent-core/runtime/context/context-delta-shadow.ts +53 -0
- package/packages/piagent-core/runtime/context/context-efficiency-format.ts +47 -0
- package/packages/piagent-core/runtime/context/context-evidence-qualification.ts +32 -0
- package/packages/piagent-core/runtime/context/prefix-telemetry.ts +47 -0
- package/packages/piagent-core/runtime/context/repository-memory-hints.ts +27 -0
- package/packages/piagent-core/runtime/hooks/agent-prompt-telemetry.ts +56 -0
- package/packages/piagent-core/runtime/hooks/agent-start-hook.ts +162 -102
- package/packages/piagent-core/runtime/hooks/completion-hook.ts +5 -6
- package/packages/piagent-core/runtime/hooks/input-hook.ts +11 -1
- package/packages/piagent-core/runtime/hooks/session-hooks.ts +22 -2
- package/packages/piagent-core/runtime/hooks/session-start-hook.ts +7 -2
- package/packages/piagent-core/runtime/hooks/tool-result-content-guards.ts +95 -0
- package/packages/piagent-core/runtime/hooks/tool-result-hook.ts +84 -102
- package/packages/piagent-core/runtime/inspection/activity-event-adapter.ts +4 -2
- package/packages/piagent-core/runtime/inspection/activity-recovery.ts +104 -0
- package/packages/piagent-core/runtime/inspection/task-compaction-history.ts +1 -1
- package/packages/piagent-core/runtime/inspection/task-handoff-history.ts +1 -1
- package/packages/piagent-core/runtime/inspection/tool-failure-classification.ts +53 -0
- package/packages/piagent-core/runtime/inspection/webui-snapshot.ts +34 -6
- package/packages/piagent-core/runtime/model/authenticated-catalog.ts +17 -9
- package/packages/piagent-core/runtime/model/openai-codex-reasoning.ts +83 -0
- package/packages/piagent-core/runtime/model/provider-wire-fingerprint.ts +238 -0
- package/packages/piagent-core/runtime/orchestration/role-policy.ts +1 -1
- package/packages/piagent-core/runtime/product/activity-inspector.ts +7 -2
- package/packages/piagent-core/runtime/product/efficiency-metrics.ts +4 -2
- package/packages/piagent-core/runtime/quality/exact-output-contract.ts +2 -1
- package/packages/piagent-core/runtime/quality/performance-assurance-signals.ts +23 -0
- package/packages/piagent-core/runtime/quality/performance-assurance.ts +6 -2
- package/packages/piagent-core/runtime/recovery/edit-recovery-context.ts +113 -0
- package/packages/piagent-core/runtime/recovery/edit-recovery-delivery.ts +42 -0
- package/packages/piagent-core/runtime/recovery/semantic-repair-handshake.ts +55 -2
- package/packages/piagent-core/runtime/recovery/semantic-repair-runtime.ts +55 -2
- package/packages/piagent-core/runtime/registration/context-commands.ts +66 -5
- package/packages/piagent-core/runtime/registration/policy-tools.ts +46 -11
- package/packages/piagent-core/runtime/registration/task-completion-tools.ts +49 -16
- package/packages/piagent-core/runtime/registration/task-start-guidance.ts +163 -0
- package/packages/piagent-core/runtime/registration/task-start-retry-helpers.ts +37 -0
- package/packages/piagent-core/runtime/registration/task-start-tool.ts +67 -62
- package/packages/piagent-core/runtime/session/runtime-state.ts +154 -2
- package/packages/piagent-core/runtime/session/system-prompt.ts +4 -4
- package/packages/piagent-core/runtime/tools/apply-patch-tool.ts +500 -0
- package/packages/piagent-core/runtime/trajectory/trajectory-runtime.ts +2 -1
- package/packages/piagent-core/runtime/workflows/task-intake.ts +43 -2
- package/packages/piagent-core/security/sensitive-data.js +13 -218
- package/packages/piagent-core/security/sensitive-project-file.js +42 -0
- package/packages/piagent-core/security/sensitive-source-expression.js +330 -0
- package/packages/piagent-core/security/sensitive-source-formats.js +394 -0
- package/packages/piagent-core/security/sensitive-text.js +234 -0
- package/packages/piagent-core/subagents/piagent-worker.md +1 -1
- package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-rD9WJKxs.js → MarkdownMessage-CxeZqTte.js} +1 -1
- package/packages/piagent-webui/dist/client/assets/index-DynIMe8I.js +176 -0
- package/packages/piagent-webui/dist/client/index.html +1 -1
- package/packages/piagent-webui/gateway/gateway-events.ts +109 -5
- package/packages/piagent-webui/gateway/gateway-service.ts +3 -0
- package/packages/piagent-webui/gateway/gateway-session-stream.ts +93 -7
- package/packages/piagent-webui/gateway/session-command-controller.ts +18 -3
- package/packages/piagent-webui/gateway/session-effective-options.ts +47 -0
- package/packages/piagent-webui/gateway/session-live-state.ts +21 -0
- package/packages/piagent-webui/gateway/session-operation-start.ts +5 -1
- package/packages/piagent-webui/gateway/session-operation-watchdog.ts +158 -0
- package/packages/piagent-webui/gateway/session-runtime-factory.ts +45 -0
- package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +139 -124
- package/packages/piagent-webui/server/loopback-server.ts +8 -0
- package/packages/piagent-webui/server/transcript-projection.ts +22 -4
- package/packages/piagent-webui/shared/text-visibility.ts +10 -0
- package/schemas/piagent-webui/catalog-v1.json +9 -0
- package/schemas/piagent-webui/gateway-protocol-v1.schema.json +53 -3
- package/schemas/piagent-webui/session-live-state-v1.schema.json +76 -0
- package/schemas/task-contract.schema.json +12 -1
- package/scripts/benchmark-runner-core.mjs +355 -359
- package/scripts/benchmark-runner-finalization.mjs +309 -0
- package/scripts/benchmark-runner-host-readiness.mjs +94 -0
- package/scripts/benchmark-runner-support.mjs +200 -0
- package/scripts/benchmark-session.mjs +95 -41
- package/scripts/configure-model-scope.sh +4 -3
- package/scripts/helper-evaluation.mjs +1 -1
- package/scripts/install-global.sh +2 -2
- package/scripts/pi-usage-history.mjs +37 -10
- package/scripts/setup.sh +2 -2
- package/scripts/verify-local.sh +5 -1
- package/templates/global/settings.json +2 -1
- package/packages/piagent-webui/dist/client/assets/index-BZRFw1u6.js +0 -174
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { durableContextEvidenceEntries, isRuntimeOwnedContextEvidenceEntry } from "../../extensions/context-evidence.js";
|
|
3
|
+
import { mergeObservedTaskContext } from "../../extensions/task-contract-view.js";
|
|
2
4
|
import { buildHandoffProjection, writeHandoffProjection } from "../recovery/handoff-projection.ts";
|
|
3
5
|
import { buildCompletionReceiptView } from "../product/operator-projections.ts";
|
|
4
6
|
import { semanticRepairProvenance } from "../recovery/semantic-repair-handshake.ts";
|
|
@@ -41,13 +43,50 @@ export function registerTaskCompletionTools(pi: ExtensionAPI, deps: Record<strin
|
|
|
41
43
|
if (task.trace.outcome !== "pending") {
|
|
42
44
|
return { content: [{ type: "text", text: `Task ${task.taskId} is immutable after ${task.trace.outcome}; context evidence was not changed.` }], details: task, isError: true };
|
|
43
45
|
}
|
|
44
|
-
|
|
46
|
+
const activeIdentity = runtimeState.taskIdentity(ctx);
|
|
47
|
+
if (!activeIdentity || activeIdentity.taskId !== task.taskId || activeIdentity.taskRunId !== task.taskRunId) {
|
|
45
48
|
return {
|
|
46
|
-
content: [{ type: "text", text: `Context
|
|
49
|
+
content: [{ type: "text", text: `Context evidence rejected for ${task.taskId}: the active runtime task identity does not match this session task.` }],
|
|
50
|
+
details: { reasonCode: "context-evidence-task-identity-mismatch" },
|
|
47
51
|
isError: true
|
|
48
52
|
};
|
|
49
53
|
}
|
|
50
|
-
const
|
|
54
|
+
const observedPaths = new Set(runtimeState.observedContext(ctx)
|
|
55
|
+
.map((entry: { path: string }) => normalizeRelative(ctx.cwd, entry.path))
|
|
56
|
+
.filter((filePath: string | undefined): filePath is string => Boolean(filePath)));
|
|
57
|
+
const qualifiedByPath = new Map<string, { path: string; reason: string }>();
|
|
58
|
+
for (const entry of runtimeState.qualifiedContextEvidence(ctx, task.taskRunId)) {
|
|
59
|
+
const filePath = normalizeRelative(ctx.cwd, entry.path);
|
|
60
|
+
if (filePath && isRuntimeOwnedContextEvidenceEntry(entry)) qualifiedByPath.set(filePath, entry);
|
|
61
|
+
}
|
|
62
|
+
const qualifiedFiles = new Map<string, { path: string; reason: string }>();
|
|
63
|
+
const unprovenPaths: string[] = [];
|
|
64
|
+
for (const file of params.files) {
|
|
65
|
+
const filePath = normalizeRelative(ctx.cwd, file.path);
|
|
66
|
+
const evidence = filePath ? qualifiedByPath.get(filePath) : undefined;
|
|
67
|
+
if (!filePath || !observedPaths.has(filePath) || !evidence) {
|
|
68
|
+
unprovenPaths.push(redactText(file.path));
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
qualifiedFiles.set(filePath, { path: filePath, reason: evidence.reason });
|
|
72
|
+
}
|
|
73
|
+
if (unprovenPaths.length > 0) {
|
|
74
|
+
return {
|
|
75
|
+
content: [{ type: "text", text: `Context evidence rejected for ${task.taskId}: no runtime-observed read or host-confirmed delivery exists in this task/session for ${unprovenPaths.join(", ")}. Read the exact file successfully in this task or rely on automatic delivery confirmation.` }],
|
|
76
|
+
details: { reasonCode: "context-evidence-unproven", paths: unprovenPaths },
|
|
77
|
+
isError: true
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const safeFiles = [...qualifiedFiles.values()];
|
|
81
|
+
const evidencePaths = new Set(durableContextEvidenceEntries(task).map((item) => item.path));
|
|
82
|
+
const projectedManifestFiles = evidencePaths.size + new Set(safeFiles.map((item) => item.path).filter((filePath) => !evidencePaths.has(filePath))).size;
|
|
83
|
+
if (runtime.contextBudget !== "off" && projectedManifestFiles > budget.maxManifestFiles) {
|
|
84
|
+
return {
|
|
85
|
+
content: [{ type: "text", text: `Context manifest budget exceeded: ${projectedManifestFiles} files > ${budget.maxManifestFiles}` }],
|
|
86
|
+
isError: true
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const fileBudget = safeFiles.map((file) => candidateFileBudget(ctx.cwd, file.path, budget));
|
|
51
90
|
const overLimit = fileBudget.filter((item) => item.overLimit);
|
|
52
91
|
if (runtime.contextBudget === "enforce" && overLimit.length > 0) {
|
|
53
92
|
return {
|
|
@@ -56,17 +95,8 @@ export function registerTaskCompletionTools(pi: ExtensionAPI, deps: Record<strin
|
|
|
56
95
|
isError: true
|
|
57
96
|
};
|
|
58
97
|
}
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
path: file.path,
|
|
62
|
-
reason: redactText(file.reason)
|
|
63
|
-
}));
|
|
64
|
-
const seen = new Set(task.contextManifest.map((item) => `${item.path}\u0000${item.reason}`));
|
|
65
|
-
for (const file of safeFiles) {
|
|
66
|
-
const key = `${file.path}\u0000${file.reason}`;
|
|
67
|
-
if (!seen.has(key)) task.contextManifest.push(file);
|
|
68
|
-
}
|
|
69
|
-
const lifecycle = task.changeMode === "read-only"
|
|
98
|
+
mergeObservedTaskContext(task, safeFiles, budget.maxManifestFiles, (value) => value);
|
|
99
|
+
const lifecycle = task.changeMode === "read-only" || task.mutationPolicy === "forbidden"
|
|
70
100
|
? applyRuntimeLifecycleObservation(task, "context-complete", nowIso())
|
|
71
101
|
: { changed: false, mode: runtimeLifecycleMode(task) };
|
|
72
102
|
const written = writeTask(ctx.cwd, task);
|
|
@@ -74,7 +104,7 @@ export function registerTaskCompletionTools(pi: ExtensionAPI, deps: Record<strin
|
|
|
74
104
|
appendSessionTrace(pi, { taskId: task.taskId, taskRunId: task.taskRunId, sessionId: task.sessionId, event: "context_record", files: safeFiles, lifecycleMode: lifecycle.mode, lifecycleAdvanced: lifecycle.changed });
|
|
75
105
|
|
|
76
106
|
return {
|
|
77
|
-
content: [{ type: "text", text: `Context recorded for ${task.taskId}: ${
|
|
107
|
+
content: [{ type: "text", text: `Context recorded for ${task.taskId}: ${safeFiles.length} runtime-qualified file(s)` }],
|
|
78
108
|
details: compactTaskDetails(written)
|
|
79
109
|
};
|
|
80
110
|
}
|
|
@@ -296,7 +326,10 @@ export function registerTaskCompletionTools(pi: ExtensionAPI, deps: Record<strin
|
|
|
296
326
|
currentDigests: finalFileDigests,
|
|
297
327
|
currentWorkingTreeDigest: workingTreeEvidenceDigest(finalFileDigests)
|
|
298
328
|
});
|
|
299
|
-
const semanticBlock = taskAuthorityDecision(nextTask, "CAP-13", "block").allowed
|
|
329
|
+
const semanticBlock = (taskAuthorityDecision(nextTask, "CAP-13", "block").allowed
|
|
330
|
+
|| taskAuthorityDecision(nextTask, "CAP-12", "mutate").allowed)
|
|
331
|
+
? semanticRepairCompletionBlock?.(ctx.cwd, nextTask.taskRunId)
|
|
332
|
+
: undefined;
|
|
300
333
|
if (semanticBlock) gate = { ...gate, decision: "fail", missing: [...new Set([...gate.missing, semanticBlock])] };
|
|
301
334
|
if (params.outcome === "completed" && (semanticBlock || (runtime.finalGate === "enforce" && gate.decision === "fail"))) {
|
|
302
335
|
const blockedTrace = {
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
type CriterionNode = {
|
|
2
|
+
id?: string;
|
|
3
|
+
obligation?: string;
|
|
4
|
+
kind?: string;
|
|
5
|
+
proofKinds?: string[];
|
|
6
|
+
targetHints?: string[];
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type CriterionGraph = { mode?: string; nodes?: CriterionNode[] } | undefined;
|
|
10
|
+
|
|
11
|
+
export type FocusedTestCandidate = {
|
|
12
|
+
criterionId: string;
|
|
13
|
+
path: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type CriterionProofGuidance = {
|
|
17
|
+
criterionId: string;
|
|
18
|
+
guidance: string[];
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
type ProofTask = {
|
|
22
|
+
changeMode?: string;
|
|
23
|
+
mutationPolicy?: string;
|
|
24
|
+
criterionGraph?: CriterionGraph;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const RUNTIME_SOURCE_REUSE_GUIDANCE = "Use runtime-delivered source; do not reread it. On edit drift/oldText mismatch, use attached recovery; otherwise reread the affected region once. Never retry guessed anchors. Otherwise read only missing source or a named criterion-focused test. Globs/directories grant scope, not file targets.";
|
|
28
|
+
|
|
29
|
+
export const EXACT_VERIFIER_EXECUTION_GUIDANCE = "Keep each exact verifier command separate and unmodified. Run the set once on the final tree; rerun only after a later mutation or a runtime-authorized same-tree infrastructure retry.";
|
|
30
|
+
|
|
31
|
+
function normalizedPath(value: string): string {
|
|
32
|
+
return value.trim().replaceAll("\\", "/");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function exactNameMentioned(obligation: string, file: string): boolean {
|
|
36
|
+
const text = obligation.toLowerCase().replaceAll("\\", "/");
|
|
37
|
+
const normalized = normalizedPath(file).toLowerCase();
|
|
38
|
+
const basename = normalized.split("/").pop() ?? "";
|
|
39
|
+
return [normalized, basename].filter(Boolean).some((candidate) => {
|
|
40
|
+
let offset = text.indexOf(candidate);
|
|
41
|
+
while (offset >= 0) {
|
|
42
|
+
const before = text[offset - 1] ?? "";
|
|
43
|
+
const after = text[offset + candidate.length] ?? "";
|
|
44
|
+
const afterNext = text[offset + candidate.length + 1] ?? "";
|
|
45
|
+
const nameContinuesAfter = /[a-z0-9_/-]/i.test(after) || (after === "." && /[a-z0-9_-]/i.test(afterNext));
|
|
46
|
+
if (!/[a-z0-9_./-]/i.test(before) && !nameContinuesAfter) return true;
|
|
47
|
+
offset = text.indexOf(candidate, offset + candidate.length);
|
|
48
|
+
}
|
|
49
|
+
return false;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function behavioralNodes(criterionGraph: CriterionGraph): CriterionNode[] {
|
|
54
|
+
return (criterionGraph?.nodes ?? []).filter((node) => (
|
|
55
|
+
typeof node?.id === "string"
|
|
56
|
+
&& typeof node?.obligation === "string"
|
|
57
|
+
&& Array.isArray(node?.proofKinds)
|
|
58
|
+
&& node.proofKinds.includes("behavioral-check")
|
|
59
|
+
));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function concretePlannedAcceptanceTests(
|
|
63
|
+
plannedContext: Array<{ path: string; reason: string }>,
|
|
64
|
+
repositoryFiles: string[],
|
|
65
|
+
criterionGraph: CriterionGraph,
|
|
66
|
+
isAcceptanceTestPath: (file: string) => boolean,
|
|
67
|
+
acceptanceLanguageAdapterForPath: (file: string) => { disposition?: string }
|
|
68
|
+
): FocusedTestCandidate[] {
|
|
69
|
+
const concreteFiles = new Set(repositoryFiles.map(normalizedPath));
|
|
70
|
+
const nodes = behavioralNodes(criterionGraph);
|
|
71
|
+
const candidates: FocusedTestCandidate[] = [];
|
|
72
|
+
for (const entry of plannedContext) {
|
|
73
|
+
const file = normalizedPath(entry.path);
|
|
74
|
+
if (!file || /[?*[\]{}]/.test(file) || !concreteFiles.has(file)) continue;
|
|
75
|
+
if (!isAcceptanceTestPath(file) || acceptanceLanguageAdapterForPath(file)?.disposition !== "supported") continue;
|
|
76
|
+
if (/\.d\.[cm]?ts$/i.test(file)) continue;
|
|
77
|
+
for (const node of nodes) {
|
|
78
|
+
if (exactNameMentioned(node.obligation!, file)) candidates.push({ criterionId: node.id!, path: file });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const seen = new Set<string>();
|
|
82
|
+
return candidates.filter((candidate) => {
|
|
83
|
+
const identity = `${candidate.criterionId}\0${candidate.path}`;
|
|
84
|
+
if (seen.has(identity)) return false;
|
|
85
|
+
seen.add(identity);
|
|
86
|
+
return true;
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function behavioralCriterionProofGuidance(
|
|
91
|
+
criterionGraph: CriterionGraph,
|
|
92
|
+
acceptanceProofGuidance: (text: string) => string[]
|
|
93
|
+
): CriterionProofGuidance[] {
|
|
94
|
+
return behavioralNodes(criterionGraph).map((node) => ({
|
|
95
|
+
criterionId: node.id!,
|
|
96
|
+
guidance: acceptanceProofGuidance(node.obligation!)
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function criticalProofSection(
|
|
101
|
+
proofGuidance: string[],
|
|
102
|
+
acceptanceTests: FocusedTestCandidate[],
|
|
103
|
+
criterionProofs: CriterionProofGuidance[]
|
|
104
|
+
): string[] {
|
|
105
|
+
if (proofGuidance.length === 0) return [];
|
|
106
|
+
const candidateMap = new Map<string, string[]>();
|
|
107
|
+
for (const candidate of acceptanceTests) {
|
|
108
|
+
candidateMap.set(candidate.criterionId, [...new Set([...(candidateMap.get(candidate.criterionId) ?? []), candidate.path])]);
|
|
109
|
+
}
|
|
110
|
+
let mappedCandidate = false;
|
|
111
|
+
const proofLines = proofGuidance.flatMap((proof) => {
|
|
112
|
+
const criterionIds = [...new Set(criterionProofs
|
|
113
|
+
.filter((entry) => entry.guidance.includes(proof))
|
|
114
|
+
.map((entry) => entry.criterionId))];
|
|
115
|
+
if (criterionIds.length === 0) {
|
|
116
|
+
return [`- [fallback] ${proof}`];
|
|
117
|
+
}
|
|
118
|
+
const tags = criterionIds.map((criterionId) => {
|
|
119
|
+
const candidates = candidateMap.get(criterionId) ?? [];
|
|
120
|
+
if (candidates.length === 0) {
|
|
121
|
+
return `[${criterionId}:fallback]`;
|
|
122
|
+
}
|
|
123
|
+
mappedCandidate = true;
|
|
124
|
+
return `[${criterionId}:candidate=${candidates.join("|")}]`;
|
|
125
|
+
});
|
|
126
|
+
return [`- ${tags.join("")} ${proof}`];
|
|
127
|
+
});
|
|
128
|
+
return [[
|
|
129
|
+
"Critical behavioral proof:",
|
|
130
|
+
...(!mappedCandidate ? ["No concrete criterion-linked focused test was selected."] : []),
|
|
131
|
+
...proofLines,
|
|
132
|
+
"Candidate tags are locations, not proof; add/update durable scoped tests with live assertions and changed-test evidence. Each fallback tag requires adding/updating a durable scoped focused test with live assertions. If test scope is unavailable, report missing durable proof and do not claim completion. Run proof after the criterion's final intended mutation and before exact verifiers; rerun after later target mutation or a runtime-authorized same-tree infrastructure retry. Transient/print-only probes and prose are insufficient."
|
|
133
|
+
].join("\n")];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function automaticTaskExecutionGuidance(task: ProofTask): string {
|
|
137
|
+
if (task.changeMode === "read-only" || task.mutationPolicy === "forbidden") {
|
|
138
|
+
return task.changeMode === "source-change"
|
|
139
|
+
? "Stay mutation-free. Runtime permits bounded inspection and the exact configured verifier, records completion evidence, and requires a zero task delta."
|
|
140
|
+
: "Stay read-only. Runtime records targeted reads and completion evidence; do not call task-management tools.";
|
|
141
|
+
}
|
|
142
|
+
return task.criterionGraph?.mode === "criterion-graph"
|
|
143
|
+
? "Follow the execution map and implement dependency-ready criteria. The map plans work but never overrides the operator request or exact verifiers. Runtime records evidence and completion; do not call task-management tools."
|
|
144
|
+
: "Privately map every operator criterion to implementation and durable focused-test coverage before mutating. Finish intended edits and a criterion-by-criterion self-review. Runtime records evidence and completion; do not call task-management tools.";
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function taskCriticalProofSection(
|
|
148
|
+
task: ProofTask,
|
|
149
|
+
plannedContext: Array<{ path: string; reason: string }>,
|
|
150
|
+
repositoryFiles: string[],
|
|
151
|
+
acceptanceProofGuidance: (taskOrText: unknown) => string[],
|
|
152
|
+
isAcceptanceTestPath: (file: string) => boolean,
|
|
153
|
+
acceptanceLanguageAdapterForPath: (file: string) => { disposition?: string }
|
|
154
|
+
): string[] {
|
|
155
|
+
const proofGuidance = task.changeMode === "source-change" && task.mutationPolicy !== "forbidden"
|
|
156
|
+
? acceptanceProofGuidance(task)
|
|
157
|
+
: [];
|
|
158
|
+
const acceptanceTests = concretePlannedAcceptanceTests(
|
|
159
|
+
plannedContext, repositoryFiles, task.criterionGraph, isAcceptanceTestPath, acceptanceLanguageAdapterForPath
|
|
160
|
+
);
|
|
161
|
+
const criterionProofs = behavioralCriterionProofGuidance(task.criterionGraph, acceptanceProofGuidance);
|
|
162
|
+
return criticalProofSection(proofGuidance, acceptanceTests, criterionProofs);
|
|
163
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
type AuthoritySnapshot = {
|
|
2
|
+
profile: string;
|
|
3
|
+
capabilities: Array<{ id: string; authority: string }>;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
type AuthorityReplacementTask = {
|
|
7
|
+
authoritySnapshot?: AuthoritySnapshot;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
type AuthorityReplacementState = {
|
|
11
|
+
reason?: string;
|
|
12
|
+
killedCapabilities: string[];
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export function sameStringRecord(left: Record<string, string>, right: Record<string, string>): boolean {
|
|
16
|
+
const leftEntries = Object.entries(left).sort(([a], [b]) => a.localeCompare(b));
|
|
17
|
+
const rightEntries = Object.entries(right).sort(([a], [b]) => a.localeCompare(b));
|
|
18
|
+
return leftEntries.length === rightEntries.length
|
|
19
|
+
&& leftEntries.every(([key, value], index) => rightEntries[index]?.[0] === key && rightEntries[index]?.[1] === value);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function satisfiesAuthorityReplacement(
|
|
23
|
+
task: AuthorityReplacementTask,
|
|
24
|
+
state: AuthorityReplacementState
|
|
25
|
+
): boolean {
|
|
26
|
+
if (!task.authoritySnapshot) return false;
|
|
27
|
+
const authoritySnapshot = task.authoritySnapshot;
|
|
28
|
+
if (state.reason === "mechanical-rollback-requested") {
|
|
29
|
+
return authoritySnapshot.profile === "mechanical-only";
|
|
30
|
+
}
|
|
31
|
+
if (state.reason === "capability-kill-switch-requested") {
|
|
32
|
+
return state.killedCapabilities.length > 0 && state.killedCapabilities.every((capabilityId) => (
|
|
33
|
+
authoritySnapshot.capabilities.some((entry) => entry.id === capabilityId && entry.authority === "off")
|
|
34
|
+
));
|
|
35
|
+
}
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
@@ -5,40 +5,25 @@ import { automaticTaskSummary, boundedRuntimeIntakeMessage } from "../workflows/
|
|
|
5
5
|
import { WORKING_TREE_DIGEST_ALGORITHM } from "../../extensions/working-tree-digest.js";
|
|
6
6
|
import { createEnvironmentBoundTaskAuthority } from "../policy/task-authority-runtime.ts";
|
|
7
7
|
import { authorityReplacementState } from "../policy/authority-resume-policy.ts";
|
|
8
|
-
import { compileCriterionGraph, criterionGraphContextSelection, criterionGraphGuidance, criterionGraphMode } from "../../extensions/criterion-graph.js";
|
|
8
|
+
import { compileCriterionGraph, criterionGraphContextSelection, criterionGraphContextSelectionDetails, criterionGraphGuidance, criterionGraphMode } from "../../extensions/criterion-graph.js";
|
|
9
9
|
import { captureTaskStartBaseline } from "../inspection/task-baseline-start-capture.ts";
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return leftEntries.length === rightEntries.length
|
|
17
|
-
&& leftEntries.every(([key, value], index) => rightEntries[index]?.[0] === key && rightEntries[index]?.[1] === value);
|
|
18
|
-
}
|
|
19
|
-
function satisfiesAuthorityReplacement(task: TaskContract, state: ReturnType<typeof authorityReplacementState>): boolean {
|
|
20
|
-
if (!task.authoritySnapshot) return false;
|
|
21
|
-
if (state.reason === "mechanical-rollback-requested") {
|
|
22
|
-
return task.authoritySnapshot.profile === "mechanical-only";
|
|
23
|
-
}
|
|
24
|
-
if (state.reason === "capability-kill-switch-requested") {
|
|
25
|
-
return state.killedCapabilities.length > 0 && state.killedCapabilities.every((capabilityId) => (
|
|
26
|
-
task.authoritySnapshot.capabilities.some((entry: any) => entry.id === capabilityId && entry.authority === "off")
|
|
27
|
-
));
|
|
28
|
-
}
|
|
29
|
-
return true;
|
|
30
|
-
}
|
|
10
|
+
import { sameStringRecord, satisfiesAuthorityReplacement } from "./task-start-retry-helpers.ts";
|
|
11
|
+
import {
|
|
12
|
+
automaticTaskExecutionGuidance, EXACT_VERIFIER_EXECUTION_GUIDANCE, RUNTIME_SOURCE_REUSE_GUIDANCE,
|
|
13
|
+
taskCriticalProofSection
|
|
14
|
+
} from "./task-start-guidance.ts";
|
|
15
|
+
type ExtensionContext = any; type TaskContract = any; type TaskStartParameters = any;
|
|
31
16
|
export function registerTaskStartTool(pi: ExtensionAPI, deps: Record<string, any>): any {
|
|
32
17
|
const {
|
|
33
18
|
DEFAULT_MAX_TASK_ATTEMPTS, ORCHESTRATION_ROLES, REVIEW_LENSES, StringEnum, Type,
|
|
34
19
|
activateToolGroups, activeSessionTask, activeTaskToolGroups, appendSessionTrace, appendTrace,
|
|
35
|
-
applyRuntimeLifecycleObservation, automaticAcceptanceCriteria, automaticReadOnlyTaskScope, automaticReviewLenses, automaticTaskIntakeMode, automaticTaskRiskLane, automaticTaskScope,
|
|
36
|
-
acceptanceBaselineGuidance, acceptanceProofGuidance, bindSessionTask, buildAcceptanceReceipt, compactTaskDetails, contextBudgetConfig, createTaskRunId,
|
|
20
|
+
applyRuntimeLifecycleObservation, automaticAcceptanceCriteria, automaticReadOnlyTaskScope, automaticReviewLenses, automaticTaskIntakeMode, automaticTaskMutationPolicy, automaticTaskRiskLane, automaticTaskScope,
|
|
21
|
+
acceptanceBaselineGuidance, acceptanceLanguageAdapterForPath, acceptanceProofGuidance, bindSessionTask, buildAcceptanceReceipt, compactTaskDetails, contextBudgetConfig, createTaskRunId,
|
|
37
22
|
currentSessionName, defaultWorkPlan, effectiveProtectedPaths, hasGitEvidenceRoot, hasOperatorSessionName,
|
|
38
|
-
loadProfileFromContext, matchesProtectedPath, normalizeReviewLenses, normalizeWorkPlanSteps, nowIso, policy,
|
|
23
|
+
isAcceptanceTestPath, loadProfileFromContext, matchesProtectedPath, normalizeReviewLenses, normalizeWorkPlanSteps, nowIso, policy,
|
|
39
24
|
priorTaskAttempts, recordTaskStartCheckpoint, redactText, redactTextArray, registerRuntimeTool,
|
|
40
|
-
repositoryFileManifest, resolveOrchestrationPolicy, resolveTaskScopePatterns, runtimeLifecycleMode, runtimeState, safeTaskId, selectVerificationPlan,
|
|
41
|
-
summarizeAttempt, validTaskScopePattern, validateNewWorkPlan, verifierCommandInstructions, workingTreeEvidenceDigest, workingTreeSnapshot, workingTreeSnapshotHasUnavailableEvidence,
|
|
25
|
+
repositoryFileManifest, repositoryFileManifestDetails, resolveOrchestrationPolicy, resolveTaskScopePatterns, runtimeLifecycleMode, runtimeState, safeTaskId, selectVerificationPlan,
|
|
26
|
+
summarizeAttempt, telemetry, validTaskScopePattern, validateNewWorkPlan, verifierCommandInstructions, workingTreeEvidenceDigest, workingTreeSnapshot, workingTreeSnapshotHasUnavailableEvidence,
|
|
42
27
|
writeTask
|
|
43
28
|
} = deps;
|
|
44
29
|
const taskStartTool = {
|
|
@@ -48,7 +33,7 @@ export function registerTaskStartTool(pi: ExtensionAPI, deps: Record<string, any
|
|
|
48
33
|
promptSnippet: "Start a governed project task and persist the task contract.",
|
|
49
34
|
promptGuidelines: [
|
|
50
35
|
"Call this exactly once before source edits in a project managed by Pi Agent Platform.",
|
|
51
|
-
"Use source-change for project
|
|
36
|
+
"Use source-change for project verifier execution or edits. For an assessment, report, or plan with no source changes, set mutationPolicy=forbidden; use read-only when no project verifier needs to execute.",
|
|
52
37
|
"Do not call context, status, policy, evidence-recording, trace, or gate tools first; runtime hooks provide those checks automatically.",
|
|
53
38
|
"Use tiny for a bounded low-risk change, normal for ordinary multi-file work, and high-risk for security, data, release, migration, or external-impact work.",
|
|
54
39
|
"Every scope entry must be a project-relative path or glob such as src/file.ts, src/**, or test/**; never put prose in scope.",
|
|
@@ -60,15 +45,16 @@ export function registerTaskStartTool(pi: ExtensionAPI, deps: Record<string, any
|
|
|
60
45
|
summary: Type.String({ minLength: 10 }),
|
|
61
46
|
riskLane: StringEnum(["tiny", "normal", "high-risk"] as const),
|
|
62
47
|
changeMode: Type.Optional(StringEnum(["source-change", "read-only"] as const, {
|
|
63
|
-
description: "Use source-change for project
|
|
48
|
+
description: "Use source-change for project verifier execution or edits; use read-only for bounded inspection."
|
|
64
49
|
})),
|
|
50
|
+
mutationPolicy: Type.Optional(StringEnum(["required", "forbidden"] as const, { description: "Required demands a final diff; forbidden allows exact verification, rejects source mutation, and requires a zero task delta." })),
|
|
65
51
|
maxAttempts: Type.Optional(Type.Number({ minimum: 1, maximum: 10 })),
|
|
66
52
|
expectedOutput: Type.String({ minLength: 10 }),
|
|
67
53
|
acceptanceCriteria: Type.Array(Type.String({ minLength: 1 }), { minItems: 1, maxItems: 12 }),
|
|
68
54
|
scope: Type.Array(Type.String({
|
|
69
55
|
minLength: 1,
|
|
70
56
|
description: "Project-relative path or glob only (for example src/file.ts, src/**, or test/**); do not use prose."
|
|
71
|
-
}), { minItems: 1 }),
|
|
57
|
+
}), { minItems: 1, maxItems: 2000 }),
|
|
72
58
|
outOfScope: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
|
|
73
59
|
reviewLenses: Type.Optional(Type.Array(StringEnum(REVIEW_LENSES))),
|
|
74
60
|
workPlan: Type.Optional(Type.Array(Type.Object({
|
|
@@ -95,6 +81,12 @@ export function registerTaskStartTool(pi: ExtensionAPI, deps: Record<string, any
|
|
|
95
81
|
const sessionId = ctx.sessionManager.getSessionId();
|
|
96
82
|
const sessionName = currentSessionName(ctx);
|
|
97
83
|
const active = activeSessionTask(ctx.cwd, sessionId) as TaskContract | undefined;
|
|
84
|
+
if (active && active.trace.outcome !== "pending" && runtimeState.taskIdentity(ctx)?.taskRunId === active.taskRunId) {
|
|
85
|
+
runtimeState.clearTaskBoundary(ctx, active.taskRunId);
|
|
86
|
+
} else if (!active) {
|
|
87
|
+
const cachedTask = runtimeState.taskIdentity(ctx);
|
|
88
|
+
if (cachedTask) runtimeState.clearTaskBoundary(ctx, cachedTask.taskRunId);
|
|
89
|
+
}
|
|
98
90
|
const activeAuthorityReplacement = active ? authorityReplacementState(ctx.cwd, active) : undefined;
|
|
99
91
|
if (activeAuthorityReplacement && !activeAuthorityReplacement.enforcementSafe) {
|
|
100
92
|
return {
|
|
@@ -246,6 +238,8 @@ export function registerTaskStartTool(pi: ExtensionAPI, deps: Record<string, any
|
|
|
246
238
|
}
|
|
247
239
|
const orchestration = resolveOrchestrationPolicy(profile, policy);
|
|
248
240
|
const changeMode = params.changeMode === "read-only" ? "read-only" : "source-change";
|
|
241
|
+
const mutationPolicy = changeMode === "read-only" || params.mutationPolicy === "forbidden" ? "forbidden" : "required";
|
|
242
|
+
if (changeMode === "read-only" && params.mutationPolicy === "required") return { content: [{ type: "text", text: "Task start refused: read-only tasks cannot require source mutation." }], isError: true };
|
|
249
243
|
if (changeMode === "source-change" && !hasGitEvidenceRoot(ctx.cwd)) {
|
|
250
244
|
return {
|
|
251
245
|
content: [{ type: "text", text: "Task start refused: source-change tasks require a Git working tree, or a workspace parent with direct child Git repositories, so changed-file evidence cannot silently disappear. Initialize Git, open the parent that contains the repos, or use read-only mode." }],
|
|
@@ -263,7 +257,7 @@ export function registerTaskStartTool(pi: ExtensionAPI, deps: Record<string, any
|
|
|
263
257
|
const reviewLenses = normalizeReviewLenses(params.reviewLenses, orchestration.defaultReviewLenses);
|
|
264
258
|
const providedWorkPlan = normalizeWorkPlanSteps(params.workPlan);
|
|
265
259
|
const defaultPlanLane = params.intakeMode === "runtime" && params.riskLane === "normal" ? "tiny" : params.riskLane;
|
|
266
|
-
const workPlan = providedWorkPlan.length ? providedWorkPlan : defaultWorkPlan(safeSummary, defaultPlanLane, changeMode);
|
|
260
|
+
const workPlan = providedWorkPlan.length ? providedWorkPlan : defaultWorkPlan(safeSummary, defaultPlanLane, changeMode, mutationPolicy);
|
|
267
261
|
const workPlanError = validateNewWorkPlan(workPlan);
|
|
268
262
|
if (workPlanError) {
|
|
269
263
|
return { content: [{ type: "text", text: `Task start refused: ${workPlanError}.` }], details: workPlan, isError: true };
|
|
@@ -298,7 +292,11 @@ export function registerTaskStartTool(pi: ExtensionAPI, deps: Record<string, any
|
|
|
298
292
|
});
|
|
299
293
|
const criterionGraph = compileCriterionGraph({ acceptanceCriteria: acceptance.acceptanceCriteria, scope: resolvedScope,
|
|
300
294
|
verifyCommands: verifyPlan.commands, changeMode, mode: criterionGraphMode(), createdAt });
|
|
301
|
-
const
|
|
295
|
+
const projectManifest = repositoryFileManifestDetails(ctx.cwd), projectFiles = projectManifest.files;
|
|
296
|
+
const maxManifestFiles = contextBudgetConfig(policy).maxManifestFiles;
|
|
297
|
+
const plannedSelection = criterionGraphContextSelectionDetails(criterionGraph, projectFiles, [], maxManifestFiles, projectManifest.complete, resolvedScope);
|
|
298
|
+
const plannedContext = plannedSelection.entries;
|
|
299
|
+
const observedContext = criterionGraphContextSelection(undefined, [], runtimeState.preTaskContext(ctx), maxManifestFiles);
|
|
302
300
|
const task: TaskContract = {
|
|
303
301
|
schemaVersion: 2,
|
|
304
302
|
taskRunId,
|
|
@@ -306,6 +304,7 @@ export function registerTaskStartTool(pi: ExtensionAPI, deps: Record<string, any
|
|
|
306
304
|
sessionId,
|
|
307
305
|
sessionName,
|
|
308
306
|
changeMode,
|
|
307
|
+
mutationPolicy,
|
|
309
308
|
attempt,
|
|
310
309
|
maxAttempts,
|
|
311
310
|
previousAttempts: priorAttempts.filter((task) => task.trace.outcome !== "pending").slice(0, 10).reverse().map(summarizeAttempt),
|
|
@@ -319,7 +318,7 @@ export function registerTaskStartTool(pi: ExtensionAPI, deps: Record<string, any
|
|
|
319
318
|
outOfScope: redactTextArray(params.outOfScope),
|
|
320
319
|
protectedPaths: profile.protectedPaths ?? [],
|
|
321
320
|
requiredContext: profile.requiredContext ?? [],
|
|
322
|
-
contextManifest:
|
|
321
|
+
contextManifest: observedContext,
|
|
323
322
|
memoryCitations: [],
|
|
324
323
|
mcpCapabilities: profile.mcpCapabilities ?? [],
|
|
325
324
|
verifyGroup: verifyPlan.group,
|
|
@@ -347,16 +346,20 @@ export function registerTaskStartTool(pi: ExtensionAPI, deps: Record<string, any
|
|
|
347
346
|
createdAt,
|
|
348
347
|
updatedAt: createdAt
|
|
349
348
|
};
|
|
350
|
-
if (
|
|
349
|
+
if (mutationPolicy === "forbidden" && observedContext.length > 0) {
|
|
351
350
|
applyRuntimeLifecycleObservation(task, "context-complete", createdAt);
|
|
352
351
|
}
|
|
353
352
|
const written = writeTask(ctx.cwd, task);
|
|
354
|
-
|
|
353
|
+
runtimeState.promotePreTaskContext(ctx, written.taskRunId, observedContext);
|
|
354
|
+
const baselineGuidance = changeMode === "source-change" && mutationPolicy === "required"
|
|
355
355
|
? acceptanceBaselineGuidance(written, { cwd: ctx.cwd })
|
|
356
356
|
: [];
|
|
357
|
-
const exactOutputGuidance =
|
|
357
|
+
const exactOutputGuidance = mutationPolicy === "forbidden"
|
|
358
358
|
? exactFinalOutputGuidance(written.summary)
|
|
359
359
|
: [];
|
|
360
|
+
const criticalProof = taskCriticalProofSection(
|
|
361
|
+
written, plannedContext, projectFiles, acceptanceProofGuidance, isAcceptanceTestPath, acceptanceLanguageAdapterForPath
|
|
362
|
+
);
|
|
360
363
|
bindSessionTask(ctx.cwd, sessionId, sessionName, written);
|
|
361
364
|
runtimeState.cacheTaskIdentity(ctx, written);
|
|
362
365
|
if (written.intakeMode !== "runtime") {
|
|
@@ -367,8 +370,9 @@ export function registerTaskStartTool(pi: ExtensionAPI, deps: Record<string, any
|
|
|
367
370
|
}
|
|
368
371
|
const lifecycleMode = runtimeLifecycleMode(written);
|
|
369
372
|
const scopeMappings = scopeResolution.mappings.map((item) => `${item.from} -> ${item.to}`);
|
|
370
|
-
appendTrace(ctx.cwd, { taskId, taskRunId, sessionId, sessionName, attempt, event: "task_start", summary: task.summary, riskLane: params.riskLane, intakeMode: task.intakeMode, changeMode: task.changeMode, lifecycleMode, criterionGraphMode: written.criterionGraph.mode, criterionGraphDigest: written.criterionGraph.graphDigest, authorityProfile: written.authoritySnapshot.profile, authoritySnapshotDigest: written.authoritySnapshot.snapshotDigest, scopeMappings,
|
|
371
|
-
appendSessionTrace(pi, { taskId, taskRunId, sessionId, sessionName, attempt, event: "task_start", summary: task.summary, riskLane: params.riskLane, intakeMode: task.intakeMode, changeMode: task.changeMode, lifecycleMode, criterionGraphMode: written.criterionGraph.mode, criterionGraphDigest: written.criterionGraph.graphDigest, authorityProfile: written.authoritySnapshot.profile, authoritySnapshotDigest: written.authoritySnapshot.snapshotDigest, scopeMappings,
|
|
373
|
+
appendTrace(ctx.cwd, { taskId, taskRunId, sessionId, sessionName, attempt, event: "task_start", turnId: runtimeState.currentTurn(ctx)?.turnId, summary: task.summary, riskLane: params.riskLane, intakeMode: task.intakeMode, changeMode: task.changeMode, mutationPolicy: task.mutationPolicy, lifecycleMode, criterionGraphMode: written.criterionGraph.mode, criterionGraphDigest: written.criterionGraph.graphDigest, authorityProfile: written.authoritySnapshot.profile, authoritySnapshotDigest: written.authoritySnapshot.snapshotDigest, scopeMappings, plannedContext: plannedContext.map((item) => item.path), plannedContextComplete: plannedSelection.complete, plannedContextCandidateCount: plannedSelection.candidateCount, observedContext: observedContext.map((item) => item.path) });
|
|
374
|
+
appendSessionTrace(pi, { taskId, taskRunId, sessionId, sessionName, attempt, event: "task_start", turnId: runtimeState.currentTurn(ctx)?.turnId, summary: task.summary, riskLane: params.riskLane, intakeMode: task.intakeMode, changeMode: task.changeMode, mutationPolicy: task.mutationPolicy, lifecycleMode, criterionGraphMode: written.criterionGraph.mode, criterionGraphDigest: written.criterionGraph.graphDigest, authorityProfile: written.authoritySnapshot.profile, authoritySnapshotDigest: written.authoritySnapshot.snapshotDigest, scopeMappings, plannedContext: plannedContext.map((item) => item.path), plannedContextComplete: plannedSelection.complete, plannedContextCandidateCount: plannedSelection.candidateCount, observedContext: observedContext.map((item) => item.path) });
|
|
375
|
+
telemetry(ctx, { event: "turn_task_bound", turnId: runtimeState.currentTurn(ctx)?.turnId, taskRunId });
|
|
372
376
|
recordTaskStartCheckpoint(ctx, written, firstReady.id, lifecycleMode);
|
|
373
377
|
return {
|
|
374
378
|
content: [{
|
|
@@ -379,6 +383,9 @@ export function registerTaskStartTool(pi: ExtensionAPI, deps: Record<string, any
|
|
|
379
383
|
written.verifyCommands.length > 0
|
|
380
384
|
? ["Exact verifier commands:", ...verifierCommandInstructions(written.verifyCommands)].join("\n")
|
|
381
385
|
: "Verify: none (read-only).",
|
|
386
|
+
...(written.verifyCommands.length > 0
|
|
387
|
+
? [EXACT_VERIFIER_EXECUTION_GUIDANCE]
|
|
388
|
+
: []),
|
|
382
389
|
written.acceptanceCriteria.length > 0
|
|
383
390
|
? ["Acceptance focus:", ...written.acceptanceCriteria.map((criterion, index) => `${index + 1}. ${criterion}`)].join("\n")
|
|
384
391
|
: "Acceptance focus: not recorded.",
|
|
@@ -386,9 +393,7 @@ export function registerTaskStartTool(pi: ExtensionAPI, deps: Record<string, any
|
|
|
386
393
|
? [["Execution map (planning only; verifier remains authoritative):", ...criterionGraphGuidance(written.criterionGraph).map((line: string) => `- ${line}`)].join("\n")]
|
|
387
394
|
: []),
|
|
388
395
|
...exactOutputGuidance,
|
|
389
|
-
...
|
|
390
|
-
? [["Critical behavioral proof:", ...acceptanceProofGuidance(written).map((item: string) => `- ${item}`), "Map every proof item above to a live focused assertion or explicit test matrix before the verifier; happy-path coverage and prose claims are insufficient."].join("\n")]
|
|
391
|
-
: []),
|
|
396
|
+
...criticalProof,
|
|
392
397
|
...(baselineGuidance.length > 0
|
|
393
398
|
? [["Existing public contract:", ...baselineGuidance.map((item: string) => `- ${item}`)].join("\n")]
|
|
394
399
|
: []),
|
|
@@ -396,6 +401,7 @@ export function registerTaskStartTool(pi: ExtensionAPI, deps: Record<string, any
|
|
|
396
401
|
? "Runtime records targeted reads and final completion automatically. Stay read-only and report cited evidence."
|
|
397
402
|
: lifecycleMode === "assisted-readonly"
|
|
398
403
|
? "Runtime records read-only evidence automatically; complete only the explicit evidence-review step before handoff."
|
|
404
|
+
: mutationPolicy === "forbidden" ? "Runtime permits bounded inspection and exact configured verifier commands only. Source mutation is blocked, and completion requires a zero task delta."
|
|
399
405
|
: lifecycleMode === "automatic"
|
|
400
406
|
? "Runtime will record reads, changes, exact verifier results, and final completion automatically. Continue with ordinary read/edit/bash work."
|
|
401
407
|
: lifecycleMode === "assisted"
|
|
@@ -408,19 +414,19 @@ export function registerTaskStartTool(pi: ExtensionAPI, deps: Record<string, any
|
|
|
408
414
|
}
|
|
409
415
|
};
|
|
410
416
|
registerRuntimeTool(pi, taskStartTool);
|
|
411
|
-
async function maybeStartAutomaticTask(prompt: string, ctx: ExtensionContext): Promise<{ started: boolean; text: string; task?: TaskContract } | undefined> {
|
|
417
|
+
async function maybeStartAutomaticTask(prompt: string, ctx: ExtensionContext): Promise<{ started: boolean; text: string; task?: TaskContract; plannedContext?: Array<{ path: string; reason: string }>; plannedContextComplete?: boolean } | undefined> {
|
|
412
418
|
const profile = loadProfileFromContext(ctx);
|
|
413
419
|
const readProtectedPaths = effectiveProtectedPaths(policy, profile).readProtectedPaths;
|
|
414
420
|
const intakeMode = automaticTaskIntakeMode(prompt, readProtectedPaths);
|
|
415
421
|
if (!intakeMode) return undefined;
|
|
416
|
-
const active = activeSessionTask(ctx.cwd, ctx.sessionManager.getSessionId()) as TaskContract | undefined;
|
|
422
|
+
const mutationPolicy = automaticTaskMutationPolicy(prompt, intakeMode), active = activeSessionTask(ctx.cwd, ctx.sessionManager.getSessionId()) as TaskContract | undefined;
|
|
417
423
|
if (active?.trace.outcome === "pending") return undefined;
|
|
418
424
|
const summary = redactText(automaticTaskSummary(prompt));
|
|
419
425
|
const sessionName = currentSessionName(ctx);
|
|
420
|
-
const
|
|
426
|
+
const projectManifest = repositoryFileManifestDetails(ctx.cwd), projectFiles = projectManifest.files;
|
|
421
427
|
const scope = intakeMode === "read-only"
|
|
422
|
-
? automaticReadOnlyTaskScope(prompt, runtimeState.
|
|
423
|
-
: automaticTaskScope(prompt, runtimeState.
|
|
428
|
+
? automaticReadOnlyTaskScope(prompt, runtimeState.preTaskContext(ctx))
|
|
429
|
+
: automaticTaskScope(prompt, runtimeState.preTaskContext(ctx), projectFiles);
|
|
424
430
|
const started = await taskStartTool.execute(
|
|
425
431
|
`runtime-intake-${ctx.sessionManager.getSessionId()}`,
|
|
426
432
|
{
|
|
@@ -429,10 +435,10 @@ export function registerTaskStartTool(pi: ExtensionAPI, deps: Record<string, any
|
|
|
429
435
|
riskLane: automaticTaskRiskLane(prompt),
|
|
430
436
|
intakeMode: "runtime",
|
|
431
437
|
changeMode: intakeMode,
|
|
432
|
-
|
|
433
|
-
|
|
438
|
+
mutationPolicy,
|
|
439
|
+
expectedOutput: mutationPolicy === "forbidden" ? "The requested read-only investigation is answered from observed project evidence without mutating files."
|
|
434
440
|
: "The requested bounded change is implemented and passes the configured verification.",
|
|
435
|
-
acceptanceCriteria: automaticAcceptanceCriteria(prompt, intakeMode),
|
|
441
|
+
acceptanceCriteria: automaticAcceptanceCriteria(prompt, mutationPolicy === "forbidden" ? "read-only" : intakeMode),
|
|
436
442
|
scope,
|
|
437
443
|
outOfScope: ["Unrelated files and behavior outside the operator request."],
|
|
438
444
|
reviewLenses: automaticReviewLenses(prompt)
|
|
@@ -458,22 +464,25 @@ export function registerTaskStartTool(pi: ExtensionAPI, deps: Record<string, any
|
|
|
458
464
|
};
|
|
459
465
|
}
|
|
460
466
|
const assurance = taskPerformanceAssurance(task);
|
|
461
|
-
const
|
|
467
|
+
const plannedSelection = criterionGraphContextSelectionDetails(task.criterionGraph, projectFiles, [], contextBudgetConfig(policy).maxManifestFiles, projectManifest.complete, task.scope);
|
|
468
|
+
const plannedContext = plannedSelection.entries;
|
|
469
|
+
const baselineGuidance = task.changeMode === "source-change" && task.mutationPolicy !== "forbidden"
|
|
462
470
|
? acceptanceBaselineGuidance(task, { cwd: ctx.cwd })
|
|
463
471
|
: [];
|
|
464
|
-
const exactOutputGuidance = task.changeMode === "read-only"
|
|
465
|
-
|
|
466
|
-
|
|
472
|
+
const exactOutputGuidance = task.changeMode === "read-only" || task.mutationPolicy === "forbidden" ? exactFinalOutputGuidance(task.summary) : [];
|
|
473
|
+
const criticalProof = taskCriticalProofSection(
|
|
474
|
+
task, plannedContext, projectFiles, acceptanceProofGuidance, isAcceptanceTestPath, acceptanceLanguageAdapterForPath
|
|
475
|
+
);
|
|
467
476
|
return {
|
|
468
477
|
started: true,
|
|
469
478
|
task,
|
|
479
|
+
plannedContext,
|
|
480
|
+
plannedContextComplete: plannedSelection.complete,
|
|
470
481
|
text: boundedRuntimeIntakeMessage([
|
|
471
482
|
`Piagent runtime task: ${task.taskId}; scope: ${task.scope.join(", ")}.`,
|
|
472
483
|
"The complete operator request above is the authoritative acceptance contract; runtime keeps its full criteria, so do not restate or re-scout it.",
|
|
473
484
|
`Assurance: ${assurance.tier} (${assurance.reasonCodes.join(", ") || "bounded-runtime"}).`,
|
|
474
|
-
...
|
|
475
|
-
? [["Critical behavioral proof:", ...acceptanceProofGuidance(task).map((item: string) => `- ${item}`), "Map every proof item above to a live focused assertion or explicit test matrix before the verifier; happy-path coverage is insufficient."].join("\n")]
|
|
476
|
-
: []),
|
|
485
|
+
...criticalProof,
|
|
477
486
|
...(baselineGuidance.length > 0
|
|
478
487
|
? [["Existing public contract:", ...baselineGuidance.map((item: string) => `- ${item}`)].join("\n")]
|
|
479
488
|
: []),
|
|
@@ -481,15 +490,11 @@ export function registerTaskStartTool(pi: ExtensionAPI, deps: Record<string, any
|
|
|
481
490
|
"Exact verifier commands:",
|
|
482
491
|
...(task.verifyCommands.length > 0 ? verifierCommandInstructions(task.verifyCommands) : ["none"]),
|
|
483
492
|
...(criterionGraphGuidance(task.criterionGraph).length > 0 ? [["Execution map (planning only):", ...criterionGraphGuidance(task.criterionGraph).map((line: string) => `- ${line}`)].join("\n")] : []),
|
|
493
|
+
RUNTIME_SOURCE_REUSE_GUIDANCE,
|
|
484
494
|
"Root project instructions are loaded. Do not re-read root AGENTS.md or inspect Piagent/platform files; work directly in relevant source/tests with ordinary tools.",
|
|
485
|
-
task
|
|
486
|
-
? "Stay read-only. Runtime records targeted reads and completion evidence; do not call task-management tools."
|
|
487
|
-
: task.criterionGraph?.mode === "criterion-graph"
|
|
488
|
-
? "Follow the execution map: batch context reads by target, implement dependency-ready criteria, then run the exact verifier once; rerun only after a later mutation. The map plans work but never overrides the operator request or verifier."
|
|
489
|
-
: "Before mutating, privately map every operator criterion to implementation and focused-test coverage; batch independent reads or writes. Finish intended edits and one criterion-by-criterion self-review, then run the exact verifier once; rerun only after a later mutation. Runtime records evidence and completion; do not call task-management tools."
|
|
495
|
+
automaticTaskExecutionGuidance(task)
|
|
490
496
|
].join("\n"))
|
|
491
497
|
};
|
|
492
498
|
}
|
|
493
|
-
|
|
494
499
|
return maybeStartAutomaticTask;
|
|
495
500
|
}
|