@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,518 @@
1
+ import { rejectionStatementErrorClass } from "./acceptance-error-classes.js";
2
+
3
+ function escapeRegex(value) {
4
+ return String(value ?? "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5
+ }
6
+
7
+ function balancedEnd(text, openIndex, opening = "(", closing = ")", ceiling = 8_000) {
8
+ if (text[openIndex] !== opening) return -1;
9
+ let depth = 0;
10
+ for (let index = openIndex; index < Math.min(text.length, openIndex + ceiling); index += 1) {
11
+ if (text[index] === opening) depth += 1;
12
+ else if (text[index] === closing && --depth === 0) return index + 1;
13
+ }
14
+ return -1;
15
+ }
16
+
17
+ function braceDepthAt(text, offset) {
18
+ let depth = 0;
19
+ for (let index = 0; index < offset; index += 1) {
20
+ if (text[index] === "{") depth += 1;
21
+ else if (text[index] === "}") depth = Math.max(0, depth - 1);
22
+ }
23
+ return depth;
24
+ }
25
+
26
+ function priorUnconditionalExit(text, offset) {
27
+ for (const match of text.slice(0, offset).matchAll(/\b(?:return|throw)\b/g)) {
28
+ if (braceDepthAt(text, match.index) !== 0) continue;
29
+ const statementStart = Math.max(text.lastIndexOf(";", match.index - 1), text.lastIndexOf("}", match.index - 1)) + 1;
30
+ if (!text.slice(statementStart, match.index).trim()) return true;
31
+ }
32
+ return false;
33
+ }
34
+
35
+ function topLevelParts(text, delimiter) {
36
+ const parts = [];
37
+ let start = 0;
38
+ let parentheses = 0;
39
+ let brackets = 0;
40
+ for (let index = 0; index < text.length; index += 1) {
41
+ if (text[index] === "(") parentheses += 1;
42
+ else if (text[index] === ")") parentheses = Math.max(0, parentheses - 1);
43
+ else if (text[index] === "[") brackets += 1;
44
+ else if (text[index] === "]") brackets = Math.max(0, brackets - 1);
45
+ else if (text[index] === delimiter && parentheses === 0 && brackets === 0) {
46
+ parts.push(text.slice(start, index));
47
+ start = index + 1;
48
+ }
49
+ }
50
+ parts.push(text.slice(start));
51
+ return parts;
52
+ }
53
+
54
+ function stripOuterParentheses(raw) {
55
+ let value = String(raw ?? "").trim();
56
+ while (value.startsWith("(") && balancedEnd(value, 0) === value.length) value = value.slice(1, -1).trim();
57
+ return value;
58
+ }
59
+
60
+ function staticFalsyToken(raw) {
61
+ const value = stripOuterParentheses(raw);
62
+ return /^(?:false|null|undefined|0|__pi_(?:empty|whitespace)_string_literal__)$/i.test(value)
63
+ || /^(?:true\s*={2,3}\s*false|false\s*={2,3}\s*true)$/i.test(value);
64
+ }
65
+
66
+ function staticPrimitiveComparison(raw) {
67
+ const match = stripOuterParentheses(raw).match(/^(-?\d+(?:\.\d+)?|true|false)\s*(===|==|!==|!=|<=|>=|<|>)\s*(-?\d+(?:\.\d+)?|true|false)$/i);
68
+ if (!match) return undefined;
69
+ const value = (token) => /^(?:true|false)$/i.test(token) ? token.toLowerCase() === "true" : Number(token);
70
+ const left = value(match[1]), right = value(match[3]);
71
+ if (["===", "=="].includes(match[2])) return left === right;
72
+ if (["!==", "!="].includes(match[2])) return left !== right;
73
+ if (match[2] === "<") return left < right;
74
+ if (match[2] === ">") return left > right;
75
+ if (match[2] === "<=") return left <= right;
76
+ return left >= right;
77
+ }
78
+
79
+ function staticallyFalseCondition(raw) {
80
+ const value = stripOuterParentheses(raw);
81
+ return staticFalsyToken(value) || staticPrimitiveComparison(value) === false
82
+ || value.replace(/[()]/g, " ").split("&&").some((part) => staticFalsyToken(part) || staticPrimitiveComparison(part) === false);
83
+ }
84
+
85
+ function staticallyTrueCondition(raw) {
86
+ const value = stripOuterParentheses(raw);
87
+ return /^true$/i.test(value) || staticPrimitiveComparison(value) === true;
88
+ }
89
+
90
+ function sideEffectFreeCondition(raw) {
91
+ const value = String(raw ?? "");
92
+ return !staticallyFalseCondition(value)
93
+ && !/(?<![=!<>])=(?!=|>)|\+\+|--|=>|[,;?]|\b(?:await|delete|new|yield)\b/i.test(value)
94
+ && !/\b[a-z_$][a-z0-9_$]*\s*\(/i.test(value);
95
+ }
96
+
97
+ function previousSignificantCharacter(text, offset) {
98
+ let index = offset - 1;
99
+ while (index >= 0 && /\s/.test(text[index])) index -= 1;
100
+ return text[index] ?? "";
101
+ }
102
+
103
+ function directBindingMutations(text, name) {
104
+ const escaped = escapeRegex(name);
105
+ const mutations = [];
106
+ const suffix = new RegExp(`(?<![a-z0-9_$])${escaped}\\b\\s*(?:=(?!=|>)|\\+\\+|--|(?:\\*\\*|<<|>>>?|&&|\\|\\||\\?\\?|[+*/%&|^-])=)`, "gi");
107
+ for (const match of text.matchAll(suffix)) {
108
+ const previous = previousSignificantCharacter(text, match.index);
109
+ if (match.index === undefined || previous && ".#".includes(previous)) continue;
110
+ mutations.push(match.index);
111
+ }
112
+ const prefix = new RegExp(`(?:\\+\\+|--)\\s*(?<![a-z0-9_$.?])${escaped}\\b`, "gi");
113
+ for (const match of text.matchAll(prefix)) mutations.push(match.index + match[0].toLowerCase().lastIndexOf(name.toLowerCase()));
114
+ const destructured = new RegExp(`(?:\\{[^}\\n]{0,500}\\b${escaped}\\b[^}\\n]{0,500}\\}|\\[[^\\]\\n]{0,500}\\b${escaped}\\b[^\\]\\n]{0,500}\\])\\s*=(?!=|>)|\\bfor(?:\\s+await)?\\s*\\(\\s*${escaped}\\s+(?:of|in)\\b`, "gi");
115
+ for (const match of text.matchAll(destructured)) mutations.push(match.index);
116
+ return [...new Set(mutations)].sort((left, right) => left - right);
117
+ }
118
+
119
+ function declarationInitializesAt(text, offset) {
120
+ return /\b(?:const|let|var)\s*$/i.test(text.slice(Math.max(0, offset - 120), offset));
121
+ }
122
+
123
+ function bindingWrittenBefore(text, name, offset) {
124
+ return directBindingMutations(text.slice(0, offset), name)
125
+ .some((mutation) => !declarationInitializesAt(text, mutation));
126
+ }
127
+
128
+ function bindingDeclared(text, name) {
129
+ const escaped = escapeRegex(name);
130
+ return new RegExp(`\\b(?:class|const|function|let|var)\\s+${escaped}\\b|\\b(?:const|let|var)\\s*[\\[{][^}\\]\\n]{0,500}\\b${escaped}\\b|\\bcatch\\s*\\([^)]*\\b${escaped}\\b|\\bimport\\b[^;\\n]{0,500}\\b${escaped}\\b`, "i").test(text);
131
+ }
132
+
133
+ function memberBindingMutation(text, name) {
134
+ const escaped = escapeRegex(name);
135
+ return new RegExp(`(?<![a-z0-9_$.?])${escaped}\\b\\s*(?:\\.[a-z_$][a-z0-9_$]*|\\[[^\\]]{1,300}\\])\\s*(?:=(?!=|>)|\\+\\+|--|(?:\\*\\*|<<|>>>?|&&|\\|\\||\\?\\?|[+*/%&|^-])=)`, "i").test(text);
136
+ }
137
+
138
+ function memberBindingCall(text, name) {
139
+ return new RegExp(`(?<![a-z0-9_$.?])${escapeRegex(name)}\\b\\s*(?:\\.[a-z_$][a-z0-9_$]*|\\[[^\\]]{1,300}\\])\\s*\\(`, "i").test(text);
140
+ }
141
+
142
+ function liveInputNames(text, names, referenceText, offset) {
143
+ return names.filter((name) => new RegExp(`\\b${escapeRegex(name)}\\b`, "i").test(referenceText)
144
+ && !bindingWrittenBefore(text, name, offset));
145
+ }
146
+
147
+ function topLevelControlBefore(text, offset) {
148
+ for (const match of text.slice(0, offset).matchAll(/\b(?:do|for|if|return|switch|throw|try|while)\b/gi)) {
149
+ if (braceDepthAt(text, match.index) === 0) return true;
150
+ }
151
+ return false;
152
+ }
153
+
154
+ function topLevelControlBetween(text, start, end) {
155
+ const segment = text.slice(start, end);
156
+ for (const match of segment.matchAll(/\b(?:do|for|if|return|switch|throw|try|while)\b/gi)) {
157
+ if (braceDepthAt(segment, match.index) === 0) return true;
158
+ }
159
+ return false;
160
+ }
161
+
162
+ function preLoopStatementsProven(text, offset, carrier) {
163
+ let prefix = text.slice(0, offset), escaped = escapeRegex(carrier);
164
+ prefix = prefix.replace(new RegExp(`\\bconst\\s+${escaped}\\s*=\\s*string\\s*\\(\\s*[a-z_$][a-z0-9_$]*\\s*\\)\\s*(?:;|(?=\\r?\\n))`, "gi"), " ");
165
+ prefix = prefix.replace(/\b(?:let|var)\s+[a-z_$][a-z0-9_$]*(?:\s*:\s*(?:boolean|string))?\s*=\s*(?:false|__pi_empty_string_literal__)\s*(?:;|(?=\r?\n))/gi, " ");
166
+ return /^[\s;]*$/.test(prefix);
167
+ }
168
+
169
+ function trustedCarrier(text, carrier, offset, parameters, sourceCode) {
170
+ const normalized = carrier.toLowerCase();
171
+ const prefix = text.slice(0, offset);
172
+ if (parameters.includes(normalized)) return "";
173
+ const escaped = escapeRegex(normalized);
174
+ const declarations = [...prefix.matchAll(new RegExp(`\\bconst\\s+${escaped}\\s*=\\s*string\\s*\\(\\s*([a-z_$][a-z0-9_$]*)\\s*\\)\\s*(?:;|(?=\\r?\\n))`, "gi"))]
175
+ .filter((match) => braceDepthAt(prefix, match.index) === 0);
176
+ const input = declarations[0]?.[1]?.toLowerCase();
177
+ if (declarations.length !== 1 || !parameters.includes(input) || bindingDeclared(prefix.slice(0, declarations[0].index), input)
178
+ || memberBindingCall(prefix.slice(0, declarations[0].index), input) || memberBindingMutation(prefix, input)
179
+ || parameters.includes("string") || bindingDeclared(sourceCode, "string") || bindingWrittenBefore(sourceCode, "string", sourceCode.length)) return "";
180
+ return bindingWrittenBefore(prefix, normalized, prefix.length)
181
+ || bindingWrittenBefore(prefix, input, prefix.length) || memberBindingMutation(prefix, normalized) ? "" : normalized;
182
+ }
183
+
184
+ function loopEvidence(text, header, offset, parameters, sourceCode) {
185
+ const clauses = topLevelParts(header, ";");
186
+ if (clauses.length !== 3) {
187
+ const iterator = header.match(/^\s*(?:const|let)\s+([a-z_$][a-z0-9_$]*)(?:\s*:\s*[^=;]+)?\s+of\s+([a-z_$][a-z0-9_$]*)\s*$/i);
188
+ const carrier = iterator && trustedCarrier(text, iterator[2], offset, parameters, sourceCode);
189
+ return carrier ? { carrier, counter: "", item: iterator[1].toLowerCase() } : undefined;
190
+ }
191
+ const initializer = stripOuterParentheses(clauses[0]).match(/^(?:let|var)\s+([a-z_$][a-z0-9_$]*)(?:\s*:\s*number)?\s*=\s*0$/i);
192
+ if (!initializer) return undefined;
193
+ const counter = initializer[1].toLowerCase(), escapedCounter = escapeRegex(counter);
194
+ const condition = stripOuterParentheses(clauses[1]);
195
+ const bound = condition.match(new RegExp(`^${escapedCounter}\\s*<\\s*([a-z_$][a-z0-9_$]*)\\s*\\.\\s*length$`, "i"));
196
+ const update = stripOuterParentheses(clauses[2]);
197
+ if (!bound || !new RegExp(`^(?:${escapedCounter}\\s*\\+\\+|\\+\\+\\s*${escapedCounter}|${escapedCounter}\\s*\\+=\\s*1)$`, "i").test(update)) return undefined;
198
+ const carrier = trustedCarrier(text, bound[1], offset, parameters, sourceCode);
199
+ return carrier ? { carrier, counter, item: "" } : undefined;
200
+ }
201
+
202
+ function statementEnd(text, start, ceiling) {
203
+ const candidates = [text.indexOf(";", start), text.indexOf("\n", start), text.indexOf("\r", start)]
204
+ .filter((index) => index >= start && index < ceiling)
205
+ .map((index) => index + 1);
206
+ return candidates.length > 0 ? Math.min(...candidates) : ceiling;
207
+ }
208
+
209
+ function conditionalRanges(text, loop) {
210
+ const ranges = [];
211
+ for (const match of text.slice(loop.start, loop.end).matchAll(/\bif\s*\(/gi)) {
212
+ const conditionStart = loop.start + match.index;
213
+ const conditionOpen = text.indexOf("(", conditionStart);
214
+ const conditionEnd = balancedEnd(text, conditionOpen);
215
+ if (conditionEnd === -1 || conditionEnd > loop.end) continue;
216
+ const condition = text.slice(conditionOpen + 1, conditionEnd - 1);
217
+ let consequentStart = conditionEnd;
218
+ while (/\s/.test(text[consequentStart] ?? "")) consequentStart += 1;
219
+ const consequentEnd = text[consequentStart] === "{"
220
+ ? balancedEnd(text, consequentStart, "{", "}")
221
+ : statementEnd(text, consequentStart, loop.end + 1);
222
+ if (consequentEnd > consequentStart && consequentEnd <= loop.end + 1) {
223
+ ranges.push({ conditionStart, condition, start: consequentStart, end: consequentEnd });
224
+ }
225
+ }
226
+ return ranges;
227
+ }
228
+
229
+ function exactBooleanWrite(text, offset, name) {
230
+ const match = text.slice(offset).match(new RegExp(`^${escapeRegex(name)}\\s*=\\s*(true|false)\\s*(?:;|(?=\\r?\\n|}))`, "i"));
231
+ return match ? { end: offset + match[0].length, value: match[1].toLowerCase() } : null;
232
+ }
233
+
234
+ function branchHasPriorExit(text, start, offset) {
235
+ const branchStart = text[start] === "{" ? start + 1 : start;
236
+ const prefix = text.slice(branchStart, offset);
237
+ for (const match of prefix.matchAll(/\bif\s*\(/gi)) {
238
+ if (braceDepthAt(prefix, match.index) !== 0) continue;
239
+ const open = prefix.indexOf("(", match.index), end = balancedEnd(prefix, open);
240
+ if (end === -1 || !staticallyTrueCondition(prefix.slice(open + 1, end - 1))) continue;
241
+ const following = prefix.slice(end).match(/^\s*(?:\{\s*)?(?:break|continue|return|throw)\b/i);
242
+ if (following) return true;
243
+ }
244
+ for (const match of prefix.matchAll(/\b(?:break|continue|return|throw)\b/g)) {
245
+ if (braceDepthAt(prefix, match.index) !== 0) continue;
246
+ const statementStart = Math.max(prefix.lastIndexOf(";", match.index - 1), prefix.lastIndexOf("}", match.index - 1)) + 1;
247
+ if (!prefix.slice(statementStart, match.index).trim()) return true;
248
+ }
249
+ return false;
250
+ }
251
+
252
+ function affirmativeQuotedTerminalContract(contractText, targetName) {
253
+ const normalized = String(contractText ?? "").normalize("NFD").replace(/[\u0300-\u036f`_*]/g, "").toLowerCase()
254
+ .replace(/\b(is|are|was|were|do|does|did|should|would|could|must|will|shall)n['’]t\b/g, "$1 not").replace(/\bcan(?:not|['’]t)\b/g, "can not").replace(/\bwon['’]t\b/g, "will not").replace(/\bshan['’]t\b/g, "shall not");
255
+ if (!targetName) return false;
256
+ const target = new RegExp(`\\b${escapeRegex(targetName)}\\b`, "i"), clauses = normalized.split(/(?<=[.!?;])\s+|\n+/);
257
+ const negated = (clause) => /\b(?:avoid|do\s+not|does\s+not|fails?\s+to|ignore|must\s+not|never|no|not|should\s+not|without|unchanged|unrelated|out\s+of\s+scope)\b/.test(clause);
258
+ const uniquelyMentionsTarget = (clause) => {
259
+ const calls = [...clause.matchAll(/\b([a-z_$][a-z0-9_$]*)\s*\(/gi)].map((match) => match[1].toLowerCase());
260
+ const escaped = escapeRegex(targetName), other = `(?!${escaped}\\b)[a-z_$][a-z0-9_$]*`, connector = `(?:,|and|or|with|alongside|rather\\s+than|instead\\s+of|versus|vs\\.?)`, peer = new RegExp(`(?:\\b${escaped}\\b(?:\\s*\\([^)]{0,200}\\))?\\s*${connector}\\s*${other}\\b|\\b${other}\\b\\s*${connector}\\s*\\b${escaped}\\b)`, "i");
261
+ return target.test(clause) && !peer.test(clause) && calls.every((name) => name === targetName.toLowerCase());
262
+ };
263
+ const directlyObligatesTarget = (clause) => uniquelyMentionsTarget(clause) && new RegExp(`\\b${escapeRegex(targetName)}\\b(?:\\s*\\([^)]{0,200}\\))?\\s+(?:(?:must|shall|should|will)\\s+)?(?:raise|reject|throw)s?\\b`, "i").test(clause);
264
+ const linkedTargetDirective = (clause) => new RegExp(`^(?:implement|preserve|replace|update)\\s+(?:the\\s+)?(?:(?:function|entrypoint)\\s+)?${escapeRegex(targetName)}(?:\\s*\\([^)]{0,200}\\))?(?:\\s+(?:function|entrypoint))?[.!?;]*$`, "i").test(clause.trim());
265
+ const linkedNeighbor = (index, affirmative) => {
266
+ const neighbors = [clauses[index - 1], clauses[index + 1]].filter((clause) => /^\s*(?:implement|preserve|replace|update)\b/i.test(clause ?? ""));
267
+ return /^\s*(?:(?:must|shall|should)\s+)?(?:raise|reject|throw)s?\b/i.test(affirmative)
268
+ && neighbors.length === 1 && linkedTargetDirective(neighbors[0]) && !negated(neighbors[0]) && uniquelyMentionsTarget(neighbors[0]);
269
+ };
270
+ return clauses.some((clause, index) => (/\bunterminated\b[\s\S]{0,100}\bquoted?\s+field\b|\bquoted?\s+field\b[\s\S]{0,100}\bunterminated\b/.test(clause)
271
+ && /\b(?:raise|reject|throw)s?\b[\s\S]{0,80}\bsyntaxerror\b|\bsyntaxerror\b[\s\S]{0,80}\b(?:raise|reject|throw)s?\b/.test(clause)
272
+ && !negated(clause)
273
+ && (directlyObligatesTarget(clause) || linkedNeighbor(index, clause))));
274
+ }
275
+
276
+ function quoteInputGuard(condition, loop) {
277
+ const operands = [...loop.items.map((name) => `\\b${escapeRegex(name)}\\b`)];
278
+ if (loop.counter) operands.push(`\\b${escapeRegex(loop.carrier)}\\b\\s*\\[\\s*${escapeRegex(loop.counter)}\\s*\\]`);
279
+ let quoteComparisons = 0;
280
+ for (const rawPart of topLevelParts(String(condition ?? "").replaceAll("&&", "\u0000"), "\u0000")) {
281
+ const part = stripOuterParentheses(rawPart);
282
+ const quote = operands.some((operand) => new RegExp(`^(?:${operand}\\s*={3}\\s*__pi_double_quote_string_literal__|__pi_double_quote_string_literal__\\s*={3}\\s*${operand})$`, "i").test(part));
283
+ if (quote) { quoteComparisons += 1; continue; }
284
+ const empty = part.match(/^(?:([a-z_$][a-z0-9_$]*)\s*={3}\s*__pi_empty_string_literal__|__pi_empty_string_literal__\s*={3}\s*([a-z_$][a-z0-9_$]*))$/i);
285
+ if (!empty || !loop.emptyAccumulators.includes((empty[1] ?? empty[2]).toLowerCase())) return false;
286
+ }
287
+ return quoteComparisons === 1;
288
+ }
289
+
290
+ function transitionEvidence(text, loop, ranges, state, item) {
291
+ const ancestors = ranges.filter((range) => item.offset >= range.start && item.write.end <= range.end);
292
+ if (ancestors.length === 0 || ancestors.some((range) => !sideEffectFreeCondition(range.condition)
293
+ || branchHasPriorExit(text, range.start, item.offset)) || branchHasPriorExit(text, loop.start, item.offset)) return undefined;
294
+ if (!ancestors.some((range) => liveInputNames(text, loop.inputNames, range.condition, range.conditionStart).length > 0
295
+ && quoteInputGuard(range.condition, loop))) return undefined;
296
+ const stateGuard = ancestors.some((range) => new RegExp(`^${escapeRegex(state)}(?:\\s*={2,3}\\s*true)?$`, "i").test(stripOuterParentheses(range.condition)));
297
+ const stateGuardBraced = ancestors.some((range) => text[range.start] === "{"
298
+ && new RegExp(`^${escapeRegex(state)}(?:\\s*={2,3}\\s*true)?$`, "i").test(stripOuterParentheses(range.condition)));
299
+ const immediate = [...ancestors].sort((left, right) => (left.end - left.start) - (right.end - right.start))[0];
300
+ return { ...item, immediate, stateGuard, stateGuardBraced };
301
+ }
302
+
303
+ function initiallyFalseAuxiliaryGuard(text, loop, ranges, ancestors, state) {
304
+ for (const ancestor of ancestors) {
305
+ const auxiliary = stripOuterParentheses(ancestor.condition).match(/^([a-z_$][a-z0-9_$]*)(?:\s*={2,3}\s*true)?$/i)?.[1]?.toLowerCase();
306
+ if (!auxiliary || auxiliary === state || auxiliary === loop.counter || loop.inputNames.includes(auxiliary)) continue;
307
+ const declarations = [...text.slice(0, loop.start).matchAll(new RegExp(`\\b(?:let|var)\\s+${escapeRegex(auxiliary)}(?:\\s*:\\s*boolean)?\\s*=\\s*false\\s*(?:;|(?=\\r?\\n))`, "gi"))]
308
+ .filter((match) => braceDepthAt(text, match.index) === 0);
309
+ if (declarations.length !== 1) continue;
310
+ const mutations = directBindingMutations(text.slice(loop.start, ancestor.conditionStart), auxiliary);
311
+ if (mutations.every((relativeOffset) => {
312
+ const offset = loop.start + relativeOffset, write = exactBooleanWrite(text, offset, auxiliary);
313
+ if (!write) return false;
314
+ if (write.value === "false") return true;
315
+ const guards = ranges.filter((range) => offset >= range.start && write.end <= range.end);
316
+ return guards.some((range) => new RegExp(`^${escapeRegex(state)}(?:\\s*={2,3}\\s*true)?$`, "i").test(stripOuterParentheses(range.condition)))
317
+ && guards.some((range) => quoteInputGuard(range.condition, loop));
318
+ })) return true;
319
+ }
320
+ return false;
321
+ }
322
+
323
+ function counterFlowProven(text, loop, ranges, state, truthyOffset) {
324
+ if (!loop.counter) return true;
325
+ const bodyText = text.slice(loop.start, loop.end);
326
+ for (const relativeOffset of directBindingMutations(bodyText, loop.counter)) {
327
+ const offset = loop.start + relativeOffset;
328
+ if (!new RegExp(`^${escapeRegex(loop.counter)}\\s*(?:\\+\\+|\\+=\\s*1)\\s*(?:;|(?=\\r?\\n|}))`, "i").test(text.slice(offset))) return false;
329
+ if (offset >= truthyOffset) continue;
330
+ const ancestors = ranges.filter((range) => offset >= range.start && offset < range.end);
331
+ const stateGuard = ancestors.some((range) => new RegExp(`^${escapeRegex(state)}(?:\\s*={2,3}\\s*true)?$`, "i").test(stripOuterParentheses(range.condition)));
332
+ const nextQuote = ancestors.some((range) => new RegExp(`\\b${escapeRegex(loop.carrier)}\\b\\s*\\[\\s*${escapeRegex(loop.counter)}\\s*\\+\\s*1\\s*\\]\\s*={3}\\s*__pi_double_quote_string_literal__`, "i").test(range.condition));
333
+ if ((!stateGuard || !nextQuote) && !initiallyFalseAuxiliaryGuard(text, loop, ranges, ancestors, state)) return false;
334
+ }
335
+ return true;
336
+ }
337
+
338
+ function priorSiblingControlProven(text, loop, ranges, state, truthyOffset) {
339
+ const loopText = text.slice(loop.start, loop.end), statePattern = new RegExp(`^${escapeRegex(state)}(?:\\s*={2,3}\\s*true)?$`, "i");
340
+ for (const range of ranges.filter((item) => item.conditionStart < truthyOffset
341
+ && braceDepthAt(loopText, item.conditionStart - loop.start) === 0)) {
342
+ if (truthyOffset >= range.start && truthyOffset < range.end) continue;
343
+ if (text[range.start] === "{" && statePattern.test(stripOuterParentheses(range.condition))
344
+ && !/^\s*else\b/i.test(text.slice(range.end))) continue;
345
+ return false;
346
+ }
347
+ return true;
348
+ }
349
+
350
+ function statementFragmentProven(raw, loop) {
351
+ let value = String(raw ?? "").trim();
352
+ if (!value) return true;
353
+ if (/^else\b/i.test(value)) value = value.replace(/^else\b\s*/i, "");
354
+ while (/^if\s*\(/i.test(value)) {
355
+ const open = value.indexOf("("), end = balancedEnd(value, open);
356
+ if (end === -1) return false;
357
+ value = value.slice(end).trim();
358
+ }
359
+ const carrierItem = loop.counter ? `${escapeRegex(loop.carrier)}\\s*\\[\\s*${escapeRegex(loop.counter)}\\s*\\]` : "";
360
+ const inputs = [...loop.items.map(escapeRegex), carrierItem].filter(Boolean).join("|");
361
+ if (/^continue$/i.test(value)) return true;
362
+ if (loop.counter && new RegExp(`^(?:${escapeRegex(loop.counter)}\\s*(?:\\+\\+|\\+=\\s*1)|\\+\\+\\s*${escapeRegex(loop.counter)})$`, "i").test(value)) return true;
363
+ if (loop.booleanBindings.some((name) => new RegExp(`^${escapeRegex(name)}\\s*=\\s*(?:true|false)$`, "i").test(value))) return true;
364
+ if (inputs && loop.emptyAccumulators.some((name) => new RegExp(`^${escapeRegex(name)}\\s*\\+=\\s*(?:${inputs})$`, "i").test(value))) return true;
365
+ return Boolean(loop.counter && loop.items.some((name) => new RegExp(`^const\\s+${escapeRegex(name)}\\s*=\\s*${carrierItem}$`, "i").test(value)));
366
+ }
367
+
368
+ function loopStatementsProven(loopBody, loop) {
369
+ if (/\b(?:break|delete|new|return|throw|typeof|void)\b/i.test(loopBody)) return false;
370
+ if (/\b(?!if\b)[a-z_$][a-z0-9_$]*\s*(?:\?\.\s*)?\(|\b[a-z_$][a-z0-9_$]*\s*__pi_template_literal__|[\])]\s*(?:\?\.\s*)?\(/i.test(loopBody)) return false;
371
+ for (const match of loopBody.matchAll(/;/g)) {
372
+ const start = Math.max(loopBody.lastIndexOf(";", match.index - 1), loopBody.lastIndexOf("{", match.index - 1), loopBody.lastIndexOf("}", match.index - 1)) + 1;
373
+ if (!statementFragmentProven(loopBody.slice(start, match.index), loop)) return false;
374
+ }
375
+ for (const line of loopBody.split(/\r?\n/)) {
376
+ const tail = line.slice(line.lastIndexOf(";") + 1).trim();
377
+ if (!tail || /^[{}]*\s*(?:else\s*)?[{}]*$/i.test(tail)) continue;
378
+ if (/^(?:}\s*)?(?:else\s+)?if\s*\(/i.test(tail)) {
379
+ const open = tail.indexOf("("), end = balancedEnd(tail, open);
380
+ if (end !== -1 && /^[\s{}]*$/.test(tail.slice(end))) continue;
381
+ }
382
+ if (!statementFragmentProven(tail.replace(/^}\s*/, ""), loop)) return false;
383
+ }
384
+ for (const declaration of loopBody.matchAll(/\b(?:const|let|var)\b/gi)) {
385
+ const exactItem = loop.counter && loopBody.slice(declaration.index).match(new RegExp(`^const\\s+[a-z_$][a-z0-9_$]*\\s*=\\s*${escapeRegex(loop.carrier)}\\s*\\[\\s*${escapeRegex(loop.counter)}\\s*\\]\\s*(?:;|(?=\\r?\\n))`, "i"));
386
+ if (!exactItem || braceDepthAt(loopBody, declaration.index) !== 0) return false;
387
+ }
388
+ return true;
389
+ }
390
+
391
+ function loopConditionsProven(ranges, loop, state) {
392
+ const statePattern = new RegExp(`^${escapeRegex(state)}(?:\\s*={2,3}\\s*true)?$`, "i");
393
+ const quote = "__pi_double_quote_string_literal__";
394
+ const next = loop.counter ? `${escapeRegex(loop.carrier)}\\s*\\[\\s*${escapeRegex(loop.counter)}\\s*\\+\\s*1\\s*\\]` : "";
395
+ return ranges.every((range) => {
396
+ const condition = stripOuterParentheses(range.condition);
397
+ if (!sideEffectFreeCondition(condition)) return false;
398
+ if (statePattern.test(condition) || quoteInputGuard(condition, loop)) return true;
399
+ if (next && new RegExp(`^(?:${next}\\s*={3}\\s*${quote}|${quote}\\s*={3}\\s*${next})$`, "i").test(condition)) return true;
400
+ return loop.booleanBindings.some((name) => name !== state && new RegExp(`^(?:!\\s*${escapeRegex(name)}|${escapeRegex(name)}(?:\\s*={2,3}\\s*(?:true|false))?)$`, "i").test(condition));
401
+ });
402
+ }
403
+
404
+ function transitionShapeProven(text, transitions) {
405
+ const truthy = transitions.find((item) => item.write.value === "true");
406
+ const falsy = transitions.find((item) => item.write.value === "false");
407
+ if (!truthy || !falsy) return false;
408
+ if (falsy.offset < truthy.offset && falsy.stateGuardBraced && !truthy.stateGuard) return true;
409
+ if (truthy.offset >= falsy.offset || truthy.stateGuard || falsy.stateGuard) return false;
410
+ return /^\s*else\s*$/i.test(text.slice(truthy.immediate.end, falsy.immediate.conditionStart));
411
+ }
412
+
413
+ function statementConsequentEnd(text, start, ceiling) {
414
+ let offset = start;
415
+ while (/\s/.test(text[offset] ?? "")) offset += 1;
416
+ return text[offset] === "{" ? balancedEnd(text, offset, "{", "}") : statementEnd(text, offset, ceiling);
417
+ }
418
+
419
+ function elseChainEnd(text, start, ceiling) {
420
+ let offset = start;
421
+ while (offset < ceiling) {
422
+ while (/\s/.test(text[offset] ?? "")) offset += 1;
423
+ if (!/^else\b/i.test(text.slice(offset))) return offset;
424
+ offset += 4;
425
+ while (/\s/.test(text[offset] ?? "")) offset += 1;
426
+ if (/^if\b/i.test(text.slice(offset))) {
427
+ const open = text.indexOf("(", offset), end = balancedEnd(text, open);
428
+ if (open === -1 || end === -1 || end > ceiling) return -1;
429
+ offset = statementConsequentEnd(text, end, ceiling);
430
+ } else offset = statementConsequentEnd(text, offset, ceiling);
431
+ if (offset === -1 || offset > ceiling) return -1;
432
+ }
433
+ return offset;
434
+ }
435
+
436
+ function truthyTransitionTailProven(text, loop, transition) {
437
+ if (!/^[\s;}]*$/.test(text.slice(transition.write.end, transition.immediate.end))) return false;
438
+ const chainEnd = elseChainEnd(text, transition.immediate.end, loop.end);
439
+ return chainEnd !== -1 && /^[\s;]*$/.test(text.slice(chainEnd, loop.end));
440
+ }
441
+
442
+ // Prove only one narrow terminal state-machine rejection shape. Callers must
443
+ // still establish stable exports/imports, live assertions, error constructors,
444
+ // the exact current verifier, and a current-tree changed source/test corpus.
445
+ export function statefulTerminalRejectionEvidence(body, parameters, requestedErrors, contractText, targetName, sourceCode) {
446
+ if (!affirmativeQuotedTerminalContract(contractText, targetName)) return false;
447
+ const loops = [];
448
+ for (const match of body.matchAll(/\bfor\s*\(/gi)) {
449
+ if (braceDepthAt(body, match.index) !== 0 || priorUnconditionalExit(body, match.index) || topLevelControlBefore(body, match.index)) continue;
450
+ const conditionOpen = body.indexOf("(", match.index), conditionEnd = balancedEnd(body, conditionOpen);
451
+ if (conditionEnd === -1) continue;
452
+ const header = body.slice(conditionOpen + 1, conditionEnd - 1);
453
+ const evidence = loopEvidence(body, header, match.index, parameters, sourceCode);
454
+ if (!evidence || !preLoopStatementsProven(body, match.index, evidence.carrier)) continue;
455
+ let bodyStart = conditionEnd;
456
+ while (/\s/.test(body[bodyStart] ?? "")) bodyStart += 1;
457
+ if (body[bodyStart] !== "{") continue;
458
+ const bodyEnd = balancedEnd(body, bodyStart, "{", "}");
459
+ const loopBody = body.slice(bodyStart + 1, bodyEnd - 1);
460
+ const nestedCallable = /=>|\b(?:catch|class|do|for|function|switch|try|while)\b|(?:^|[;{}]\s*)(?!(?:else|if)\b)[a-z_$][a-z0-9_$]*\s*\([^)]*\)\s*\{/im;
461
+ if (bodyEnd === -1 || nestedCallable.test(loopBody) || memberBindingMutation(loopBody, evidence.carrier)
462
+ || [evidence.carrier, evidence.counter, evidence.item].filter(Boolean).some((name) => bindingDeclared(loopBody, name))) continue;
463
+ const items = [evidence.item].filter(Boolean), inputNames = [evidence.carrier, ...items];
464
+ if (evidence.counter) {
465
+ for (const declaration of loopBody.matchAll(new RegExp(`\\bconst\\s+([a-z_$][a-z0-9_$]*)\\s*=\\s*${escapeRegex(evidence.carrier)}\\s*\\[\\s*${escapeRegex(evidence.counter)}\\s*\\]\\s*(?:;|(?=\\r?\\n))`, "gi"))) {
466
+ if (braceDepthAt(loopBody, declaration.index) === 0) items.push(declaration[1].toLowerCase());
467
+ }
468
+ }
469
+ inputNames.push(...items);
470
+ const emptyAccumulators = [...body.slice(0, match.index).matchAll(/\b(?:let|var)\s+([a-z_$][a-z0-9_$]*)(?:\s*:\s*string)?\s*=\s*__pi_empty_string_literal__\s*(?:;|(?=\r?\n))/gi)]
471
+ .filter((item) => braceDepthAt(body, item.index) === 0).map((item) => item[1].toLowerCase());
472
+ const booleanBindings = [...body.slice(0, match.index).matchAll(/\b(?:let|var)\s+([a-z_$][a-z0-9_$]*)(?:\s*:\s*boolean)?\s*=\s*false\s*(?:;|(?=\r?\n))/gi)]
473
+ .filter((item) => braceDepthAt(body, item.index) === 0).map((item) => item[1].toLowerCase());
474
+ const loop = { ...evidence, start: bodyStart + 1, end: bodyEnd - 1, items: [...new Set(items)], inputNames: [...new Set(inputNames)], emptyAccumulators, booleanBindings };
475
+ if (loopStatementsProven(loopBody, loop)) loops.push(loop);
476
+ }
477
+ if (loops.length === 0) return false;
478
+
479
+ for (const declaration of body.matchAll(/\b(?:let|var)\s+([a-z_$][a-z0-9_$]*)(?:\s*:\s*boolean)?\s*=\s*false\s*(?:;|(?=\r?\n))/gi)) {
480
+ if (braceDepthAt(body, declaration.index) !== 0) continue;
481
+ const state = declaration[1].toLowerCase(), escaped = escapeRegex(state);
482
+ if (parameters.includes(state)
483
+ || [...body.matchAll(new RegExp(`\\b(?:const|let|var)\\s+${escaped}\\b`, "gi"))].length !== 1) continue;
484
+ const mutations = directBindingMutations(body, state)
485
+ .filter((offset) => offset < declaration.index || offset >= declaration.index + declaration[0].length);
486
+ const writes = mutations.map((offset) => ({ offset, write: exactBooleanWrite(body, offset, state) }));
487
+ if (writes.length !== 2 || writes.some((item) => !item.write)
488
+ || !writes.some((item) => item.write.value === "true") || !writes.some((item) => item.write.value === "false")) continue;
489
+ const loop = loops.find((candidate) => candidate.start > declaration.index + declaration[0].length
490
+ && writes.every((item) => item.offset >= candidate.start && item.write.end <= candidate.end + 1));
491
+ if (!loop) continue;
492
+ const ranges = conditionalRanges(body, loop);
493
+ const transitions = writes.map((item) => transitionEvidence(body, loop, ranges, state, item));
494
+ const truthyOffset = writes.find((item) => item.write.value === "true").offset;
495
+ const truthyTransition = transitions.find((item) => item?.write.value === "true");
496
+ if (!loopConditionsProven(ranges, loop, state) || transitions.some((item) => !item) || !transitionShapeProven(body, transitions)
497
+ || !counterFlowProven(body, loop, ranges, state, truthyOffset)
498
+ || !priorSiblingControlProven(body, loop, ranges, state, truthyOffset)
499
+ || !truthyTransitionTailProven(body, loop, truthyTransition)) continue;
500
+
501
+ for (const terminal of body.matchAll(/\bif\s*\(/gi)) {
502
+ if (terminal.index <= loop.end || braceDepthAt(body, terminal.index) !== 0 || priorUnconditionalExit(body, terminal.index)
503
+ || topLevelControlBetween(body, loop.end + 1, terminal.index)
504
+ || !/^[\s;]*$/.test(body.slice(loop.end + 1, terminal.index))) continue;
505
+ const conditionOpen = body.indexOf("(", terminal.index), conditionEnd = balancedEnd(body, conditionOpen);
506
+ if (conditionEnd === -1) continue;
507
+ const condition = stripOuterParentheses(body.slice(conditionOpen + 1, conditionEnd - 1));
508
+ if (!new RegExp(`^${escaped}(?:\\s*={2,3}\\s*true)?$`, "i").test(condition)) continue;
509
+ let consequentStart = conditionEnd;
510
+ while (/\s/.test(body[consequentStart] ?? "")) consequentStart += 1;
511
+ const consequentEnd = body[consequentStart] === "{"
512
+ ? balancedEnd(body, consequentStart, "{", "}")
513
+ : statementEnd(body, consequentStart, body.length);
514
+ if (consequentEnd !== -1 && rejectionStatementErrorClass(body.slice(consequentStart, consequentEnd).replace(/^\s*\{/, ""), requestedErrors)) return true;
515
+ }
516
+ }
517
+ return false;
518
+ }
@@ -0,0 +1,60 @@
1
+ import { assertionsProveErrorMapping } from "./acceptance-error-classes.js";
2
+ import { executableRejectionAssertions } from "./acceptance-executable-evidence.js";
3
+
4
+ export function callableAssertionMode(bodies, name) {
5
+ const callable = bodies.get(name);
6
+ return callable ? (callable.asynchronous ? "rejects" : "throws") : undefined;
7
+ }
8
+
9
+ /** Keep executable rejection assertions bound to the file that imported them. */
10
+ export function boundRejectionTestEvidence(input) {
11
+ const codeByPath = new Map(input.testCodeEntries.map((entry) => [entry.path, entry.code]));
12
+ const cache = new Map();
13
+ const assertionsForBinding = (binding) => {
14
+ const key = `${binding.testPath}\u0000${binding.testName}`;
15
+ if (cache.has(key)) return cache.get(key);
16
+ const code = codeByPath.get(binding.testPath);
17
+ const assertions = typeof code === "string" ? executableRejectionAssertions(code, new Set([binding.testName]))
18
+ .filter((assertion) => assertion.targets.includes(binding.testName)) : [];
19
+ cache.set(key, assertions);
20
+ return assertions;
21
+ };
22
+ const requestedError = (assertion) => input.requestedErrors.length === 0
23
+ || input.requestedErrors.some((name) => assertion.errorClasses.includes(name));
24
+ const bindingGroups = input.bindings.map((binding) => {
25
+ const allAssertions = (binding.testBindings ?? []).flatMap(assertionsForBinding);
26
+ return { binding, allAssertions, assertions: allAssertions.filter(requestedError) };
27
+ });
28
+ const testCodes = input.testCodeEntries.length > 0
29
+ ? input.testCodeEntries.map((entry) => entry.code) : [input.fallbackTestCode];
30
+ const structuralGroups = input.structuralTargets.map((target) => {
31
+ const allAssertions = testCodes.flatMap((code) => executableRejectionAssertions(
32
+ code, new Set([`*.${target}`])
33
+ ).filter((assertion) => assertion.targets.includes(`*.${target}`)));
34
+ return { target, allAssertions, assertions: allAssertions.filter(requestedError) };
35
+ });
36
+ const groups = [...bindingGroups, ...structuralGroups];
37
+ const assertions = groups.flatMap((group) => group.assertions);
38
+ const assertionModesMatch = (items, targets, expectedMode) => {
39
+ const relevant = items.filter((item) => item.targets.some((target) => targets.includes(target)));
40
+ return Boolean(expectedMode) && relevant.length > 0 && relevant.every((item) => item.mode === expectedMode);
41
+ };
42
+ const mappingOk = input.requestedErrors.length <= 1 || Boolean(input.requestedErrorMapping
43
+ && groups.length > 0
44
+ && bindingGroups.every(({ binding, allAssertions }) => assertionsProveErrorMapping(
45
+ allAssertions, binding.testNames, input.requestedErrorMapping
46
+ ))
47
+ && structuralGroups.every(({ target, allAssertions }) => assertionsProveErrorMapping(
48
+ allAssertions, [`*.${target}`], input.requestedErrorMapping
49
+ )));
50
+ const targetOk = assertions.length > 0
51
+ && bindingGroups.every(({ binding, assertions: items }) => binding.testNames.length > 0 && items.length > 0)
52
+ && structuralGroups.every(({ assertions: items }) => items.length > 0);
53
+ const modeOk = bindingGroups.every(({ binding, assertions: items }) => assertionModesMatch(
54
+ items, binding.testNames, callableAssertionMode(input.bodyMaps.get(binding.sourcePath) ?? new Map(), binding.sourceName)
55
+ )) && structuralGroups.every(({ target, assertions: items }) => {
56
+ const matches = [...input.bodyMaps.values()].filter((bodies) => bodies.has(target));
57
+ return assertionModesMatch(items, [`*.${target}`], matches.length === 1 ? callableAssertionMode(matches[0], target) : undefined);
58
+ });
59
+ return { assertions, mappingOk, modeOk, targetOk };
60
+ }