@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.
Files changed (323) hide show
  1. package/CHANGELOG.md +166 -0
  2. package/README.md +25 -21
  3. package/README.vi.md +34 -5
  4. package/adapters/web-frontend/profile.json +2 -4
  5. package/architecture/layers.json +18 -3
  6. package/benchmarks/deep-logic-v1/SPECIALIST_PROTOCOL.vi.md +122 -0
  7. package/benchmarks/deep-logic-v1/grade.mjs +272 -10
  8. package/benchmarks/deep-logic-v1/project/apps/admin/src/billing-summary.js +3 -0
  9. package/benchmarks/deep-logic-v1/project/benchmark-contract/public-smoke.test.js +166 -0
  10. package/benchmarks/deep-logic-v1/project/package.json +1 -1
  11. package/benchmarks/deep-logic-v1/project/packages/billing/src/close-period.js +3 -0
  12. package/benchmarks/deep-logic-v1/project/packages/billing/src/plan-timeline.js +3 -0
  13. package/benchmarks/deep-logic-v1/project/test/smoke.test.js +7 -1
  14. package/benchmarks/deep-logic-v1/prompts/budgeted-context-graph.md +2 -0
  15. package/benchmarks/deep-logic-v1/prompts/fair-dependency-scheduler.md +2 -0
  16. package/benchmarks/deep-logic-v1/prompts/layered-policy-resolution.md +2 -0
  17. package/benchmarks/deep-logic-v1/prompts/resumable-stream-assembly.md +3 -1
  18. package/benchmarks/deep-logic-v1/prompts/revision-event-reconciliation.md +2 -0
  19. package/benchmarks/deep-logic-v1/prompts/temporal-usage-billing-close.md +123 -0
  20. package/benchmarks/deep-logic-v1/prompts/transactional-config-merge.md +2 -0
  21. package/benchmarks/deep-logic-v1/suite.json +51 -3
  22. package/benchmarks/deep-logic-v1/variant.mjs +22 -1
  23. package/benchmarks/production-v1/spend-control.v1.json +92 -0
  24. package/benchmarks/production-v1/suite.json +46 -3
  25. package/catalog/capabilities.json +18 -18
  26. package/docs/authority-manifest.md +1 -1
  27. package/docs/auto-delegation-policy.md +38 -73
  28. package/docs/command-reference-vietnamese.md +27 -35
  29. package/docs/context-engine.md +12 -4
  30. package/docs/context-window-policy.md +37 -1
  31. package/docs/distribution-standard.md +7 -7
  32. package/docs/en/architecture.md +27 -13
  33. package/docs/languages.json +4 -4
  34. package/docs/model-options.md +8 -5
  35. package/docs/operator-manual-vietnamese.md +27 -45
  36. package/docs/project-adapters.md +2 -2
  37. package/docs/publishing-for-teams.md +10 -0
  38. package/docs/quality-benchmark.md +427 -62
  39. package/docs/release-install-policy.md +10 -9
  40. package/docs/runtime-harness-standard.md +3 -3
  41. package/docs/runtime-policy-design.md +4 -4
  42. package/docs/runtime-quality-baseline.md +13 -6
  43. package/docs/security-threat-model.md +2 -2
  44. package/docs/subagent-orchestration-capabilities.md +19 -51
  45. package/docs/subagents-and-multiagent.md +21 -47
  46. package/docs/task-implementation-contract.md +3 -3
  47. package/docs/task-lifecycle-vietnamese.md +3 -3
  48. package/docs/team-onboarding.md +2 -2
  49. package/docs/usage-observability.md +7 -2
  50. package/docs/vi/architecture.md +27 -13
  51. package/docs-site/architecture.html +1 -1
  52. package/docs-site/benchmark.html +89 -95
  53. package/docs-site/commands.html +1 -1
  54. package/docs-site/content/benchmark.html +88 -94
  55. package/docs-site/content/en/benchmark.html +134 -90
  56. package/docs-site/content/en/index.html +19 -19
  57. package/docs-site/content/en/runtime.html +4 -4
  58. package/docs-site/content/en/whats-new.html +12 -1
  59. package/docs-site/content/en/workflows.html +2 -2
  60. package/docs-site/content/index.html +19 -19
  61. package/docs-site/content/runtime.html +4 -4
  62. package/docs-site/content/whats-new.html +12 -1
  63. package/docs-site/content/workflows.html +2 -2
  64. package/docs-site/context-engine.html +1 -1
  65. package/docs-site/en/architecture.html +1 -1
  66. package/docs-site/en/benchmark.html +135 -91
  67. package/docs-site/en/commands.html +1 -1
  68. package/docs-site/en/context-engine.html +1 -1
  69. package/docs-site/en/index.html +20 -20
  70. package/docs-site/en/mcp-approval.html +1 -1
  71. package/docs-site/en/mcp-auth.html +1 -1
  72. package/docs-site/en/mcp-servers.html +1 -1
  73. package/docs-site/en/mcp.html +1 -1
  74. package/docs-site/en/permissions.html +1 -1
  75. package/docs-site/en/profiles.html +1 -1
  76. package/docs-site/en/quickstart.html +1 -1
  77. package/docs-site/en/runtime.html +5 -5
  78. package/docs-site/en/scope.html +1 -1
  79. package/docs-site/en/security.html +1 -1
  80. package/docs-site/en/team.html +1 -1
  81. package/docs-site/en/whats-new.html +13 -2
  82. package/docs-site/en/workflows.html +3 -3
  83. package/docs-site/index.html +20 -20
  84. package/docs-site/mcp-approval.html +1 -1
  85. package/docs-site/mcp-auth.html +1 -1
  86. package/docs-site/mcp-servers.html +1 -1
  87. package/docs-site/mcp.html +1 -1
  88. package/docs-site/permissions.html +1 -1
  89. package/docs-site/profiles.html +1 -1
  90. package/docs-site/quickstart.html +1 -1
  91. package/docs-site/runtime.html +5 -5
  92. package/docs-site/scope.html +1 -1
  93. package/docs-site/security.html +1 -1
  94. package/docs-site/team.html +1 -1
  95. package/docs-site/whats-new.html +13 -2
  96. package/docs-site/workflows.html +3 -3
  97. package/evals/fixtures/benchmark-assurance-evidence.invalid.json +1 -1
  98. package/evals/fixtures/benchmark-assurance-evidence.valid.json +1 -1
  99. package/evals/fixtures/codex-relative-efficiency-v1.invalid.json +4 -0
  100. package/evals/fixtures/codex-relative-efficiency-v1.valid.json +94 -0
  101. package/evals/fixtures/helper-request.invalid.json +1 -1
  102. package/evals/fixtures/helper-request.valid.json +1 -1
  103. package/evals/fixtures/piagent-webui/session-live-state-v1.invalid.json +18 -0
  104. package/evals/fixtures/piagent-webui/session-live-state-v1.valid.json +27 -0
  105. package/evals/fixtures/piagent-webui/subagent-tree-v1.valid.json +7 -0
  106. package/evals/fixtures/task-authority-snapshot.valid.json +4 -4
  107. package/evals/fs4-readiness-matrix.v1.json +2 -2
  108. package/evals/fs5-causal-arm.v1.json +3 -3
  109. package/evals/fs5-pilot-protocol.v1.json +3 -3
  110. package/evals/fs5-pilot-protocol.v2.json +8 -8
  111. package/evals/fs5-pilot-protocol.v3.json +8 -8
  112. package/evals/fs5-pilot-protocol.v4.json +7 -6
  113. package/evals/fs5-pilot-protocol.v5.json +9 -7
  114. package/evals/private-holdout-v1/public-exposure.v1.json +1 -1
  115. package/evals/runtime-conformance-v1/README.md +43 -0
  116. package/evals/runtime-conformance-v1/evaluate-paid-canary.mjs +198 -0
  117. package/evals/runtime-conformance-v1/lane.json +57 -0
  118. package/evals/runtime-conformance-v1/paid-canary.json +31 -0
  119. package/evals/runtime-conformance-v1/runner.mjs +456 -0
  120. package/package.json +3 -1
  121. package/packages/piagent-core/README.md +13 -4
  122. package/packages/piagent-core/benchmark/benchmark-assurance.js +4 -1
  123. package/packages/piagent-core/benchmark/benchmark-bootstrap.js +91 -9
  124. package/packages/piagent-core/benchmark/benchmark-claim-restrictions.js +38 -7
  125. package/packages/piagent-core/benchmark/benchmark-cli.js +7 -7
  126. package/packages/piagent-core/benchmark/benchmark-codex-relative-efficiency.js +698 -0
  127. package/packages/piagent-core/benchmark/benchmark-codex.js +3 -1
  128. package/packages/piagent-core/benchmark/benchmark-comparison.js +271 -2
  129. package/packages/piagent-core/benchmark/benchmark-core.js +736 -664
  130. package/packages/piagent-core/benchmark/benchmark-execution-guard.js +7 -5
  131. package/packages/piagent-core/benchmark/benchmark-forensics.js +441 -4
  132. package/packages/piagent-core/benchmark/benchmark-host-readiness.js +639 -0
  133. package/packages/piagent-core/benchmark/benchmark-matrix.js +22 -4
  134. package/packages/piagent-core/benchmark/benchmark-normalized-cost.js +317 -0
  135. package/packages/piagent-core/benchmark/benchmark-preflight.js +5 -1
  136. package/packages/piagent-core/benchmark/benchmark-process.js +6 -5
  137. package/packages/piagent-core/benchmark/benchmark-provider-free-evidence.js +345 -0
  138. package/packages/piagent-core/benchmark/benchmark-provider-wire.js +126 -0
  139. package/packages/piagent-core/benchmark/benchmark-record-validation.js +252 -0
  140. package/packages/piagent-core/benchmark/benchmark-report.js +130 -12
  141. package/packages/piagent-core/benchmark/benchmark-resume-recovery.js +14 -0
  142. package/packages/piagent-core/benchmark/benchmark-runner-policy.js +26 -0
  143. package/packages/piagent-core/benchmark/benchmark-runtime-causal.js +244 -0
  144. package/packages/piagent-core/benchmark/benchmark-stage-diagnostic.js +979 -0
  145. package/packages/piagent-core/benchmark/benchmark-statistics.js +64 -0
  146. package/packages/piagent-core/benchmark/benchmark-stop-policy.js +8 -5
  147. package/packages/piagent-core/benchmark/benchmark-suite-identity.js +31 -0
  148. package/packages/piagent-core/benchmark/benchmark-suite-runtime.js +13 -9
  149. package/packages/piagent-core/benchmark/benchmark-suite.js +149 -2
  150. package/packages/piagent-core/benchmark/benchmark-summary-support.js +380 -0
  151. package/packages/piagent-core/benchmark/benchmark-timing-diagnostics.js +971 -0
  152. package/packages/piagent-core/benchmark/benchmark-usage.js +322 -0
  153. package/packages/piagent-core/benchmark/benchmark-workflow.js +166 -0
  154. package/packages/piagent-core/capabilities/runtime-integrity.js +3 -0
  155. package/packages/piagent-core/docs/context-efficiency-improvement-plan.md +1080 -0
  156. package/packages/piagent-core/extensions/acceptance-behavior-proof.js +806 -0
  157. package/packages/piagent-core/extensions/acceptance-contract-semantics.js +83 -69
  158. package/packages/piagent-core/extensions/acceptance-error-classes.js +160 -0
  159. package/packages/piagent-core/extensions/acceptance-executable-evidence.js +12 -9
  160. package/packages/piagent-core/extensions/acceptance-receipt.js +123 -59
  161. package/packages/piagent-core/extensions/acceptance-state-machine-evidence.js +518 -0
  162. package/packages/piagent-core/extensions/acceptance-test-binding-evidence.js +60 -0
  163. package/packages/piagent-core/extensions/context-efficiency-metrics.js +302 -0
  164. package/packages/piagent-core/extensions/context-efficiency-report.js +167 -0
  165. package/packages/piagent-core/extensions/context-engine.js +149 -156
  166. package/packages/piagent-core/extensions/context-evidence.js +48 -0
  167. package/packages/piagent-core/extensions/context-import-links.js +296 -0
  168. package/packages/piagent-core/extensions/context-index-policy.js +4 -0
  169. package/packages/piagent-core/extensions/context-query-paths.js +57 -0
  170. package/packages/piagent-core/extensions/context-telemetry-integrity.js +65 -0
  171. package/packages/piagent-core/extensions/criterion-context-pack.js +435 -7
  172. package/packages/piagent-core/extensions/criterion-graph.js +82 -9
  173. package/packages/piagent-core/extensions/fallback-base-policy.ts +101 -0
  174. package/packages/piagent-core/extensions/guard-types.ts +6 -0
  175. package/packages/piagent-core/extensions/javascript-regex-evidence.js +32 -0
  176. package/packages/piagent-core/extensions/piagent-guard.ts +420 -260
  177. package/packages/piagent-core/extensions/task-contract-view.js +17 -3
  178. package/packages/piagent-core/extensions/task-lifecycle.js +39 -2
  179. package/packages/piagent-core/extensions/task-state.js +109 -142
  180. package/packages/piagent-core/extensions/verification-intelligence.js +52 -13
  181. package/packages/piagent-core/extensions/workspace-evidence-roots.js +193 -0
  182. package/packages/piagent-core/package.json +1 -1
  183. package/packages/piagent-core/policies/base-policy.json +44 -2
  184. package/packages/piagent-core/policy/authority-manifest.v1.json +3 -3
  185. package/packages/piagent-core/prompts/be-to-fe.md +2 -8
  186. package/packages/piagent-core/prompts/plan.md +1 -1
  187. package/packages/piagent-core/prompts/platform-improve.md +2 -6
  188. package/packages/piagent-core/prompts/review.md +2 -7
  189. package/packages/piagent-core/prompts/scout.md +1 -1
  190. package/packages/piagent-core/prompts/task.md +4 -3
  191. package/packages/piagent-core/runtime/context/context-delivery.ts +103 -0
  192. package/packages/piagent-core/runtime/context/context-delta-shadow.ts +53 -0
  193. package/packages/piagent-core/runtime/context/context-efficiency-format.ts +47 -0
  194. package/packages/piagent-core/runtime/context/context-evidence-qualification.ts +32 -0
  195. package/packages/piagent-core/runtime/context/prefix-telemetry.ts +47 -0
  196. package/packages/piagent-core/runtime/context/repository-memory-hints.ts +27 -0
  197. package/packages/piagent-core/runtime/hooks/agent-prompt-telemetry.ts +56 -0
  198. package/packages/piagent-core/runtime/hooks/agent-start-hook.ts +162 -102
  199. package/packages/piagent-core/runtime/hooks/completion-hook.ts +12 -12
  200. package/packages/piagent-core/runtime/hooks/input-hook.ts +17 -1
  201. package/packages/piagent-core/runtime/hooks/session-hooks.ts +22 -2
  202. package/packages/piagent-core/runtime/hooks/session-start-hook.ts +107 -3
  203. package/packages/piagent-core/runtime/hooks/tool-result-content-guards.ts +95 -0
  204. package/packages/piagent-core/runtime/hooks/tool-result-hook.ts +134 -166
  205. package/packages/piagent-core/runtime/hooks/tool-result-value-helpers.ts +66 -0
  206. package/packages/piagent-core/runtime/inspection/activity-event-adapter.ts +4 -2
  207. package/packages/piagent-core/runtime/inspection/activity-recovery.ts +104 -0
  208. package/packages/piagent-core/runtime/inspection/bounded-emission-guard.ts +286 -0
  209. package/packages/piagent-core/runtime/inspection/runtime-event-store.ts +37 -7
  210. package/packages/piagent-core/runtime/inspection/task-compaction-history.ts +1 -1
  211. package/packages/piagent-core/runtime/inspection/task-handoff-history.ts +1 -1
  212. package/packages/piagent-core/runtime/inspection/task-subagent-tree.ts +27 -4
  213. package/packages/piagent-core/runtime/inspection/tool-failure-classification.ts +70 -0
  214. package/packages/piagent-core/runtime/inspection/webui-snapshot.ts +73 -9
  215. package/packages/piagent-core/runtime/model/authenticated-catalog.ts +17 -9
  216. package/packages/piagent-core/runtime/model/openai-codex-reasoning.ts +83 -0
  217. package/packages/piagent-core/runtime/model/provider-wire-fingerprint.ts +238 -0
  218. package/packages/piagent-core/runtime/orchestration/helper-lifecycle.ts +16 -12
  219. package/packages/piagent-core/runtime/orchestration/minimal-delegation-policy.ts +71 -0
  220. package/packages/piagent-core/runtime/orchestration/owned-work-budget.ts +28 -20
  221. package/packages/piagent-core/runtime/orchestration/role-policy.ts +27 -9
  222. package/packages/piagent-core/runtime/orchestration/subagent-tool-policy.ts +329 -0
  223. package/packages/piagent-core/runtime/policy/block-remedy.ts +0 -4
  224. package/packages/piagent-core/runtime/product/activity-inspector.ts +7 -2
  225. package/packages/piagent-core/runtime/product/efficiency-metrics.ts +7 -2
  226. package/packages/piagent-core/runtime/quality/edit-freshness-guard.ts +102 -0
  227. package/packages/piagent-core/runtime/quality/exact-output-contract.ts +22 -11
  228. package/packages/piagent-core/runtime/quality/performance-assurance-signals.ts +23 -0
  229. package/packages/piagent-core/runtime/quality/performance-assurance.ts +9 -5
  230. package/packages/piagent-core/runtime/recovery/continuation-budget.ts +1 -1
  231. package/packages/piagent-core/runtime/recovery/edit-recovery-context.ts +113 -0
  232. package/packages/piagent-core/runtime/recovery/edit-recovery-delivery.ts +42 -0
  233. package/packages/piagent-core/runtime/recovery/handoff-projection.ts +4 -1
  234. package/packages/piagent-core/runtime/recovery/pristine-task-policy-refresh.ts +91 -0
  235. package/packages/piagent-core/runtime/recovery/recovery-policy.ts +7 -4
  236. package/packages/piagent-core/runtime/recovery/resume-state.ts +112 -14
  237. package/packages/piagent-core/runtime/recovery/semantic-repair-handshake.ts +60 -13
  238. package/packages/piagent-core/runtime/recovery/semantic-repair-runtime.ts +55 -2
  239. package/packages/piagent-core/runtime/recovery/task-evidence-coverage-refresh.ts +217 -0
  240. package/packages/piagent-core/runtime/registration/context-commands.ts +82 -7
  241. package/packages/piagent-core/runtime/registration/knowledge-tools.ts +9 -5
  242. package/packages/piagent-core/runtime/registration/operator-request-intake.ts +49 -0
  243. package/packages/piagent-core/runtime/registration/policy-tools.ts +50 -15
  244. package/packages/piagent-core/runtime/registration/session-commands.ts +1 -1
  245. package/packages/piagent-core/runtime/registration/task-completion-tools.ts +61 -25
  246. package/packages/piagent-core/runtime/registration/task-evidence-tools.ts +21 -12
  247. package/packages/piagent-core/runtime/registration/task-start-guidance.ts +169 -0
  248. package/packages/piagent-core/runtime/registration/task-start-manifest.ts +54 -0
  249. package/packages/piagent-core/runtime/registration/task-start-retry-helpers.ts +37 -0
  250. package/packages/piagent-core/runtime/registration/task-start-tool.ts +108 -103
  251. package/packages/piagent-core/runtime/runtime-limits.ts +16 -2
  252. package/packages/piagent-core/runtime/session/adaptive-context-analysis.ts +461 -0
  253. package/packages/piagent-core/runtime/session/adaptive-context-governor.ts +464 -0
  254. package/packages/piagent-core/runtime/session/adaptive-context-ledger.ts +265 -0
  255. package/packages/piagent-core/runtime/session/message-signals.ts +7 -3
  256. package/packages/piagent-core/runtime/session/operator-request-carry.ts +44 -0
  257. package/packages/piagent-core/runtime/session/runtime-state.ts +161 -5
  258. package/packages/piagent-core/runtime/session/source-checkout-read-grants.ts +35 -0
  259. package/packages/piagent-core/runtime/session/system-prompt.ts +76 -14
  260. package/packages/piagent-core/runtime/session/task-contract-presentation.ts +39 -0
  261. package/packages/piagent-core/runtime/tools/apply-patch-tool.ts +500 -0
  262. package/packages/piagent-core/runtime/tools/phase-tools.ts +8 -8
  263. package/packages/piagent-core/runtime/tools/tool-groups.ts +12 -2
  264. package/packages/piagent-core/runtime/trajectory/trajectory-runtime.ts +2 -1
  265. package/packages/piagent-core/runtime/verification/exact-verifier-reuse.ts +101 -0
  266. package/packages/piagent-core/runtime/workflows/runtime-intake-compaction.ts +164 -0
  267. package/packages/piagent-core/runtime/workflows/task-intake.ts +207 -38
  268. package/packages/piagent-core/security/sensitive-data.js +13 -218
  269. package/packages/piagent-core/security/sensitive-project-file.js +42 -0
  270. package/packages/piagent-core/security/sensitive-source-expression.js +330 -0
  271. package/packages/piagent-core/security/sensitive-source-formats.js +394 -0
  272. package/packages/piagent-core/security/sensitive-text.js +234 -0
  273. package/packages/piagent-core/subagents/piagent-oracle.md +5 -3
  274. package/packages/piagent-core/subagents/piagent-planner.md +6 -4
  275. package/packages/piagent-core/subagents/piagent-reviewer.md +5 -3
  276. package/packages/piagent-core/subagents/piagent-scout.md +4 -2
  277. package/packages/piagent-core/subagents/piagent-worker.md +7 -5
  278. package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-rD9WJKxs.js → MarkdownMessage-B4XehTCU.js} +1 -1
  279. package/packages/piagent-webui/dist/client/assets/index-CgOEyRPG.js +176 -0
  280. package/packages/piagent-webui/dist/client/index.html +1 -1
  281. package/packages/piagent-webui/extension/piagent-webui.ts +3 -1
  282. package/packages/piagent-webui/extension/same-session-bridge.ts +1 -1
  283. package/packages/piagent-webui/extension/session-stream-adapter.ts +33 -10
  284. package/packages/piagent-webui/gateway/gateway-events.ts +109 -5
  285. package/packages/piagent-webui/gateway/gateway-service.ts +3 -0
  286. package/packages/piagent-webui/gateway/gateway-session-stream.ts +122 -20
  287. package/packages/piagent-webui/gateway/session-command-controller.ts +18 -3
  288. package/packages/piagent-webui/gateway/session-effective-options.ts +47 -0
  289. package/packages/piagent-webui/gateway/session-live-state.ts +21 -0
  290. package/packages/piagent-webui/gateway/session-operation-lifecycle.ts +237 -0
  291. package/packages/piagent-webui/gateway/session-operation-retry-control.ts +38 -0
  292. package/packages/piagent-webui/gateway/session-operation-start.ts +5 -1
  293. package/packages/piagent-webui/gateway/session-operation-watchdog.ts +165 -0
  294. package/packages/piagent-webui/gateway/session-runtime-factory.ts +45 -0
  295. package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +141 -125
  296. package/packages/piagent-webui/ownership/session-lease-store.ts +5 -2
  297. package/packages/piagent-webui/server/core-inspection-provider.ts +2 -1
  298. package/packages/piagent-webui/server/loopback-server.ts +8 -0
  299. package/packages/piagent-webui/server/transcript-projection.ts +113 -9
  300. package/packages/piagent-webui/shared/text-visibility.ts +10 -0
  301. package/schemas/codex-relative-efficiency-v1.schema.json +155 -0
  302. package/schemas/piagent-webui/catalog-v1.json +9 -0
  303. package/schemas/piagent-webui/gateway-protocol-v1.schema.json +53 -3
  304. package/schemas/piagent-webui/runtime-event-v2.schema.json +15 -5
  305. package/schemas/piagent-webui/session-live-state-v1.schema.json +76 -0
  306. package/schemas/piagent-webui/subagent-tree-v1.schema.json +11 -1
  307. package/schemas/project-profile.schema.json +1 -1
  308. package/schemas/task-contract.schema.json +27 -1
  309. package/scripts/benchmark-runner-core.mjs +367 -363
  310. package/scripts/benchmark-runner-finalization.mjs +313 -0
  311. package/scripts/benchmark-runner-host-readiness.mjs +94 -0
  312. package/scripts/benchmark-runner-support.mjs +200 -0
  313. package/scripts/benchmark-session.mjs +95 -41
  314. package/scripts/configure-model-scope.sh +4 -3
  315. package/scripts/configure-subagents.sh +42 -34
  316. package/scripts/helper-evaluation.mjs +2 -2
  317. package/scripts/install-global.sh +2 -2
  318. package/scripts/pi-usage-history.mjs +37 -10
  319. package/scripts/setup.sh +2 -2
  320. package/scripts/team-doctor.sh +1 -1
  321. package/scripts/verify-local.sh +10 -1
  322. package/templates/global/settings.json +2 -1
  323. 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, RUNTIME_INTAKE_MESSAGE_MAX_CHARS } from "../runtime-limits.ts";
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
- const AUTO_READ_ONLY_BOUNDARY = /\b(?:read-only|no edits?|do not edit(?: files?| source| project| repo)?|do not change (?:files?|source|project|repo)|do not mutate (?:files?|source|project|repo|workspace)|khong sua(?: file| source| project)?|khong edit(?: file| source| project)?|khong doi(?: file| source| project)?)\b/i;
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
- if (!AUTO_READ_ONLY_INTENT.test(text) && !AUTO_READ_ONLY_BOUNDARY.test(text)) return false;
69
- if (AUTO_INTAKE_CHANGE_INTENT.test(text) && !AUTO_READ_ONLY_BOUNDARY.test(text)) return false;
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
- let remaining = label ? normalized.slice(label.length).trim() : normalized;
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
- while (remaining.length > available) {
93
- const window = remaining.slice(0, available + 1);
94
- const sentence = [...window.matchAll(/[.!?;:]\s+/g)].at(-1);
95
- const clause = [...window.matchAll(/,\s+/g)].at(-1);
96
- const whitespace = window.lastIndexOf(" ");
97
- const preferred = sentence && sentence.index! + sentence[0].trimEnd().length >= Math.floor(available / 3)
98
- ? sentence.index! + sentence[0].trimEnd().length
99
- : clause && clause.index! + 1 >= Math.floor(available / 2)
100
- ? clause.index! + 1
101
- : whitespace;
102
- const cut = preferred > 0 ? preferred : available;
103
- fragments.push(`${prefix}${remaining.slice(0, cut).trim()}`);
104
- remaining = remaining.slice(cut).trim();
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
- export function automaticAcceptanceCriteria(
111
- prompt: string,
112
- changeMode: "source-change" | "read-only" = "source-change"
113
- ): string[] {
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 criteria: string[] = [];
116
- const obligation = /\b(?:add|change only|do not|ensure|exactly|invalid|must|never|preserve|reject|return|throw|without)\b/i;
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
- criteria.push(...splitAcceptanceCriterion(value));
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
- : ["Changes stay within the runtime-derived task scope.", "The configured verification command passes after the final mutation."];
145
- return uniqueStrings([...criteria, ...generic]).slice(0, AUTO_ACCEPTANCE_CRITERIA_MAX);
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 {