@piagent/platform 1.5.5 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +166 -0
- package/README.md +25 -21
- package/README.vi.md +34 -5
- package/adapters/web-frontend/profile.json +2 -4
- package/architecture/layers.json +18 -3
- package/benchmarks/deep-logic-v1/SPECIALIST_PROTOCOL.vi.md +122 -0
- package/benchmarks/deep-logic-v1/grade.mjs +272 -10
- package/benchmarks/deep-logic-v1/project/apps/admin/src/billing-summary.js +3 -0
- package/benchmarks/deep-logic-v1/project/benchmark-contract/public-smoke.test.js +166 -0
- package/benchmarks/deep-logic-v1/project/package.json +1 -1
- package/benchmarks/deep-logic-v1/project/packages/billing/src/close-period.js +3 -0
- package/benchmarks/deep-logic-v1/project/packages/billing/src/plan-timeline.js +3 -0
- package/benchmarks/deep-logic-v1/project/test/smoke.test.js +7 -1
- package/benchmarks/deep-logic-v1/prompts/budgeted-context-graph.md +2 -0
- package/benchmarks/deep-logic-v1/prompts/fair-dependency-scheduler.md +2 -0
- package/benchmarks/deep-logic-v1/prompts/layered-policy-resolution.md +2 -0
- package/benchmarks/deep-logic-v1/prompts/resumable-stream-assembly.md +3 -1
- package/benchmarks/deep-logic-v1/prompts/revision-event-reconciliation.md +2 -0
- package/benchmarks/deep-logic-v1/prompts/temporal-usage-billing-close.md +123 -0
- package/benchmarks/deep-logic-v1/prompts/transactional-config-merge.md +2 -0
- package/benchmarks/deep-logic-v1/suite.json +51 -3
- package/benchmarks/deep-logic-v1/variant.mjs +22 -1
- package/benchmarks/production-v1/spend-control.v1.json +92 -0
- package/benchmarks/production-v1/suite.json +46 -3
- package/catalog/capabilities.json +18 -18
- package/docs/authority-manifest.md +1 -1
- package/docs/auto-delegation-policy.md +38 -73
- package/docs/command-reference-vietnamese.md +27 -35
- package/docs/context-engine.md +12 -4
- package/docs/context-window-policy.md +37 -1
- package/docs/distribution-standard.md +7 -7
- package/docs/en/architecture.md +27 -13
- package/docs/languages.json +4 -4
- package/docs/model-options.md +8 -5
- package/docs/operator-manual-vietnamese.md +27 -45
- package/docs/project-adapters.md +2 -2
- package/docs/publishing-for-teams.md +10 -0
- package/docs/quality-benchmark.md +427 -62
- package/docs/release-install-policy.md +10 -9
- package/docs/runtime-harness-standard.md +3 -3
- package/docs/runtime-policy-design.md +4 -4
- package/docs/runtime-quality-baseline.md +13 -6
- package/docs/security-threat-model.md +2 -2
- package/docs/subagent-orchestration-capabilities.md +19 -51
- package/docs/subagents-and-multiagent.md +21 -47
- package/docs/task-implementation-contract.md +3 -3
- package/docs/task-lifecycle-vietnamese.md +3 -3
- package/docs/team-onboarding.md +2 -2
- package/docs/usage-observability.md +7 -2
- package/docs/vi/architecture.md +27 -13
- package/docs-site/architecture.html +1 -1
- package/docs-site/benchmark.html +89 -95
- package/docs-site/commands.html +1 -1
- package/docs-site/content/benchmark.html +88 -94
- package/docs-site/content/en/benchmark.html +134 -90
- package/docs-site/content/en/index.html +19 -19
- package/docs-site/content/en/runtime.html +4 -4
- package/docs-site/content/en/whats-new.html +12 -1
- package/docs-site/content/en/workflows.html +2 -2
- package/docs-site/content/index.html +19 -19
- package/docs-site/content/runtime.html +4 -4
- package/docs-site/content/whats-new.html +12 -1
- package/docs-site/content/workflows.html +2 -2
- package/docs-site/context-engine.html +1 -1
- package/docs-site/en/architecture.html +1 -1
- package/docs-site/en/benchmark.html +135 -91
- package/docs-site/en/commands.html +1 -1
- package/docs-site/en/context-engine.html +1 -1
- package/docs-site/en/index.html +20 -20
- package/docs-site/en/mcp-approval.html +1 -1
- package/docs-site/en/mcp-auth.html +1 -1
- package/docs-site/en/mcp-servers.html +1 -1
- package/docs-site/en/mcp.html +1 -1
- package/docs-site/en/permissions.html +1 -1
- package/docs-site/en/profiles.html +1 -1
- package/docs-site/en/quickstart.html +1 -1
- package/docs-site/en/runtime.html +5 -5
- package/docs-site/en/scope.html +1 -1
- package/docs-site/en/security.html +1 -1
- package/docs-site/en/team.html +1 -1
- package/docs-site/en/whats-new.html +13 -2
- package/docs-site/en/workflows.html +3 -3
- package/docs-site/index.html +20 -20
- package/docs-site/mcp-approval.html +1 -1
- package/docs-site/mcp-auth.html +1 -1
- package/docs-site/mcp-servers.html +1 -1
- package/docs-site/mcp.html +1 -1
- package/docs-site/permissions.html +1 -1
- package/docs-site/profiles.html +1 -1
- package/docs-site/quickstart.html +1 -1
- package/docs-site/runtime.html +5 -5
- package/docs-site/scope.html +1 -1
- package/docs-site/security.html +1 -1
- package/docs-site/team.html +1 -1
- package/docs-site/whats-new.html +13 -2
- package/docs-site/workflows.html +3 -3
- package/evals/fixtures/benchmark-assurance-evidence.invalid.json +1 -1
- package/evals/fixtures/benchmark-assurance-evidence.valid.json +1 -1
- package/evals/fixtures/codex-relative-efficiency-v1.invalid.json +4 -0
- package/evals/fixtures/codex-relative-efficiency-v1.valid.json +94 -0
- package/evals/fixtures/helper-request.invalid.json +1 -1
- package/evals/fixtures/helper-request.valid.json +1 -1
- package/evals/fixtures/piagent-webui/session-live-state-v1.invalid.json +18 -0
- package/evals/fixtures/piagent-webui/session-live-state-v1.valid.json +27 -0
- package/evals/fixtures/piagent-webui/subagent-tree-v1.valid.json +7 -0
- package/evals/fixtures/task-authority-snapshot.valid.json +4 -4
- package/evals/fs4-readiness-matrix.v1.json +2 -2
- package/evals/fs5-causal-arm.v1.json +3 -3
- package/evals/fs5-pilot-protocol.v1.json +3 -3
- package/evals/fs5-pilot-protocol.v2.json +8 -8
- package/evals/fs5-pilot-protocol.v3.json +8 -8
- package/evals/fs5-pilot-protocol.v4.json +7 -6
- package/evals/fs5-pilot-protocol.v5.json +9 -7
- package/evals/private-holdout-v1/public-exposure.v1.json +1 -1
- package/evals/runtime-conformance-v1/README.md +43 -0
- package/evals/runtime-conformance-v1/evaluate-paid-canary.mjs +198 -0
- package/evals/runtime-conformance-v1/lane.json +57 -0
- package/evals/runtime-conformance-v1/paid-canary.json +31 -0
- package/evals/runtime-conformance-v1/runner.mjs +456 -0
- package/package.json +3 -1
- package/packages/piagent-core/README.md +13 -4
- package/packages/piagent-core/benchmark/benchmark-assurance.js +4 -1
- package/packages/piagent-core/benchmark/benchmark-bootstrap.js +91 -9
- package/packages/piagent-core/benchmark/benchmark-claim-restrictions.js +38 -7
- package/packages/piagent-core/benchmark/benchmark-cli.js +7 -7
- package/packages/piagent-core/benchmark/benchmark-codex-relative-efficiency.js +698 -0
- package/packages/piagent-core/benchmark/benchmark-codex.js +3 -1
- package/packages/piagent-core/benchmark/benchmark-comparison.js +271 -2
- package/packages/piagent-core/benchmark/benchmark-core.js +736 -664
- package/packages/piagent-core/benchmark/benchmark-execution-guard.js +7 -5
- package/packages/piagent-core/benchmark/benchmark-forensics.js +441 -4
- package/packages/piagent-core/benchmark/benchmark-host-readiness.js +639 -0
- package/packages/piagent-core/benchmark/benchmark-matrix.js +22 -4
- package/packages/piagent-core/benchmark/benchmark-normalized-cost.js +317 -0
- package/packages/piagent-core/benchmark/benchmark-preflight.js +5 -1
- package/packages/piagent-core/benchmark/benchmark-process.js +6 -5
- package/packages/piagent-core/benchmark/benchmark-provider-free-evidence.js +345 -0
- package/packages/piagent-core/benchmark/benchmark-provider-wire.js +126 -0
- package/packages/piagent-core/benchmark/benchmark-record-validation.js +252 -0
- package/packages/piagent-core/benchmark/benchmark-report.js +130 -12
- package/packages/piagent-core/benchmark/benchmark-resume-recovery.js +14 -0
- package/packages/piagent-core/benchmark/benchmark-runner-policy.js +26 -0
- package/packages/piagent-core/benchmark/benchmark-runtime-causal.js +244 -0
- package/packages/piagent-core/benchmark/benchmark-stage-diagnostic.js +979 -0
- package/packages/piagent-core/benchmark/benchmark-statistics.js +64 -0
- package/packages/piagent-core/benchmark/benchmark-stop-policy.js +8 -5
- package/packages/piagent-core/benchmark/benchmark-suite-identity.js +31 -0
- package/packages/piagent-core/benchmark/benchmark-suite-runtime.js +13 -9
- package/packages/piagent-core/benchmark/benchmark-suite.js +149 -2
- package/packages/piagent-core/benchmark/benchmark-summary-support.js +380 -0
- package/packages/piagent-core/benchmark/benchmark-timing-diagnostics.js +971 -0
- package/packages/piagent-core/benchmark/benchmark-usage.js +322 -0
- package/packages/piagent-core/benchmark/benchmark-workflow.js +166 -0
- package/packages/piagent-core/capabilities/runtime-integrity.js +3 -0
- package/packages/piagent-core/docs/context-efficiency-improvement-plan.md +1080 -0
- package/packages/piagent-core/extensions/acceptance-behavior-proof.js +806 -0
- package/packages/piagent-core/extensions/acceptance-contract-semantics.js +83 -69
- package/packages/piagent-core/extensions/acceptance-error-classes.js +160 -0
- package/packages/piagent-core/extensions/acceptance-executable-evidence.js +12 -9
- package/packages/piagent-core/extensions/acceptance-receipt.js +123 -59
- package/packages/piagent-core/extensions/acceptance-state-machine-evidence.js +518 -0
- package/packages/piagent-core/extensions/acceptance-test-binding-evidence.js +60 -0
- package/packages/piagent-core/extensions/context-efficiency-metrics.js +302 -0
- package/packages/piagent-core/extensions/context-efficiency-report.js +167 -0
- package/packages/piagent-core/extensions/context-engine.js +149 -156
- package/packages/piagent-core/extensions/context-evidence.js +48 -0
- package/packages/piagent-core/extensions/context-import-links.js +296 -0
- package/packages/piagent-core/extensions/context-index-policy.js +4 -0
- package/packages/piagent-core/extensions/context-query-paths.js +57 -0
- package/packages/piagent-core/extensions/context-telemetry-integrity.js +65 -0
- package/packages/piagent-core/extensions/criterion-context-pack.js +435 -7
- package/packages/piagent-core/extensions/criterion-graph.js +82 -9
- package/packages/piagent-core/extensions/fallback-base-policy.ts +101 -0
- package/packages/piagent-core/extensions/guard-types.ts +6 -0
- package/packages/piagent-core/extensions/javascript-regex-evidence.js +32 -0
- package/packages/piagent-core/extensions/piagent-guard.ts +420 -260
- package/packages/piagent-core/extensions/task-contract-view.js +17 -3
- package/packages/piagent-core/extensions/task-lifecycle.js +39 -2
- package/packages/piagent-core/extensions/task-state.js +109 -142
- package/packages/piagent-core/extensions/verification-intelligence.js +52 -13
- package/packages/piagent-core/extensions/workspace-evidence-roots.js +193 -0
- package/packages/piagent-core/package.json +1 -1
- package/packages/piagent-core/policies/base-policy.json +44 -2
- package/packages/piagent-core/policy/authority-manifest.v1.json +3 -3
- package/packages/piagent-core/prompts/be-to-fe.md +2 -8
- package/packages/piagent-core/prompts/plan.md +1 -1
- package/packages/piagent-core/prompts/platform-improve.md +2 -6
- package/packages/piagent-core/prompts/review.md +2 -7
- package/packages/piagent-core/prompts/scout.md +1 -1
- package/packages/piagent-core/prompts/task.md +4 -3
- package/packages/piagent-core/runtime/context/context-delivery.ts +103 -0
- package/packages/piagent-core/runtime/context/context-delta-shadow.ts +53 -0
- package/packages/piagent-core/runtime/context/context-efficiency-format.ts +47 -0
- package/packages/piagent-core/runtime/context/context-evidence-qualification.ts +32 -0
- package/packages/piagent-core/runtime/context/prefix-telemetry.ts +47 -0
- package/packages/piagent-core/runtime/context/repository-memory-hints.ts +27 -0
- package/packages/piagent-core/runtime/hooks/agent-prompt-telemetry.ts +56 -0
- package/packages/piagent-core/runtime/hooks/agent-start-hook.ts +162 -102
- package/packages/piagent-core/runtime/hooks/completion-hook.ts +12 -12
- package/packages/piagent-core/runtime/hooks/input-hook.ts +17 -1
- package/packages/piagent-core/runtime/hooks/session-hooks.ts +22 -2
- package/packages/piagent-core/runtime/hooks/session-start-hook.ts +107 -3
- package/packages/piagent-core/runtime/hooks/tool-result-content-guards.ts +95 -0
- package/packages/piagent-core/runtime/hooks/tool-result-hook.ts +134 -166
- package/packages/piagent-core/runtime/hooks/tool-result-value-helpers.ts +66 -0
- package/packages/piagent-core/runtime/inspection/activity-event-adapter.ts +4 -2
- package/packages/piagent-core/runtime/inspection/activity-recovery.ts +104 -0
- package/packages/piagent-core/runtime/inspection/bounded-emission-guard.ts +286 -0
- package/packages/piagent-core/runtime/inspection/runtime-event-store.ts +37 -7
- package/packages/piagent-core/runtime/inspection/task-compaction-history.ts +1 -1
- package/packages/piagent-core/runtime/inspection/task-handoff-history.ts +1 -1
- package/packages/piagent-core/runtime/inspection/task-subagent-tree.ts +27 -4
- package/packages/piagent-core/runtime/inspection/tool-failure-classification.ts +70 -0
- package/packages/piagent-core/runtime/inspection/webui-snapshot.ts +73 -9
- package/packages/piagent-core/runtime/model/authenticated-catalog.ts +17 -9
- package/packages/piagent-core/runtime/model/openai-codex-reasoning.ts +83 -0
- package/packages/piagent-core/runtime/model/provider-wire-fingerprint.ts +238 -0
- package/packages/piagent-core/runtime/orchestration/helper-lifecycle.ts +16 -12
- package/packages/piagent-core/runtime/orchestration/minimal-delegation-policy.ts +71 -0
- package/packages/piagent-core/runtime/orchestration/owned-work-budget.ts +28 -20
- package/packages/piagent-core/runtime/orchestration/role-policy.ts +27 -9
- package/packages/piagent-core/runtime/orchestration/subagent-tool-policy.ts +329 -0
- package/packages/piagent-core/runtime/policy/block-remedy.ts +0 -4
- package/packages/piagent-core/runtime/product/activity-inspector.ts +7 -2
- package/packages/piagent-core/runtime/product/efficiency-metrics.ts +7 -2
- package/packages/piagent-core/runtime/quality/edit-freshness-guard.ts +102 -0
- package/packages/piagent-core/runtime/quality/exact-output-contract.ts +22 -11
- package/packages/piagent-core/runtime/quality/performance-assurance-signals.ts +23 -0
- package/packages/piagent-core/runtime/quality/performance-assurance.ts +9 -5
- package/packages/piagent-core/runtime/recovery/continuation-budget.ts +1 -1
- package/packages/piagent-core/runtime/recovery/edit-recovery-context.ts +113 -0
- package/packages/piagent-core/runtime/recovery/edit-recovery-delivery.ts +42 -0
- package/packages/piagent-core/runtime/recovery/handoff-projection.ts +4 -1
- package/packages/piagent-core/runtime/recovery/pristine-task-policy-refresh.ts +91 -0
- package/packages/piagent-core/runtime/recovery/recovery-policy.ts +7 -4
- package/packages/piagent-core/runtime/recovery/resume-state.ts +112 -14
- package/packages/piagent-core/runtime/recovery/semantic-repair-handshake.ts +60 -13
- package/packages/piagent-core/runtime/recovery/semantic-repair-runtime.ts +55 -2
- package/packages/piagent-core/runtime/recovery/task-evidence-coverage-refresh.ts +217 -0
- package/packages/piagent-core/runtime/registration/context-commands.ts +82 -7
- package/packages/piagent-core/runtime/registration/knowledge-tools.ts +9 -5
- package/packages/piagent-core/runtime/registration/operator-request-intake.ts +49 -0
- package/packages/piagent-core/runtime/registration/policy-tools.ts +50 -15
- package/packages/piagent-core/runtime/registration/session-commands.ts +1 -1
- package/packages/piagent-core/runtime/registration/task-completion-tools.ts +61 -25
- package/packages/piagent-core/runtime/registration/task-evidence-tools.ts +21 -12
- package/packages/piagent-core/runtime/registration/task-start-guidance.ts +169 -0
- package/packages/piagent-core/runtime/registration/task-start-manifest.ts +54 -0
- package/packages/piagent-core/runtime/registration/task-start-retry-helpers.ts +37 -0
- package/packages/piagent-core/runtime/registration/task-start-tool.ts +108 -103
- package/packages/piagent-core/runtime/runtime-limits.ts +16 -2
- package/packages/piagent-core/runtime/session/adaptive-context-analysis.ts +461 -0
- package/packages/piagent-core/runtime/session/adaptive-context-governor.ts +464 -0
- package/packages/piagent-core/runtime/session/adaptive-context-ledger.ts +265 -0
- package/packages/piagent-core/runtime/session/message-signals.ts +7 -3
- package/packages/piagent-core/runtime/session/operator-request-carry.ts +44 -0
- package/packages/piagent-core/runtime/session/runtime-state.ts +161 -5
- package/packages/piagent-core/runtime/session/source-checkout-read-grants.ts +35 -0
- package/packages/piagent-core/runtime/session/system-prompt.ts +76 -14
- package/packages/piagent-core/runtime/session/task-contract-presentation.ts +39 -0
- package/packages/piagent-core/runtime/tools/apply-patch-tool.ts +500 -0
- package/packages/piagent-core/runtime/tools/phase-tools.ts +8 -8
- package/packages/piagent-core/runtime/tools/tool-groups.ts +12 -2
- package/packages/piagent-core/runtime/trajectory/trajectory-runtime.ts +2 -1
- package/packages/piagent-core/runtime/verification/exact-verifier-reuse.ts +101 -0
- package/packages/piagent-core/runtime/workflows/runtime-intake-compaction.ts +164 -0
- package/packages/piagent-core/runtime/workflows/task-intake.ts +207 -38
- package/packages/piagent-core/security/sensitive-data.js +13 -218
- package/packages/piagent-core/security/sensitive-project-file.js +42 -0
- package/packages/piagent-core/security/sensitive-source-expression.js +330 -0
- package/packages/piagent-core/security/sensitive-source-formats.js +394 -0
- package/packages/piagent-core/security/sensitive-text.js +234 -0
- package/packages/piagent-core/subagents/piagent-oracle.md +5 -3
- package/packages/piagent-core/subagents/piagent-planner.md +6 -4
- package/packages/piagent-core/subagents/piagent-reviewer.md +5 -3
- package/packages/piagent-core/subagents/piagent-scout.md +4 -2
- package/packages/piagent-core/subagents/piagent-worker.md +7 -5
- package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-rD9WJKxs.js → MarkdownMessage-B4XehTCU.js} +1 -1
- package/packages/piagent-webui/dist/client/assets/index-CgOEyRPG.js +176 -0
- package/packages/piagent-webui/dist/client/index.html +1 -1
- package/packages/piagent-webui/extension/piagent-webui.ts +3 -1
- package/packages/piagent-webui/extension/same-session-bridge.ts +1 -1
- package/packages/piagent-webui/extension/session-stream-adapter.ts +33 -10
- package/packages/piagent-webui/gateway/gateway-events.ts +109 -5
- package/packages/piagent-webui/gateway/gateway-service.ts +3 -0
- package/packages/piagent-webui/gateway/gateway-session-stream.ts +122 -20
- package/packages/piagent-webui/gateway/session-command-controller.ts +18 -3
- package/packages/piagent-webui/gateway/session-effective-options.ts +47 -0
- package/packages/piagent-webui/gateway/session-live-state.ts +21 -0
- package/packages/piagent-webui/gateway/session-operation-lifecycle.ts +237 -0
- package/packages/piagent-webui/gateway/session-operation-retry-control.ts +38 -0
- package/packages/piagent-webui/gateway/session-operation-start.ts +5 -1
- package/packages/piagent-webui/gateway/session-operation-watchdog.ts +165 -0
- package/packages/piagent-webui/gateway/session-runtime-factory.ts +45 -0
- package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +141 -125
- package/packages/piagent-webui/ownership/session-lease-store.ts +5 -2
- package/packages/piagent-webui/server/core-inspection-provider.ts +2 -1
- package/packages/piagent-webui/server/loopback-server.ts +8 -0
- package/packages/piagent-webui/server/transcript-projection.ts +113 -9
- package/packages/piagent-webui/shared/text-visibility.ts +10 -0
- package/schemas/codex-relative-efficiency-v1.schema.json +155 -0
- package/schemas/piagent-webui/catalog-v1.json +9 -0
- package/schemas/piagent-webui/gateway-protocol-v1.schema.json +53 -3
- package/schemas/piagent-webui/runtime-event-v2.schema.json +15 -5
- package/schemas/piagent-webui/session-live-state-v1.schema.json +76 -0
- package/schemas/piagent-webui/subagent-tree-v1.schema.json +11 -1
- package/schemas/project-profile.schema.json +1 -1
- package/schemas/task-contract.schema.json +27 -1
- package/scripts/benchmark-runner-core.mjs +367 -363
- package/scripts/benchmark-runner-finalization.mjs +313 -0
- package/scripts/benchmark-runner-host-readiness.mjs +94 -0
- package/scripts/benchmark-runner-support.mjs +200 -0
- package/scripts/benchmark-session.mjs +95 -41
- package/scripts/configure-model-scope.sh +4 -3
- package/scripts/configure-subagents.sh +42 -34
- package/scripts/helper-evaluation.mjs +2 -2
- package/scripts/install-global.sh +2 -2
- package/scripts/pi-usage-history.mjs +37 -10
- package/scripts/setup.sh +2 -2
- package/scripts/team-doctor.sh +1 -1
- package/scripts/verify-local.sh +10 -1
- package/templates/global/settings.json +2 -1
- package/packages/piagent-webui/dist/client/assets/index-BZRFw1u6.js +0 -174
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
|
|
3
|
+
import type { TaskContract } from "../../extensions/guard-types.ts";
|
|
4
|
+
import { extractShellCommandInput, normalizeShellCommandForPolicy } from "../../extensions/guard-shell-analysis.ts";
|
|
5
|
+
import { commandMatchesVerifyPlan } from "../../extensions/runtime-evidence.js";
|
|
6
|
+
import { passingVerifyCommandsForDigest } from "../../extensions/task-contract-view.js";
|
|
7
|
+
import {
|
|
8
|
+
workingTreeSnapshot,
|
|
9
|
+
workingTreeSnapshotHasUnavailableEvidence
|
|
10
|
+
} from "../../extensions/task-state.js";
|
|
11
|
+
import { workingTreeEvidenceDigest } from "../../extensions/task-lifecycle.js";
|
|
12
|
+
|
|
13
|
+
const SHELL_TOOLS = new Set(["bash", "shell", "exec"]);
|
|
14
|
+
const REUSE_COMMAND = "printf '%s\\n' 'Piagent reused exact verifier evidence for the unchanged working tree; the verifier was not rerun.'";
|
|
15
|
+
|
|
16
|
+
export type ExactVerifierReuseDecision = {
|
|
17
|
+
reused: boolean;
|
|
18
|
+
reasonCode: string;
|
|
19
|
+
commandDigest: string | null;
|
|
20
|
+
workingTreeDigest: string | null;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function messageText(message: unknown): string {
|
|
24
|
+
if (!message || typeof message !== "object") return "";
|
|
25
|
+
const content = (message as Record<string, any>).content;
|
|
26
|
+
if (typeof content === "string") return content;
|
|
27
|
+
if (!Array.isArray(content)) return "";
|
|
28
|
+
return content.filter((block) => block?.type === "text").map((block) => String(block.text ?? "")).join("\n");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function latestOperatorText(entries: unknown[]): string {
|
|
32
|
+
for (let index = entries.length - 1; index >= 0; index -= 1) {
|
|
33
|
+
const entry = entries[index] as Record<string, any> | undefined;
|
|
34
|
+
if (entry?.type === "message" && entry.message?.role === "user") return messageText(entry.message);
|
|
35
|
+
}
|
|
36
|
+
return "";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function operatorExplicitlyRequestedVerifierExecution(entries: unknown[]): boolean {
|
|
40
|
+
const value = latestOperatorText(entries).normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase();
|
|
41
|
+
return /\b(rerun|run again|test again|verify again|one more run|final run|last run)\b/.test(value)
|
|
42
|
+
|| /\b(run|execute)\b[^\n]{0,80}\b(test|tests|verify|verifier|lint|typecheck|build)\b/.test(value)
|
|
43
|
+
|| /\b(chay lai|test lai|verify lai|kiem tra lai|kiem thu lai|lan cuoi|chay (?:mot|1) lan nua)\b/.test(value)
|
|
44
|
+
|| /\b(chay|thuc hien)\b[^\n]{0,80}\b(test|kiem thu|verify|verifier|lint|typecheck|build)\b/.test(value);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function evaluateExactVerifierReuse(input: {
|
|
48
|
+
task?: TaskContract;
|
|
49
|
+
toolName: string;
|
|
50
|
+
toolInput: Record<string, unknown>;
|
|
51
|
+
workingTreeDigest: string | null;
|
|
52
|
+
sessionEntries?: unknown[];
|
|
53
|
+
}): ExactVerifierReuseDecision {
|
|
54
|
+
const no = (reasonCode: string): ExactVerifierReuseDecision => ({ reused: false, reasonCode, commandDigest: null, workingTreeDigest: input.workingTreeDigest });
|
|
55
|
+
const task = input.task;
|
|
56
|
+
if (!task || task.trace.outcome !== "pending" || task.changeMode !== "source-change") return no("no-pending-source-task");
|
|
57
|
+
if (!SHELL_TOOLS.has(input.toolName)) return no("not-shell-tool");
|
|
58
|
+
if (operatorExplicitlyRequestedVerifierExecution(input.sessionEntries ?? [])) return no("operator-requested-execution");
|
|
59
|
+
if (Object.hasOwn(input.toolInput, "args") && Array.isArray(input.toolInput.args) && input.toolInput.args.length > 0) return no("shell-args-present");
|
|
60
|
+
const extracted = extractShellCommandInput(input.toolInput);
|
|
61
|
+
if (!extracted.command || extracted.reason) return no("shell-command-unavailable");
|
|
62
|
+
const command = normalizeShellCommandForPolicy(extracted.command);
|
|
63
|
+
if (!commandMatchesVerifyPlan(command, task.verifyCommands)) return no("not-exact-verifier");
|
|
64
|
+
const passing = passingVerifyCommandsForDigest(task, input.workingTreeDigest);
|
|
65
|
+
const exactVerifier = task.verifyCommands.find((candidate) => (
|
|
66
|
+
passing.has(candidate.trim())
|
|
67
|
+
&& commandMatchesVerifyPlan(command, [candidate])
|
|
68
|
+
));
|
|
69
|
+
if (!exactVerifier) return no("current-tree-pass-missing");
|
|
70
|
+
const field = typeof input.toolInput.command === "string" ? "command" : typeof input.toolInput.cmd === "string" ? "cmd" : null;
|
|
71
|
+
if (!field) return no("mutable-command-field-missing");
|
|
72
|
+
input.toolInput[field] = REUSE_COMMAND;
|
|
73
|
+
return {
|
|
74
|
+
reused: true,
|
|
75
|
+
reasonCode: "current-tree-exact-verifier-reused",
|
|
76
|
+
commandDigest: crypto.createHash("sha256").update(exactVerifier.trim()).digest("hex"),
|
|
77
|
+
workingTreeDigest: input.workingTreeDigest
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function reuseCurrentTreeExactVerifier(input: {
|
|
82
|
+
cwd: string;
|
|
83
|
+
task?: TaskContract;
|
|
84
|
+
toolName: string;
|
|
85
|
+
toolInput: Record<string, unknown>;
|
|
86
|
+
sessionEntries?: unknown[];
|
|
87
|
+
}): ExactVerifierReuseDecision {
|
|
88
|
+
if (!input.task || !SHELL_TOOLS.has(input.toolName)) {
|
|
89
|
+
return { reused: false, reasonCode: "not-applicable", commandDigest: null, workingTreeDigest: null };
|
|
90
|
+
}
|
|
91
|
+
const extracted = extractShellCommandInput(input.toolInput);
|
|
92
|
+
const command = extracted.command ? normalizeShellCommandForPolicy(extracted.command) : "";
|
|
93
|
+
if (!command || !commandMatchesVerifyPlan(command, input.task.verifyCommands)) {
|
|
94
|
+
return { reused: false, reasonCode: "not-exact-verifier", commandDigest: null, workingTreeDigest: null };
|
|
95
|
+
}
|
|
96
|
+
const snapshot = workingTreeSnapshot(input.cwd) as Record<string, string>;
|
|
97
|
+
const workingTreeDigest = workingTreeSnapshotHasUnavailableEvidence(snapshot)
|
|
98
|
+
? null
|
|
99
|
+
: workingTreeEvidenceDigest(snapshot);
|
|
100
|
+
return evaluateExactVerifierReuse({ ...input, workingTreeDigest });
|
|
101
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { RUNTIME_INTAKE_MESSAGE_MAX_CHARS } from "../runtime-limits.ts";
|
|
2
|
+
import { verifierCommandBoundsError } from "../../extensions/verification-intelligence.js";
|
|
3
|
+
|
|
4
|
+
const COMPACTION_MARKER = "[Piagent intake guidance compacted; the complete operator request and durable Task Contract remain authoritative.]";
|
|
5
|
+
const MAX_EXACT_FINAL_OUTPUT_LINE_CHARS = 512;
|
|
6
|
+
|
|
7
|
+
function edgeBounded(value: string, maximum: number): string {
|
|
8
|
+
if (value.length <= maximum) return value;
|
|
9
|
+
const marker = " … ";
|
|
10
|
+
const available = maximum - marker.length;
|
|
11
|
+
const head = Math.floor(available * 0.6);
|
|
12
|
+
return `${value.slice(0, head).trimEnd()}${marker}${value.slice(-(available - head)).trimStart()}`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function legacyHeadTail(text: string): string {
|
|
16
|
+
const marker = `\n\n${COMPACTION_MARKER}\n\n`;
|
|
17
|
+
const available = RUNTIME_INTAKE_MESSAGE_MAX_CHARS - marker.length;
|
|
18
|
+
const head = Math.floor(available * 0.58);
|
|
19
|
+
return `${text.slice(0, head).trimEnd()}${marker}${text.slice(-(available - head)).trimStart()}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function minimalCriticalProof(lines: string[]): string[] {
|
|
23
|
+
if (lines.length === 0) return [];
|
|
24
|
+
const tags = [...new Set((lines.join("\n").match(/\[(?:criterion-[0-9]{2}|fallback)(?::[^\]]+)?\]/g) ?? []).map((tag) => {
|
|
25
|
+
const identity = tag.match(/^\[(criterion-[0-9]{2}|fallback)/)?.[1] ?? "fallback";
|
|
26
|
+
if (tag.includes(":fallback")) return `[${identity}:fallback]`;
|
|
27
|
+
if (tag.includes(":")) return `[${identity}:candidate]`;
|
|
28
|
+
return `[${identity}]`;
|
|
29
|
+
}))].slice(0, 12);
|
|
30
|
+
return [
|
|
31
|
+
"Critical behavioral proof:",
|
|
32
|
+
`- ${tags.join("")} Prove every tagged observable clause with linked durable live assertions; the generic verifier alone is insufficient.`,
|
|
33
|
+
"Fallback tags require a focused test. Run proof after final mutation and before exact verifiers; prose or transient probes are insufficient."
|
|
34
|
+
];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function minimalExecutionMap(lines: string[]): string[] {
|
|
38
|
+
if (lines.length === 0) return [];
|
|
39
|
+
const rows = lines.slice(1, 13).map((line) => {
|
|
40
|
+
const criterion = line.match(/^-\s+(criterion-[0-9]{2})\b/)?.[1];
|
|
41
|
+
const target = line.match(/^-\s+criterion-[0-9]{2}\s+(\S+)/)?.[1];
|
|
42
|
+
const after = line.match(/\bafter=(\S+)/)?.[1];
|
|
43
|
+
if (!criterion) return `- ${edgeBounded(line.replace(/^-\s*/, ""), 72)}`;
|
|
44
|
+
return `- ${criterion}${target ? ` ${edgeBounded(target, 38)}` : ""}${after ? ` after=${edgeBounded(after, 18)}` : ""}`;
|
|
45
|
+
});
|
|
46
|
+
return [lines[0], ...rows];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function identityOnlyExecutionMap(lines: string[]): string[] {
|
|
50
|
+
if (lines.length === 0) return [];
|
|
51
|
+
return [
|
|
52
|
+
lines[0],
|
|
53
|
+
...lines.slice(1, 13).map((line, index) => `- ${line.match(/\bcriterion-[0-9]{2}\b/)?.[0] ?? `criterion-${String(index + 1).padStart(2, "0")}`}`)
|
|
54
|
+
];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function structuredCompaction(text: string): string | undefined {
|
|
58
|
+
const lines = text.split("\n");
|
|
59
|
+
const critical = lines.indexOf("Critical behavioral proof:");
|
|
60
|
+
const exactOutput = lines.findIndex((line) => line.startsWith("Exact final-output contract:"));
|
|
61
|
+
const exact = lines.indexOf("Exact verifier commands:");
|
|
62
|
+
const execution = lines.indexOf("Execution map (planning only):");
|
|
63
|
+
const reuse = lines.findIndex((line, index) => index > execution && line.startsWith("Use runtime-delivered source;"));
|
|
64
|
+
if (!(exact >= 0 && execution > exact && reuse > execution)) return undefined;
|
|
65
|
+
const publicContract = critical >= 0
|
|
66
|
+
? lines.findIndex((line, index) => index > critical && index < exact && line === "Existing public contract:")
|
|
67
|
+
: -1;
|
|
68
|
+
const criticalLines = critical >= 0 ? lines.slice(critical, publicContract > critical ? publicContract : exact) : [];
|
|
69
|
+
const exactOutputLines = exactOutput >= 0 && exactOutput < exact ? [lines[exactOutput]] : [];
|
|
70
|
+
const exactLines = lines.slice(exact, execution);
|
|
71
|
+
const executionLines = lines.slice(execution, reuse);
|
|
72
|
+
const prefixEnd = [critical, exactOutput, exact].filter((index) => index >= 0).sort((left, right) => left - right)[0];
|
|
73
|
+
const prefix = lines.slice(0, prefixEnd).slice(0, 3);
|
|
74
|
+
const finalGuidance = [...lines].reverse().find(Boolean) ?? "";
|
|
75
|
+
const compose = (proof: string[], map: string[], compactPrefix = false) => [
|
|
76
|
+
...(compactPrefix ? prefix.map((line) => edgeBounded(line, 360)) : prefix),
|
|
77
|
+
COMPACTION_MARKER,
|
|
78
|
+
...proof,
|
|
79
|
+
...exactOutputLines,
|
|
80
|
+
...displayExactLines,
|
|
81
|
+
...map,
|
|
82
|
+
lines[reuse],
|
|
83
|
+
finalGuidance
|
|
84
|
+
].filter(Boolean).join("\n");
|
|
85
|
+
const presentedCommands = exactLines.slice(1)
|
|
86
|
+
.map((line) => line.match(/^Verifier\s+\d+\s+\(run as an exact standalone shell command\): (.*)$/)?.[1] ?? line)
|
|
87
|
+
.filter((command) => command.trim());
|
|
88
|
+
const displayExactLines = [
|
|
89
|
+
"Exact verifier commands:",
|
|
90
|
+
...presentedCommands.map((command, index) => `Verifier ${index + 1} (run as an exact standalone shell command): ${command}`)
|
|
91
|
+
];
|
|
92
|
+
const verifierError = verifierCommandBoundsError(presentedCommands);
|
|
93
|
+
if (verifierError) return [
|
|
94
|
+
...prefix.slice(0, 2).map((line) => edgeBounded(line, 220)),
|
|
95
|
+
COMPACTION_MARKER,
|
|
96
|
+
...exactOutputLines.map((line) => edgeBounded(line, 260)),
|
|
97
|
+
"Exact verifier commands:",
|
|
98
|
+
`Runtime intake refused: ${verifierError} No verifier was truncated or accepted as evidence; correct the profile and restart the task.`,
|
|
99
|
+
...minimalExecutionMap(executionLines),
|
|
100
|
+
edgeBounded(lines[reuse], 220),
|
|
101
|
+
"Stop this task until a bounded exact verifier is configured."
|
|
102
|
+
].filter(Boolean).join("\n");
|
|
103
|
+
if (exactOutputLines.some((line) => line.length > MAX_EXACT_FINAL_OUTPUT_LINE_CHARS)) return [
|
|
104
|
+
...prefix.slice(0, 2).map((line) => edgeBounded(line, 220)),
|
|
105
|
+
COMPACTION_MARKER,
|
|
106
|
+
"Exact final-output contract:",
|
|
107
|
+
`Runtime intake refused: the mandatory exact final-output guidance exceeds ${MAX_EXACT_FINAL_OUTPUT_LINE_CHARS} characters and cannot be carried verbatim. Shorten the directive and restart the task.`,
|
|
108
|
+
...displayExactLines,
|
|
109
|
+
...identityOnlyExecutionMap(executionLines).slice(0, 9),
|
|
110
|
+
"Stop this task until the exact final-output contract can be injected without mutation."
|
|
111
|
+
].filter(Boolean).join("\n");
|
|
112
|
+
const preferred = compose(criticalLines, executionLines);
|
|
113
|
+
if (preferred.length <= RUNTIME_INTAKE_MESSAGE_MAX_CHARS) return preferred;
|
|
114
|
+
const minimal = compose(minimalCriticalProof(criticalLines), minimalExecutionMap(executionLines), true);
|
|
115
|
+
if (minimal.length <= RUNTIME_INTAKE_MESSAGE_MAX_CHARS) return minimal;
|
|
116
|
+
const guaranteed = [
|
|
117
|
+
...prefix.slice(0, 2).map((line) => edgeBounded(line, 220)),
|
|
118
|
+
COMPACTION_MARKER,
|
|
119
|
+
...minimalCriticalProof(criticalLines),
|
|
120
|
+
...exactOutputLines,
|
|
121
|
+
...displayExactLines,
|
|
122
|
+
...minimalExecutionMap(executionLines),
|
|
123
|
+
edgeBounded(lines[reuse], 220),
|
|
124
|
+
edgeBounded(finalGuidance, 220)
|
|
125
|
+
].filter(Boolean).join("\n");
|
|
126
|
+
if (guaranteed.length <= RUNTIME_INTAKE_MESSAGE_MAX_CHARS) return guaranteed;
|
|
127
|
+
const final = [
|
|
128
|
+
...prefix.slice(0, 1).map((line) => edgeBounded(line, 120)),
|
|
129
|
+
COMPACTION_MARKER,
|
|
130
|
+
...minimalCriticalProof(criticalLines),
|
|
131
|
+
...exactOutputLines,
|
|
132
|
+
...displayExactLines,
|
|
133
|
+
...identityOnlyExecutionMap(executionLines),
|
|
134
|
+
edgeBounded(lines[reuse], 120),
|
|
135
|
+
edgeBounded(finalGuidance, 120)
|
|
136
|
+
].filter(Boolean).join("\n");
|
|
137
|
+
if (final.length <= RUNTIME_INTAKE_MESSAGE_MAX_CHARS) return final;
|
|
138
|
+
const absoluteFallback = [
|
|
139
|
+
COMPACTION_MARKER,
|
|
140
|
+
...minimalCriticalProof(criticalLines).slice(0, 2).map((line) => edgeBounded(line, 420)),
|
|
141
|
+
...exactOutputLines,
|
|
142
|
+
...displayExactLines,
|
|
143
|
+
...identityOnlyExecutionMap(executionLines).slice(0, 9),
|
|
144
|
+
edgeBounded(lines[reuse], 100),
|
|
145
|
+
edgeBounded(finalGuidance, 100)
|
|
146
|
+
].filter(Boolean).join("\n");
|
|
147
|
+
if (absoluteFallback.length <= RUNTIME_INTAKE_MESSAGE_MAX_CHARS) return absoluteFallback;
|
|
148
|
+
return [
|
|
149
|
+
COMPACTION_MARKER,
|
|
150
|
+
...exactOutputLines,
|
|
151
|
+
...displayExactLines,
|
|
152
|
+
"Runtime intake refused: mandatory exact output and verifier guidance cannot fit the bounded provider intake without mutation. Shorten the configured contract and restart the task.",
|
|
153
|
+
"Stop this task; do not infer or reconstruct omitted acceptance details."
|
|
154
|
+
].filter(Boolean).join("\n");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function boundedRuntimeIntakeMessage(value: string): string {
|
|
158
|
+
const text = String(value ?? "").replace(/\r\n/g, "\n");
|
|
159
|
+
const structured = structuredCompaction(text);
|
|
160
|
+
if (text.length <= RUNTIME_INTAKE_MESSAGE_MAX_CHARS) {
|
|
161
|
+
return structured?.includes("Runtime intake refused:") ? structured : text;
|
|
162
|
+
}
|
|
163
|
+
return structured ?? legacyHeadTail(text);
|
|
164
|
+
}
|
|
@@ -1,17 +1,41 @@
|
|
|
1
1
|
import { classifyContextTask } from "../../extensions/context-engine.js";
|
|
2
2
|
import { matchesAnyPath, matchesProtectedPath, normalizePathCandidate } from "../../extensions/policy-core.js";
|
|
3
3
|
import type { ReviewLens } from "../../extensions/guard-types.js";
|
|
4
|
-
import { LONG_INPUT_CHARS
|
|
4
|
+
import { LONG_INPUT_CHARS } from "../runtime-limits.ts";
|
|
5
|
+
export { boundedRuntimeIntakeMessage } from "./runtime-intake-compaction.ts";
|
|
5
6
|
|
|
6
7
|
const AUTO_INTAKE_MAX_PROMPT_CHARS = LONG_INPUT_CHARS;
|
|
7
8
|
const AUTO_TASK_SUMMARY_CHARS = 700;
|
|
8
9
|
const AUTO_ACCEPTANCE_CRITERION_CHARS = 600;
|
|
9
10
|
const AUTO_ACCEPTANCE_CRITERIA_MAX = 12;
|
|
10
|
-
const AUTO_INTAKE_CHANGE_INTENT = /\b(?:add|build|change|correct|create|fix|implement|modify|refactor|remove|rename|repair|replace|update|write|sua|them|doi|cap nhat|xoa|tao)\b/i;
|
|
11
|
+
const AUTO_INTAKE_CHANGE_INTENT = /\b(?:add|build|change|correct|create|fix|implement|modify|mutate|refactor|remove|rename|repair|replace|update|write|sua|them|doi|cap nhat|xoa|tao)\b/i;
|
|
11
12
|
const AUTO_INTAKE_READ_ONLY_LEAD = /^\s*\/?(?:analy[sz]e|audit|check|discuss|explain|inspect|plan|research|review|scout|summari[sz]e|why|how|can\s+(?:you|we)|kiem tra|nghien cuu|giai thich|danh gia)\b/i;
|
|
12
13
|
const AUTO_INTAKE_MANUAL_RISK = /\b(?:credential|database|deploy|destructive|encryption|external provider|payment|permission|production|publish|secret|token rotation)\b/i;
|
|
13
14
|
const AUTO_READ_ONLY_INTENT = /\b(?:analy[sz]e|audit|check|diagnos(?:e|is)|explain|inspect|investigate|plan|research|review|scout|summari[sz]e|triage|kiem tra|nghien cuu|giai thich|danh gia)\b/i;
|
|
14
|
-
|
|
15
|
+
// Candidate wording is separated from the following qualifier. This keeps a
|
|
16
|
+
// durable zero-delta instruction distinct from local path authority and from a
|
|
17
|
+
// temporary "inspect first, edit later" instruction.
|
|
18
|
+
const AUTO_NO_MUTATION_CANDIDATES = [
|
|
19
|
+
/\b(?:do not|don't|must not|never)\s+(?:edit|change|modify|mutate|touch|write(?:\s+to)?)\s+(?:(?:any|all|the|this|entire)\s+)?(?:files?|code|source(?:\s+files?)?|project(?:\s+files?)?|workspace|repo(?:sitory)?|anything)\b/i,
|
|
20
|
+
/\b(?:do not|don't|must not|never)\s+make\s+(?:any\s+)?(?:changes?|edits?|mutations?)(?:\s+to\s+(?:(?:any|all|the)\s+)?(?:files?|code|source(?:\s+files?)?|project(?:\s+files?)?|workspace|repo(?:sitory)?))?\b/i,
|
|
21
|
+
/\b(?:make\s+)?no\s+(?:(?:code|source|project|file|workspace|repo(?:sitory)?)\s+)?(?:changes?|edits?|mutations?)(?:\s+to\s+(?:(?:any|all|the)\s+)?(?:files?|code|source(?:\s+files?)?|project(?:\s+files?)?|workspace|repo(?:sitory)?))?\b/i,
|
|
22
|
+
/\bno\s+(?:project|source)\s+files?\s+(?:are\s+)?(?:changed|edited|modified|mutated)\b/i,
|
|
23
|
+
/\bwithout\s+(?:editing|changing|modifying|mutating|touching|writing(?:\s+to)?)\s+(?:(?:any|all|the)\s+)?(?:files?|code|source(?:\s+files?)?|project(?:\s+files?)?|workspace|repo(?:sitory)?)\b/i,
|
|
24
|
+
/\bleave\s+(?:(?:all|the|this|entire)\s+)?(?:files?|code|source|project|workspace|repo(?:sitory)?)\s+(?:fully\s+)?(?:unchanged|unmodified)\b/i,
|
|
25
|
+
/\b(?:zero\s+task\s+delta|mutation[- ]free)\b/i,
|
|
26
|
+
/\bkh(?:o|ô)ng\s+(?:s(?:u|ử)a|edit|thay\s+(?:d|đ)(?:o|ổ)i)\s+(?:b(?:a|ấ)t\s+k(?:y|ỳ)\s+)?(?:file|source|project|workspace|repo)\b/i
|
|
27
|
+
];
|
|
28
|
+
const AUTO_BOUNDARY_TAIL_PREFIX = "^(?:\\s|[,.;:!?()\\[\\]{}—–-])*(?:but\\s+)?";
|
|
29
|
+
const AUTO_LOCAL_BOUNDARY_TAIL = new RegExp(`${AUTO_BOUNDARY_TAIL_PREFIX}(?:outside|beyond|under|within|inside|in|on|for(?!\\s+now\\b)|except(?:\\s+for)?|other\\s+than|apart\\s+from|to\\b|only\\s+(?:(?:edit|change|modify|mutate|touch|write)\\b|(?:in|under|within|outside)\\b|(?:[a-z0-9_.@*?-]+\\/))|ngo(?:a|à)i\\b|tr(?:u|ừ)\\b)`, "i");
|
|
30
|
+
const AUTO_TEMPORARY_BOUNDARY_TAIL = new RegExp(`${AUTO_BOUNDARY_TAIL_PREFIX}(?:(?:not\\s+)?yet\\b|right\\s+now\\b|for\\s+now\\b|at\\s+(?:(?:this|the\\s+current)\\s+(?:step|stage|phase|time)|the\\s+moment)\\b|during\\s+(?:(?:this|the\\s+current)\\s+)?(?:step|stage|phase|planning)\\b|while\\b|unless\\b|until\\b|before\\b|hi(?:e|ệ)n\\s+t(?:a|ạ)i\\b|b(?:a|â)y\\s+gi(?:o|ờ)\\b)`, "i");
|
|
31
|
+
const AUTO_GLOBAL_READ_ONLY_PATTERNS = [
|
|
32
|
+
/^\s*\/?read-only\s*(?:$|[.!?:;—–-])/im,
|
|
33
|
+
/^\s*\/?read-only\s+(?:task|run|session)\s*(?:$|[.!?:;—–-]|\b(?:to|for)\b)/im,
|
|
34
|
+
/^\s*\/?read-only\s+(?:assessment|review)\s*(?:$|[.!?:;—–-]|\b(?:of|for)\b)/im,
|
|
35
|
+
/\b(?:as|in)\s+(?:a\s+)?read-only\s+(?:mode|task|run|session|assessment|review)\b/i,
|
|
36
|
+
/\b(?:use|perform|conduct|run)\s+(?:a\s+)?read-only\s+(?:task|run|assessment|review)\b/i,
|
|
37
|
+
/\b(?:keep\s+)?(?:this\s+|the\s+)?(?:task|run|session|workspace|project|repo|repository)\s+(?:(?:is|must|should)\s+(?:be\s+|remain\s+)?|remain\s+)?read-only\b/i
|
|
38
|
+
];
|
|
15
39
|
const AUTO_EXECUTION_INTENT = /(?:\b(?:run|execute|execution|rerun|re-run|chay)\b.{0,80}\b(?:tests?|build|checks?|gates?|lint|typecheck|package|pack|verify|verification)\b|\b(?:npm|pnpm|yarn|bun)\s+(?:test|pack|run\s+(?:build|check|lint|typecheck|verify))\b)/i;
|
|
16
40
|
|
|
17
41
|
export const AUTO_INTAKE_SNAPSHOT_PATTERNS = [
|
|
@@ -22,6 +46,27 @@ function uniqueStrings(values: string[]): string[] {
|
|
|
22
46
|
return [...new Set(values)];
|
|
23
47
|
}
|
|
24
48
|
|
|
49
|
+
function noMutationBoundarySignals(text: string): { taskWide: boolean; temporary: boolean } {
|
|
50
|
+
let temporary = false;
|
|
51
|
+
for (const pattern of AUTO_NO_MUTATION_CANDIDATES) {
|
|
52
|
+
const matcher = new RegExp(pattern.source, `${pattern.flags}g`);
|
|
53
|
+
for (const match of text.matchAll(matcher)) {
|
|
54
|
+
const tail = text.slice((match.index ?? 0) + match[0].length, (match.index ?? 0) + match[0].length + 160);
|
|
55
|
+
if (AUTO_LOCAL_BOUNDARY_TAIL.test(tail)) continue;
|
|
56
|
+
if (AUTO_TEMPORARY_BOUNDARY_TAIL.test(tail)) {
|
|
57
|
+
temporary = true;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
return { taskWide: true, temporary };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return { taskWide: false, temporary };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function hasGlobalReadOnlyBoundary(text: string): boolean {
|
|
67
|
+
return AUTO_GLOBAL_READ_ONLY_PATTERNS.some((pattern) => pattern.test(text));
|
|
68
|
+
}
|
|
69
|
+
|
|
25
70
|
const PLAUSIBLE_SCOPE_ROOT = /^(?:\.github|app|apps|bin|config|docs|examples|lib|logs|packages|pages|public|scripts|spec|src|test|tests|vendor|__tests__)(?:\/|$)/i;
|
|
26
71
|
const PLAUSIBLE_SCOPE_FILE = /(?:^|\/)(?:\.[^/]+|[^/]+\.(?:bash|c|cc|cjs|cpp|css|csv|env|go|graphql|gql|h|hpp|html|java|js|json|jsx|kt|kts|md|mdx|mjs|php|proto|py|rb|rs|scss|sh|sql|svg|swift|toml|ts|tsx|txt|xml|yaml|yml))$/i;
|
|
27
72
|
|
|
@@ -37,18 +82,11 @@ export function automaticTaskSummary(prompt: string): string {
|
|
|
37
82
|
return String(prompt ?? "").replace(/\s+/g, " ").trim().slice(0, AUTO_TASK_SUMMARY_CHARS);
|
|
38
83
|
}
|
|
39
84
|
|
|
40
|
-
export function boundedRuntimeIntakeMessage(value: string): string {
|
|
41
|
-
const text = String(value ?? "").replace(/\r\n/g, "\n");
|
|
42
|
-
if (text.length <= RUNTIME_INTAKE_MESSAGE_MAX_CHARS) return text;
|
|
43
|
-
const marker = "\n\n[Piagent intake guidance compacted; the complete operator request and durable Task Contract remain authoritative.]\n\n";
|
|
44
|
-
const available = RUNTIME_INTAKE_MESSAGE_MAX_CHARS - marker.length;
|
|
45
|
-
const head = Math.floor(available * 0.58);
|
|
46
|
-
return `${text.slice(0, head).trimEnd()}${marker}${text.slice(-(available - head)).trimStart()}`;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
85
|
export function automaticTaskIntakeEligible(prompt: string, readProtectedPaths: string[]): boolean {
|
|
50
86
|
const text = String(prompt ?? "").trim();
|
|
51
87
|
if (!text || text.length > AUTO_INTAKE_MAX_PROMPT_CHARS) return false;
|
|
88
|
+
const noMutationBoundary = noMutationBoundarySignals(text);
|
|
89
|
+
if (noMutationBoundary.temporary && !noMutationBoundary.taskWide) return false;
|
|
52
90
|
const signal = classifyContextTask(text);
|
|
53
91
|
if (AUTO_EXECUTION_INTENT.test(text)) {
|
|
54
92
|
if (/\bpiagent_task_start\b/i.test(text)) return false;
|
|
@@ -65,8 +103,9 @@ export function automaticReadOnlyTaskIntakeEligible(prompt: string, readProtecte
|
|
|
65
103
|
if (!text || text.length > AUTO_INTAKE_MAX_PROMPT_CHARS) return false;
|
|
66
104
|
const signal = classifyContextTask(text);
|
|
67
105
|
if (signal.workflow === "usage" || signal.workflow === "permission" || signal.workflow === "context") return false;
|
|
68
|
-
|
|
69
|
-
if (
|
|
106
|
+
const readOnlyBoundary = noMutationBoundarySignals(text).taskWide || hasGlobalReadOnlyBoundary(text);
|
|
107
|
+
if (!AUTO_READ_ONLY_INTENT.test(text) && !readOnlyBoundary) return false;
|
|
108
|
+
if (AUTO_INTAKE_CHANGE_INTENT.test(text) && !readOnlyBoundary) return false;
|
|
70
109
|
if (/\bpiagent_task_start\b/i.test(text)) return false;
|
|
71
110
|
return !signal.paths.some((candidate) => matchesProtectedPath(candidate, readProtectedPaths));
|
|
72
111
|
}
|
|
@@ -77,47 +116,171 @@ export function automaticTaskIntakeMode(prompt: string, readProtectedPaths: stri
|
|
|
77
116
|
return undefined;
|
|
78
117
|
}
|
|
79
118
|
|
|
119
|
+
export function automaticTaskMutationPolicy(
|
|
120
|
+
prompt: string,
|
|
121
|
+
changeMode: "source-change" | "read-only"
|
|
122
|
+
): "required" | "forbidden" {
|
|
123
|
+
const text = String(prompt ?? "");
|
|
124
|
+
// A feature can intentionally become read-only while the implementation
|
|
125
|
+
// still requires source changes. Only a task-wide boundary may suppress
|
|
126
|
+
// mutation; incidental domain wording such as "list/detail read-only" must
|
|
127
|
+
// never convert a delegated implementation into a zero-delta task.
|
|
128
|
+
return changeMode === "read-only"
|
|
129
|
+
|| noMutationBoundarySignals(text).taskWide
|
|
130
|
+
|| hasGlobalReadOnlyBoundary(text)
|
|
131
|
+
? "forbidden"
|
|
132
|
+
: "required";
|
|
133
|
+
}
|
|
134
|
+
|
|
80
135
|
export function automaticTaskRiskLane(prompt: string): "tiny" | "normal" {
|
|
81
136
|
return classifyContextTask(prompt).lane === "tiny" ? "tiny" : "normal";
|
|
82
137
|
}
|
|
83
138
|
|
|
139
|
+
const ATOMIC_CLAUSE_LEAD = /^(?:accept|add|build|change|correct|create|do not|emit|ensure|fail|fix|handle|implement|invalid|missing|modify|must|never|parse|preserve|reject|repair|return|support|throw|treat|update|verify|without)\b/i;
|
|
140
|
+
|
|
141
|
+
function topLevelImperativeClauses(value: string): string[] {
|
|
142
|
+
const clauses: string[] = [];
|
|
143
|
+
let current = "", quote = "", round = 0, square = 0, curly = 0;
|
|
144
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
145
|
+
const character = value[index], previous = value[index - 1] ?? "", next = value[index + 1] ?? "";
|
|
146
|
+
if (quote) {
|
|
147
|
+
current += character;
|
|
148
|
+
if (character === quote && previous !== "\\") quote = "";
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
const apostropheInsideWord = character === "'" && /[\p{L}\p{N}]/u.test(previous) && /[\p{L}\p{N}]/u.test(next);
|
|
152
|
+
if ((character === "`" || character === '"' || character === "'") && !apostropheInsideWord) {
|
|
153
|
+
quote = character; current += character;
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (character === "(") round += 1;
|
|
157
|
+
else if (character === ")") round = Math.max(0, round - 1);
|
|
158
|
+
else if (character === "[") square += 1;
|
|
159
|
+
else if (character === "]") square = Math.max(0, square - 1);
|
|
160
|
+
else if (character === "{") curly += 1;
|
|
161
|
+
else if (character === "}") curly = Math.max(0, curly - 1);
|
|
162
|
+
if (character === ";" && round === 0 && square === 0 && curly === 0 && ATOMIC_CLAUSE_LEAD.test(value.slice(index + 1).trimStart())) {
|
|
163
|
+
if (current.trim().length >= 8) clauses.push(current.trim());
|
|
164
|
+
current = "";
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
current += character;
|
|
168
|
+
}
|
|
169
|
+
if (current.trim()) clauses.push(current.trim());
|
|
170
|
+
return clauses;
|
|
171
|
+
}
|
|
172
|
+
|
|
84
173
|
function splitAcceptanceCriterion(value: string): string[] {
|
|
85
174
|
const normalized = value.replace(/^\s*(?:[-*+] |\d+[.)]\s+)/, "").replace(/\s+/g, " ").trim();
|
|
86
175
|
if (!normalized || normalized.length < 8) return [];
|
|
87
176
|
const label = normalized.match(/^(\[[^\]\n]{1,40}\])\s+/)?.[1] ?? "";
|
|
88
|
-
|
|
177
|
+
const body = label ? normalized.slice(label.length).trim() : normalized;
|
|
89
178
|
const prefix = label ? `${label} ` : "";
|
|
90
179
|
const available = AUTO_ACCEPTANCE_CRITERION_CHARS - prefix.length;
|
|
91
180
|
const fragments: string[] = [];
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
181
|
+
const units = topLevelImperativeClauses(body)
|
|
182
|
+
.flatMap((clause) => clause.split(/(?<=[.!?])\s+(?=(?:[`"'([{]|[\p{Lu}\p{N}]|(?:do|must|never|verify)\b))/u))
|
|
183
|
+
.filter((item) => item.trim().length >= 8);
|
|
184
|
+
for (const unit of units) {
|
|
185
|
+
let remaining = unit.trim();
|
|
186
|
+
while (remaining.length > available) {
|
|
187
|
+
const window = remaining.slice(0, available + 1);
|
|
188
|
+
const sentence = [...window.matchAll(/[.!?;:]\s+/g)].at(-1);
|
|
189
|
+
const clause = [...window.matchAll(/,\s+/g)].at(-1);
|
|
190
|
+
const whitespace = window.lastIndexOf(" ");
|
|
191
|
+
const preferred = sentence && sentence.index! + sentence[0].trimEnd().length >= Math.floor(available / 3)
|
|
192
|
+
? sentence.index! + sentence[0].trimEnd().length
|
|
193
|
+
: clause && clause.index! + 1 >= Math.floor(available / 2)
|
|
194
|
+
? clause.index! + 1
|
|
195
|
+
: whitespace;
|
|
196
|
+
const cut = preferred > 0 ? preferred : available;
|
|
197
|
+
fragments.push(`${prefix}${remaining.slice(0, cut).trim()}`);
|
|
198
|
+
remaining = remaining.slice(cut).trim();
|
|
199
|
+
}
|
|
200
|
+
if (remaining) fragments.push(`${prefix}${remaining}`);
|
|
105
201
|
}
|
|
106
|
-
if (remaining) fragments.push(`${prefix}${remaining}`);
|
|
107
202
|
return fragments;
|
|
108
203
|
}
|
|
109
204
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
)
|
|
205
|
+
function isPathOnlyCriterion(value: string): boolean {
|
|
206
|
+
const normalized = value
|
|
207
|
+
.replace(/^\s*(?:[-*+] |\d+[.)]\s+)/, "")
|
|
208
|
+
.replace(/^`|`$/g, "")
|
|
209
|
+
.trim();
|
|
210
|
+
return /^(?:[A-Za-z0-9_@.-]+\/)+[A-Za-z0-9_@.-]+$/.test(normalized);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function evenlySpacedCriteria(values: string[], limit: number): string[] {
|
|
214
|
+
if (limit <= 0) return [];
|
|
215
|
+
if (values.length <= limit) return values;
|
|
216
|
+
if (limit === 1) return [values[values.length - 1]];
|
|
217
|
+
return Array.from({ length: limit }, (_entry, index) => (
|
|
218
|
+
values[Math.round((index * (values.length - 1)) / (limit - 1))]
|
|
219
|
+
));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function boundedAcceptanceCriteria(values: string[], limit: number): string[] {
|
|
223
|
+
if (limit <= 0 || values.length === 0) return [];
|
|
224
|
+
if (values.length <= limit) return values;
|
|
225
|
+
const highSignal = /\b(?:do not|fail(?:s|ed)?(?:[-\s]+)closed|invalid|missing|must|never|reject(?:s|ed|ion)?|throws?|typeerror|without mutat(?:e|ing)|unchanged)\b/i;
|
|
226
|
+
const required = values.filter((criterion) => highSignal.test(criterion));
|
|
227
|
+
const selectedRequired = evenlySpacedCriteria(required, Math.min(limit, required.length));
|
|
228
|
+
const selected = new Set(selectedRequired);
|
|
229
|
+
const remaining = values.filter((criterion) => !selected.has(criterion));
|
|
230
|
+
for (const criterion of evenlySpacedCriteria(remaining, limit - selected.size)) selected.add(criterion);
|
|
231
|
+
return values.filter((criterion) => selected.has(criterion));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
type AtomicCriterionGroup = { criteria: string[]; index: number };
|
|
235
|
+
|
|
236
|
+
function atomicCriterionScore(value: string): number {
|
|
237
|
+
const weighted = [
|
|
238
|
+
[/(?:\bdo not\b|\bdoes not\b|\bcannot\b|\bnever\b|\binvalid\b|\breject|\bthrow|\bfail(?:s|ed)?[- ]closed\b)/i, 8],
|
|
239
|
+
[/(?:\bidentical\b|\breplay|\bcanonical|\bidempoten)/i, 6],
|
|
240
|
+
[/(?:\bstale\b|changed owner|inclusive boundary)/i, 5],
|
|
241
|
+
[/\bexact(?:ly)?\b/i, 4],
|
|
242
|
+
[/(?:\bnon-finite\b|\bnon-json\b|\bmutat|\bdigest\b|\breturn|\boutput\b)/i, 3]
|
|
243
|
+
] as const;
|
|
244
|
+
return weighted.reduce((score, [pattern, weight]) => score + (pattern.test(value) ? weight : 0), 0);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function atomicCoverageCriteria(groups: AtomicCriterionGroup[], limit: number): string[] {
|
|
248
|
+
const candidates = groups.flatMap((group) => group.criteria.map((criterion, clause) => ({ criterion, group: group.index, clause })));
|
|
249
|
+
if (candidates.length <= limit) return uniqueStrings(candidates.map((item) => item.criterion));
|
|
250
|
+
const representatives = groups.filter((group) => group.criteria.length > 0).map((group) => group.criteria.at(-1)!);
|
|
251
|
+
const selected = new Set(boundedAcceptanceCriteria(representatives, Math.min(limit, representatives.length)));
|
|
252
|
+
const ranked = candidates.filter((item) => !selected.has(item.criterion)).sort((left, right) => (
|
|
253
|
+
atomicCriterionScore(right.criterion) - atomicCriterionScore(left.criterion)
|
|
254
|
+
|| left.group - right.group
|
|
255
|
+
|| left.clause - right.clause
|
|
256
|
+
));
|
|
257
|
+
for (const item of ranked) {
|
|
258
|
+
if (selected.size < limit) { selected.add(item.criterion); continue; }
|
|
259
|
+
if (groups.length <= limit || atomicCriterionScore(item.criterion) === 0) continue;
|
|
260
|
+
const sameGroupAlreadyCovered = candidates.some((candidate) => candidate.group === item.group && selected.has(candidate.criterion));
|
|
261
|
+
if (!sameGroupAlreadyCovered) continue;
|
|
262
|
+
const removable = candidates
|
|
263
|
+
.filter((candidate) => selected.has(candidate.criterion) && candidate.group !== item.group)
|
|
264
|
+
.sort((left, right) => (
|
|
265
|
+
Number(/^\[/.test(left.criterion)) - Number(/^\[/.test(right.criterion))
|
|
266
|
+
|| atomicCriterionScore(left.criterion) - atomicCriterionScore(right.criterion)
|
|
267
|
+
|| right.group - left.group
|
|
268
|
+
))
|
|
269
|
+
.find((candidate) => atomicCriterionScore(candidate.criterion) < atomicCriterionScore(item.criterion));
|
|
270
|
+
if (removable) { selected.delete(removable.criterion); selected.add(item.criterion); }
|
|
271
|
+
}
|
|
272
|
+
return uniqueStrings(candidates.filter((item) => selected.has(item.criterion)).map((item) => item.criterion)).slice(0, limit);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export function automaticAcceptanceCriteria(prompt: string, changeMode: "source-change" | "read-only" = "source-change"): string[] {
|
|
114
276
|
const lines = String(prompt ?? "").split(/\r?\n/);
|
|
115
|
-
const
|
|
116
|
-
const obligation = /\b(?:add|change only|do not|ensure|exactly|invalid|must|never|preserve|reject|
|
|
277
|
+
const criterionGroups: AtomicCriterionGroup[] = [];
|
|
278
|
+
const obligation = /\b(?:accept|add|build|change|change only|correct|create|do not|emits?|ensure|exactly|fail(?:s|ed)?(?:[-\s]+)closed|fix|handle|implement|invalid|missing|modify|must|never|parse|preserve|reject|repair|returns?|support|throw|treat|update|without)\b/i;
|
|
117
279
|
let current = "";
|
|
118
280
|
let currentIsBullet = false;
|
|
119
281
|
const push = (value: string) => {
|
|
120
|
-
|
|
282
|
+
if (isPathOnlyCriterion(value)) return;
|
|
283
|
+
criterionGroups.push({ criteria: splitAcceptanceCriterion(value), index: criterionGroups.length });
|
|
121
284
|
};
|
|
122
285
|
const flush = () => {
|
|
123
286
|
if (current && (currentIsBullet || obligation.test(current))) push(current);
|
|
@@ -141,8 +304,14 @@ export function automaticAcceptanceCriteria(
|
|
|
141
304
|
flush();
|
|
142
305
|
const generic = changeMode === "read-only"
|
|
143
306
|
? ["No project files are changed.", "The final response addresses the requested diagnostic result."]
|
|
144
|
-
: ["
|
|
145
|
-
|
|
307
|
+
: ["The configured verification command passes after the final mutation."];
|
|
308
|
+
const groupCount = criterionGroups.filter((group) => group.criteria.length > 0).length;
|
|
309
|
+
const reservedGeneric = Math.min(generic.length, Math.max(0, AUTO_ACCEPTANCE_CRITERIA_MAX - groupCount));
|
|
310
|
+
const uniqueCriteria = atomicCoverageCriteria(criterionGroups, AUTO_ACCEPTANCE_CRITERIA_MAX - reservedGeneric);
|
|
311
|
+
const selected = boundedAcceptanceCriteria(uniqueCriteria, AUTO_ACCEPTANCE_CRITERIA_MAX);
|
|
312
|
+
if (selected.length === AUTO_ACCEPTANCE_CRITERIA_MAX) return selected;
|
|
313
|
+
const missingGeneric = generic.filter((criterion) => !selected.includes(criterion));
|
|
314
|
+
return [...selected, ...missingGeneric].slice(0, AUTO_ACCEPTANCE_CRITERIA_MAX);
|
|
146
315
|
}
|
|
147
316
|
|
|
148
317
|
export function manualTaskIntakeEligible(prompt: string, readProtectedPaths: string[]): boolean {
|