@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
@@ -11,6 +11,13 @@ const data = oracle.graderData;
11
11
  const checks = [];
12
12
  async function check(id, action) { try { await action(); checks.push({ id, passed: true }); } catch { checks.push({ id, passed: false }); } }
13
13
  async function load(file) { const url = pathToFileURL(path.join(workspace, file)); url.searchParams.set("benchmark", `${Date.now()}-${Math.random()}`); return import(url.href); }
14
+ function canonicalJson(value) {
15
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
16
+ if (value && typeof value === "object") {
17
+ return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(value[key])}`).join(",")}}`;
18
+ }
19
+ return JSON.stringify(value);
20
+ }
14
21
 
15
22
  switch (scenario) {
16
23
  case "revision-event-reconciliation": {
@@ -109,29 +116,97 @@ switch (scenario) {
109
116
  }
110
117
  case "resumable-stream-assembly": {
111
118
  const { assembleStream } = await load("src/stream.js");
112
- await check("interleaved-replay-and-gap", () => {
113
- const snapshot = { cursor: 2, messages: [{ id: data.message, text: data.first, complete: false }] };
119
+ await check("contiguous-interleaving-exact-duplicates-and-order", () => {
120
+ const snapshot = { cursor: data.start, messages: [{ id: data.message, text: data.first, complete: false }] };
121
+ const first = {
122
+ cursor: data.start + 1,
123
+ messageId: data.message,
124
+ offset: data.first.length,
125
+ text: data.second,
126
+ complete: false
127
+ };
128
+ const second = {
129
+ cursor: data.start + 2,
130
+ messageId: data.other,
131
+ offset: 0,
132
+ text: data.third,
133
+ complete: false
134
+ };
135
+ const final = {
136
+ cursor: data.start + 3,
137
+ messageId: data.message,
138
+ offset: (data.first + data.second).length,
139
+ text: "",
140
+ complete: true
141
+ };
142
+ const events = [second, first, structuredClone(second), final];
143
+ const before = structuredClone([snapshot, events]), result = assembleStream(snapshot, events);
144
+ assert.deepEqual([snapshot, events], before);
145
+ assert.equal(result.cursor, data.start + 3);
146
+ assert.deepEqual(result.messages, [
147
+ { id: data.message, text: data.first + data.second, complete: true },
148
+ { id: data.other, text: data.third, complete: false }
149
+ ]);
150
+ assert.deepEqual(result.appliedCursors, [data.start + 1, data.start + 2, data.start + 3]);
151
+ assert.deepEqual(result.replayedCursors, []);
152
+ assert.deepEqual(result.buffered, []);
153
+ assert.equal(result.gap, null);
154
+ });
155
+ await check("replay-gap-and-buffer-order", () => {
156
+ const snapshot = { cursor: data.start, messages: [] };
157
+ const replay = { cursor: data.start, messageId: "replay", offset: 0, text: "old", complete: false };
114
158
  const events = [
115
- { cursor: 5, messageId: "other", offset: 0, text: "late", complete: false },
116
- { cursor: 1, messageId: data.message, offset: 0, text: "old", complete: false },
117
- { cursor: 3, messageId: data.message, offset: data.first.length, text: data.second, complete: true }
159
+ { cursor: data.start + 3, messageId: "late-b", offset: 0, text: data.second, complete: false },
160
+ replay,
161
+ { cursor: data.start + 2, messageId: "late-a", offset: 0, text: data.first, complete: false },
162
+ structuredClone(replay)
118
163
  ];
119
164
  const before = structuredClone([snapshot, events]), result = assembleStream(snapshot, events);
120
165
  assert.deepEqual([snapshot, events], before);
121
- assert.equal(result.cursor, 3); assert.equal(result.messages[0].text, data.first + data.second); assert.equal(result.messages[0].complete, true);
122
- assert.deepEqual(result.appliedCursors, [3]); assert.deepEqual(result.replayedCursors, [1]);
123
- assert.deepEqual(result.gap, { expected: 4, observed: 5 }); assert.equal(result.buffered[0].cursor, 5);
166
+ assert.equal(result.cursor, data.start);
167
+ assert.deepEqual(result.appliedCursors, []);
168
+ assert.deepEqual(result.replayedCursors, [data.start]);
169
+ assert.deepEqual(result.buffered.map((event) => event.cursor), [data.start + 2, data.start + 3]);
170
+ assert.deepEqual(result.gap, { expected: data.start + 1, observed: data.start + 2 });
124
171
  });
125
- await check("offset-completion-and-cursor-conflicts", () => {
172
+ await check("utf16-offset-and-empty-finalization", () => {
173
+ const initial = `${data.astral}${data.first}`;
174
+ const snapshot = { cursor: 0, messages: [{ id: data.message, text: initial, complete: false }] };
175
+ const events = [{ cursor: 1, messageId: data.message, offset: initial.length, text: data.second, complete: true }];
176
+ const result = assembleStream(snapshot, events);
177
+ assert.equal(result.messages[0].text, initial + data.second);
178
+ assert.equal(result.messages[0].complete, true);
179
+ const emptyFinal = assembleStream(
180
+ { cursor: 0, messages: [{ id: data.other, text: initial, complete: false }] },
181
+ [{ cursor: 1, messageId: data.other, offset: initial.length, text: "", complete: true }]
182
+ );
183
+ assert.equal(emptyFinal.messages[0].text, initial);
184
+ assert.equal(emptyFinal.messages[0].complete, true);
185
+ });
186
+ await check("invalid-offset-rejected", () => {
126
187
  const base = { cursor: 0, messages: [] };
127
188
  assert.throws(() => assembleStream(base, [{ cursor: 1, messageId: "m", offset: 1, text: "x", complete: false }]));
189
+ });
190
+ await check("conflicting-cursor-rejected", () => {
191
+ const base = { cursor: 0, messages: [] };
128
192
  assert.throws(() => assembleStream(base, [
129
193
  { cursor: 1, messageId: "m", offset: 0, text: "x", complete: false },
130
194
  { cursor: 1, messageId: "m", offset: 0, text: "y", complete: false }
131
195
  ]));
196
+ });
197
+ await check("event-after-completion-rejected", () => {
132
198
  assert.throws(() => assembleStream({ cursor: 0, messages: [{ id: "m", text: "x", complete: true }] },
133
199
  [{ cursor: 1, messageId: "m", offset: 1, text: "", complete: true }]));
134
200
  });
201
+ await check("input-shape-validation", () => {
202
+ assert.throws(() => assembleStream({ cursor: -1, messages: [] }, []));
203
+ assert.throws(() => assembleStream({ cursor: 0, messages: [] }, [
204
+ { cursor: 1, messageId: "m", offset: Number.MAX_SAFE_INTEGER + 1, text: "x", complete: false }
205
+ ]));
206
+ assert.throws(() => assembleStream({ cursor: 0, messages: [] }, [
207
+ { cursor: 1, messageId: "m", offset: 0, text: 1, complete: false }
208
+ ]));
209
+ });
135
210
  break;
136
211
  }
137
212
  case "transactional-config-merge": {
@@ -142,7 +217,10 @@ switch (scenario) {
142
217
  const schema = { service: { type: "object", required: true }, remove: { type: "string" }, enabled: { type: "boolean", default: false } };
143
218
  const before = structuredClone([base, layers, schema]), result = planConfigTransaction(base, layers, schema);
144
219
  assert.deepEqual([base, layers, schema], before);
145
- assert.deepEqual(result.next, { service: { port: 0, stale: true, [data.key]: data.value }, enabled: false });
220
+ assert.equal(
221
+ canonicalJson(result.next),
222
+ canonicalJson({ service: { port: 0, stale: true, [data.key]: data.value }, enabled: false })
223
+ );
146
224
  assert.deepEqual(result.changes.map((item) => item.path), [...result.changes.map((item) => item.path)].sort());
147
225
  assert.ok(result.changes.some((item) => item.path === "/remove" && item.kind === "delete"));
148
226
  assert.ok(result.changes.some((item) => item.path === "/enabled" && item.kind === "add"));
@@ -158,6 +236,190 @@ switch (scenario) {
158
236
  });
159
237
  break;
160
238
  }
239
+ case "temporal-usage-billing-close": {
240
+ const { normalizePlanTimeline } = await load("packages/billing/src/plan-timeline.js");
241
+ const { closeUsagePeriod } = await load("packages/billing/src/close-period.js");
242
+ const { billingSummary } = await load("apps/admin/src/billing-summary.js");
243
+ const period = { start: "2026-08-01T00:00:00.000Z", end: "2026-09-01T00:00:00.000Z" };
244
+ const [planA1, planA2, planB1] = data.plans;
245
+ const [price1, price2, price3] = data.prices;
246
+ const tiers = (prices = data.prices) => [
247
+ { upTo: data.firstLimit, unitPriceMicros: prices[0] },
248
+ { upTo: data.secondLimit, unitPriceMicros: prices[1] },
249
+ { upTo: null, unitPriceMicros: prices[2] }
250
+ ];
251
+ const plan = ({ id, tenantId, effectiveAt, currency = "USD", meters = { [data.meter]: tiers() } }) => (
252
+ { id, tenantId, currency, effectiveAt, meters }
253
+ );
254
+ const plans = () => [
255
+ plan({ id: planA2, tenantId: data.tenantA, effectiveAt: "2026-08-15T00:00:00.000Z" }),
256
+ plan({ id: planB1, tenantId: data.tenantB, effectiveAt: "2026-07-01T00:00:00.000Z" }),
257
+ plan({ id: planA1, tenantId: data.tenantA, effectiveAt: "2026-07-01T00:00:00.000Z" })
258
+ ];
259
+ const usage = (id, tenantId, at, units, meter = data.meter) => ({ id, kind: "usage", tenantId, meter, at, units });
260
+ const reversal = (id, tenantId, at, targetId) => ({ id, kind: "reversal", tenantId, at, targetId });
261
+ const canonical = (value) => {
262
+ if (value === null || typeof value === "string" || typeof value === "boolean" || Number.isSafeInteger(value)) return JSON.stringify(value);
263
+ if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
264
+ return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`).join(",")}}`;
265
+ };
266
+ await check("period-and-timeline-normalization", () => {
267
+ const source = plans();
268
+ source[0].meters = { zeta: tiers(), [data.meter]: tiers() };
269
+ const before = structuredClone(source), normalized = normalizePlanTimeline(source, period);
270
+ assert.deepEqual(source, before);
271
+ assert.notEqual(normalized.find((item) => item.id === planA1), source[2]);
272
+ assert.deepEqual(normalized.map((item) => item.id), before.toSorted((left, right) => left.tenantId.localeCompare(right.tenantId)
273
+ || left.effectiveAt.localeCompare(right.effectiveAt) || left.id.localeCompare(right.id)).map((item) => item.id));
274
+ assert.deepEqual(Object.keys(normalized.find((item) => item.id === planA2).meters), [data.meter, "zeta"].sort());
275
+ assert.deepEqual(Object.keys(normalized[0]), ["id", "tenantId", "currency", "effectiveAt", "meters"]);
276
+ });
277
+ await check("timeline-validation-fail-closed", () => {
278
+ assert.throws(() => normalizePlanTimeline(plans(), { start: period.end, end: period.start }), TypeError);
279
+ assert.throws(() => normalizePlanTimeline([plans()[0], { ...plans()[1], id: plans()[0].id }], period), TypeError);
280
+ assert.throws(() => normalizePlanTimeline([plans()[0], { ...plans()[2], effectiveAt: plans()[0].effectiveAt }], period), TypeError);
281
+ assert.throws(() => normalizePlanTimeline([plans()[2], { ...plans()[0], currency: "EUR" }], period), TypeError);
282
+ assert.throws(() => normalizePlanTimeline([plan({ id: "bad", tenantId: data.tenantA, effectiveAt: "2026-07-01T00:00:00.000Z", meters: { [data.meter]: [{ upTo: 3, unitPriceMicros: 1 }] } })], period), TypeError);
283
+ assert.throws(() => normalizePlanTimeline([plan({ id: "bad", tenantId: data.tenantA, effectiveAt: "2026-07-01", meters: { [data.meter]: tiers() } })], period), TypeError);
284
+ });
285
+ await check("tier-boundary-and-multi-tier-allocation", () => {
286
+ const firstUnits = data.firstLimit - 1, secondUnits = data.secondLimit - data.firstLimit + 3;
287
+ const result = closeUsagePeriod({ period, plans: plans(), events: [
288
+ usage("u-1", data.tenantA, "2026-08-02T00:00:00.000Z", firstUnits),
289
+ usage("u-2", data.tenantA, "2026-08-03T00:00:00.000Z", secondUnits)
290
+ ] });
291
+ const [first, second] = result.invoices[0].lines;
292
+ assert.deepEqual(first.allocations, [{ tierIndex: 0, units: firstUnits, unitPriceMicros: String(price1), chargeMicros: String(BigInt(firstUnits) * BigInt(price1)) }]);
293
+ assert.deepEqual(second.allocations.map((item) => [item.tierIndex, item.units]), [
294
+ [0, 1], [1, data.secondLimit - data.firstLimit], [2, 2]
295
+ ]);
296
+ assert.equal(second.chargeMicros, String(BigInt(price1) + BigInt(data.secondLimit - data.firstLimit) * BigInt(price2) + 2n * BigInt(price3)));
297
+ });
298
+ await check("effective-boundary-and-plan-reset", () => {
299
+ const result = closeUsagePeriod({ period, plans: plans(), events: [
300
+ usage("before", data.tenantA, "2026-08-14T23:59:59.999Z", data.firstLimit),
301
+ usage("boundary", data.tenantA, "2026-08-15T00:00:00.000Z", data.firstLimit + 1)
302
+ ] });
303
+ const [before, boundary] = result.invoices[0].lines;
304
+ assert.equal(before.planId, planA1); assert.equal(boundary.planId, planA2);
305
+ assert.deepEqual(boundary.allocations.map((item) => [item.tierIndex, item.units]), [[0, data.firstLimit], [1, 1]]);
306
+ });
307
+ await check("tenant-and-meter-counter-isolation", () => {
308
+ const otherMeter = `${data.meter}-secondary`;
309
+ const expanded = plans().map((item) => ({ ...item, meters: { ...item.meters, [otherMeter]: tiers() } }));
310
+ const result = closeUsagePeriod({ period, plans: expanded, events: [
311
+ usage("a-main", data.tenantA, "2026-08-02T00:00:00.000Z", data.firstLimit),
312
+ usage("a-other", data.tenantA, "2026-08-03T00:00:00.000Z", data.firstLimit, otherMeter),
313
+ usage("b-main", data.tenantB, "2026-08-02T00:00:00.000Z", data.firstLimit)
314
+ ] });
315
+ assert.equal(result.invoices.length, 2);
316
+ for (const invoice of result.invoices) for (const line of invoice.lines) {
317
+ assert.deepEqual(line.allocations.map((item) => item.tierIndex), [0]);
318
+ }
319
+ });
320
+ await check("reversal-applied-before-rating", () => {
321
+ const result = closeUsagePeriod({ period, plans: plans(), events: [
322
+ reversal("reverse-first", data.tenantA, "2026-08-10T00:00:00.000Z", "charged-first"),
323
+ usage("charged-second", data.tenantA, "2026-08-03T00:00:00.000Z", data.firstLimit),
324
+ usage("charged-first", data.tenantA, "2026-08-02T00:00:00.000Z", data.firstLimit)
325
+ ] });
326
+ assert.deepEqual(result.reversedEventIds, ["charged-first"]);
327
+ assert.deepEqual(result.invoices[0].lines.map((item) => item.eventId), ["charged-second"]);
328
+ assert.deepEqual(result.invoices[0].lines[0].allocations.map((item) => item.tierIndex), [0]);
329
+ });
330
+ await check("reversal-validation", () => {
331
+ const target = usage("target", data.tenantA, "2026-08-05T00:00:00.000Z", 1);
332
+ assert.throws(() => closeUsagePeriod({ period, plans: plans(), events: [target, reversal("r", data.tenantB, "2026-08-06T00:00:00.000Z", "target")] }), TypeError);
333
+ assert.throws(() => closeUsagePeriod({ period, plans: plans(), events: [target, reversal("r", data.tenantA, "2026-08-04T00:00:00.000Z", "target")] }), TypeError);
334
+ assert.throws(() => closeUsagePeriod({ period, plans: plans(), events: [target, reversal("r1", data.tenantA, "2026-08-06T00:00:00.000Z", "target"), reversal("r2", data.tenantA, "2026-08-07T00:00:00.000Z", "target")] }), TypeError);
335
+ assert.throws(() => closeUsagePeriod({ period, plans: plans(), events: [reversal("r", data.tenantA, "2026-08-06T00:00:00.000Z", "missing")] }), TypeError);
336
+ });
337
+ await check("permutation-independent-canonical-output", () => {
338
+ const events = [
339
+ usage("z-last", data.tenantA, "2026-08-04T00:00:00.000Z", 2),
340
+ usage("a-first", data.tenantA, "2026-08-02T00:00:00.000Z", 2),
341
+ usage("b-same-time", data.tenantA, "2026-08-02T00:00:00.000Z", 2),
342
+ usage("tenant-b", data.tenantB, "2026-08-03T00:00:00.000Z", 2)
343
+ ];
344
+ const forward = closeUsagePeriod({ period, plans: plans(), events });
345
+ const reversed = closeUsagePeriod({ period, plans: plans().reverse(), events: [...events].reverse() });
346
+ assert.deepEqual(reversed, forward);
347
+ assert.deepEqual(forward.invoices.find((item) => item.tenantId === data.tenantA).lines.map((item) => item.eventId), ["a-first", "b-same-time", "z-last"]);
348
+ });
349
+ await check("bigint-charge-exactness", () => {
350
+ const units = Number.MAX_SAFE_INTEGER, unitPriceMicros = Number.MAX_SAFE_INTEGER;
351
+ const hugePlan = [plan({ id: planA1, tenantId: data.tenantA, effectiveAt: "2026-07-01T00:00:00.000Z", meters: { [data.meter]: [{ upTo: null, unitPriceMicros }] } })];
352
+ const result = closeUsagePeriod({ period, plans: hugePlan, events: [usage("huge", data.tenantA, "2026-08-02T00:00:00.000Z", units)] });
353
+ const expected = BigInt(units) * BigInt(unitPriceMicros);
354
+ assert.equal(result.invoices[0].lines[0].chargeMicros, String(expected));
355
+ assert.equal(result.invoices[0].subtotalMicros, String(expected));
356
+ });
357
+ await check("round-half-to-even-once", () => {
358
+ const closeAt = (price) => closeUsagePeriod({ period, plans: [plan({ id: planA1, tenantId: data.tenantA, effectiveAt: "2026-07-01T00:00:00.000Z", meters: { [data.meter]: [{ upTo: null, unitPriceMicros: price }] } })], events: [usage("tie", data.tenantA, "2026-08-02T00:00:00.000Z", 1)] });
359
+ assert.equal(closeAt(2_500_000).invoices[0].totalMinor, "2");
360
+ assert.equal(closeAt(3_500_000).invoices[0].totalMinor, "4");
361
+ });
362
+ await check("invoice-shape-total-and-nested-digest", () => {
363
+ const result = closeUsagePeriod({ period, plans: plans(), events: [usage("digest-line", data.tenantA, "2026-08-02T00:00:00.000Z", data.firstLimit + 2)] });
364
+ assert.deepEqual(Object.keys(result), ["period", "invoices", "reversedEventIds"]);
365
+ const { digest: observed, ...content } = result.invoices[0];
366
+ assert.equal(observed, crypto.createHash("sha256").update(canonical(content)).digest("hex"));
367
+ assert.equal(content.subtotalMicros, String(content.lines.reduce((sum, line) => sum + BigInt(line.chargeMicros), 0n)));
368
+ const mutated = structuredClone(content); mutated.lines[0].allocations[0].units += 1;
369
+ assert.notEqual(observed, crypto.createHash("sha256").update(canonical(mutated)).digest("hex"));
370
+ });
371
+ await check("input-immutability", () => {
372
+ const input = { period, plans: plans(), events: [usage("valid", data.tenantA, "2026-08-02T00:00:00.000Z", 1)] };
373
+ const before = structuredClone(input); closeUsagePeriod(input); assert.deepEqual(input, before);
374
+ const finalInvalid = { period, plans: plans(), events: [usage("ok", data.tenantA, "2026-08-02T00:00:00.000Z", 1), usage("bad", data.tenantA, period.end, 1)] };
375
+ const finalBefore = structuredClone(finalInvalid); assert.throws(() => closeUsagePeriod(finalInvalid), TypeError); assert.deepEqual(finalInvalid, finalBefore);
376
+ });
377
+ await check("event-validation", () => {
378
+ for (const invalid of [
379
+ { ...usage("x", data.tenantA, period.end, 1) },
380
+ { ...usage("x", data.tenantA, "2026-08-02T00:00:00.000Z", 1), extra: true },
381
+ { ...usage("x", data.tenantA, "2026-08-02T00:00:00.000Z", 1), units: Number.MAX_SAFE_INTEGER + 1 }
382
+ ]) assert.throws(() => closeUsagePeriod({ period, plans: plans(), events: [invalid] }), TypeError);
383
+ });
384
+ await check("missing-plan-or-meter-fails-closed", () => {
385
+ assert.throws(() => closeUsagePeriod({
386
+ period,
387
+ plans: plans(),
388
+ events: [usage("missing-meter", data.tenantA, "2026-08-02T00:00:00.000Z", 1, "missing-meter")]
389
+ }), TypeError);
390
+ assert.throws(() => closeUsagePeriod({
391
+ period,
392
+ plans: plans(),
393
+ events: [usage("missing-plan", "tenant-without-plan", "2026-08-02T00:00:00.000Z", 1)]
394
+ }), TypeError);
395
+ });
396
+ await check("non-json-and-poison-values-rejected", () => {
397
+ const sparse = []; sparse.length = 1;
398
+ assert.throws(() => normalizePlanTimeline(sparse, period), TypeError);
399
+ const poisonedMeters = JSON.parse(`{"__proto__":[{"upTo":null,"unitPriceMicros":1}]}`);
400
+ assert.throws(() => normalizePlanTimeline([plan({ id: planA1, tenantId: data.tenantA, effectiveAt: "2026-07-01T00:00:00.000Z", meters: poisonedMeters })], period), TypeError);
401
+ const invalidPrice = plans(); invalidPrice[0].meters[data.meter][0].unitPriceMicros = Number.NaN;
402
+ assert.throws(() => normalizePlanTimeline(invalidPrice, period), TypeError);
403
+ const accessorPeriod = { end: period.end }; Object.defineProperty(accessorPeriod, "start", { enumerable: true, get: () => period.start });
404
+ assert.throws(() => normalizePlanTimeline(plans(), accessorPeriod), TypeError);
405
+ const symbolPlan = plans()[0]; symbolPlan[Symbol("hidden")] = true;
406
+ assert.throws(() => normalizePlanTimeline([symbolPlan], period), TypeError);
407
+ });
408
+ await check("terminal-webui-summary-format-parity", () => {
409
+ const result = closeUsagePeriod({ period, plans: plans(), events: [usage("summary", data.tenantA, "2026-08-02T00:00:00.000Z", 1)] });
410
+ const invoice = result.invoices[0];
411
+ assert.equal(billingSummary(result), [
412
+ `period=${period.start}..${period.end}; invoices=1; reversed=0`,
413
+ `tenant=${JSON.stringify(data.tenantA)}; currency=USD; lines=1; subtotalMicros=${invoice.subtotalMicros}; totalMinor=${invoice.totalMinor}; digest=${invoice.digest}`
414
+ ].join("\n"));
415
+ });
416
+ await check("terminal-webui-summary-rejects-malformed-result", () => {
417
+ const result = closeUsagePeriod({ period, plans: plans(), events: [usage("summary-invalid", data.tenantA, "2026-08-02T00:00:00.000Z", 1)] });
418
+ const invoice = result.invoices[0];
419
+ assert.throws(() => billingSummary({ ...result, invoices: [{ ...invoice, digest: "wrong" }] }), TypeError);
420
+ });
421
+ break;
422
+ }
161
423
  default: throw new Error(`unsupported deep logic scenario ${scenario}`);
162
424
  }
163
425
 
@@ -0,0 +1,3 @@
1
+ export function billingSummary(_result) {
2
+ throw new Error("not implemented");
3
+ }
@@ -0,0 +1,166 @@
1
+ import assert from "node:assert/strict";
2
+ import { test } from "node:test";
3
+
4
+ import { billingSummary } from "../apps/admin/src/billing-summary.js";
5
+ import { closeUsagePeriod } from "../packages/billing/src/close-period.js";
6
+ import { normalizePlanTimeline } from "../packages/billing/src/plan-timeline.js";
7
+ import { planConfigTransaction } from "../src/config-transaction.js";
8
+ import { buildContextPack } from "../src/context-pack.js";
9
+ import { reconcileSession } from "../src/event-reconcile.js";
10
+ import { decideAccess } from "../src/policy.js";
11
+ import { scheduleJobs } from "../src/scheduler.js";
12
+ import { assembleStream } from "../src/stream.js";
13
+
14
+ const scenario = process.env.PIAGENT_BENCHMARK_SCENARIO;
15
+
16
+ function canonicalJson(value) {
17
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
18
+ if (value && typeof value === "object") {
19
+ return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(value[key])}`).join(",")}}`;
20
+ }
21
+ return JSON.stringify(value);
22
+ }
23
+
24
+ test("the selected benchmark entrypoint handles a fixed public happy path", { skip: !scenario }, () => {
25
+ switch (scenario) {
26
+ case "revision-event-reconciliation": {
27
+ const snapshot = { revision: "r0", nextSequence: 1, state: { keep: true } };
28
+ const events = [{ id: "e1", sequence: 1, baseRevision: "r0", revision: "r1", patch: { value: 1 } }];
29
+ const before = structuredClone([snapshot, events]);
30
+ assert.equal(canonicalJson(reconcileSession(snapshot, events)), canonicalJson({
31
+ revision: "r1",
32
+ nextSequence: 2,
33
+ state: { keep: true, value: 1 },
34
+ appliedIds: ["e1"],
35
+ duplicateIds: [],
36
+ pending: [],
37
+ gap: null
38
+ }));
39
+ assert.deepEqual([snapshot, events], before);
40
+ break;
41
+ }
42
+ case "fair-dependency-scheduler": {
43
+ const jobs = [{ id: "job-1", tenant: "tenant-a", weight: 1, dependsOn: [] }];
44
+ const before = structuredClone(jobs);
45
+ assert.deepEqual(scheduleJobs(jobs, 1), [["job-1"]]);
46
+ assert.deepEqual(jobs, before);
47
+ break;
48
+ }
49
+ case "layered-policy-resolution": {
50
+ const result = decideAccess({ path: "lib/tool.js", operation: "inspect" }, [
51
+ { name: "local", rules: [{ pattern: "lib/*", operations: ["inspect"], effect: "allow" }] }
52
+ ]);
53
+ assert.equal(result.allowed, true);
54
+ assert.equal(result.effect, "allow");
55
+ assert.equal(result.layer, "local");
56
+ assert.equal(result.ruleIndex, 0);
57
+ assert.equal(result.protected, false);
58
+ assert.equal(typeof result.reason, "string");
59
+ break;
60
+ }
61
+ case "budgeted-context-graph": {
62
+ const nodes = [{ id: "note", path: "notes/item.txt", text: "Needle note", tokens: 1 }];
63
+ const before = structuredClone(nodes);
64
+ const result = buildContextPack(
65
+ "needle",
66
+ nodes,
67
+ [],
68
+ 1
69
+ );
70
+ assert.deepEqual(result.selected.map((item) => item.id), ["note"]);
71
+ assert.equal(result.selected[0].directScore, 1);
72
+ assert.equal(result.selected[0].score, 1);
73
+ assert.equal(result.usedTokens, 1);
74
+ assert.deepEqual(result.omitted, []);
75
+ assert.equal(result.confidence, "high");
76
+ assert.deepEqual(nodes, before);
77
+ break;
78
+ }
79
+ case "resumable-stream-assembly": {
80
+ const snapshot = { cursor: 0, messages: [] };
81
+ const events = [{ cursor: 1, messageId: "m1", offset: 0, text: "hello", complete: true }];
82
+ const before = structuredClone([snapshot, events]);
83
+ assert.equal(canonicalJson(assembleStream(snapshot, events)), canonicalJson({
84
+ cursor: 1,
85
+ messages: [{ id: "m1", text: "hello", complete: true }],
86
+ appliedCursors: [1],
87
+ replayedCursors: [],
88
+ buffered: [],
89
+ gap: null
90
+ }));
91
+ assert.deepEqual([snapshot, events], before);
92
+
93
+ const openSnapshot = { cursor: 0, messages: [{ id: "open", text: "ready", complete: false }] };
94
+ const emptyFinal = [{ cursor: 1, messageId: "open", offset: 5, text: "", complete: true }];
95
+ assert.equal(canonicalJson(assembleStream(openSnapshot, emptyFinal)), canonicalJson({
96
+ cursor: 1,
97
+ messages: [{ id: "open", text: "ready", complete: true }],
98
+ appliedCursors: [1],
99
+ replayedCursors: [],
100
+ buffered: [],
101
+ gap: null
102
+ }));
103
+
104
+ const completedSnapshot = { cursor: 0, messages: [{ id: "done", text: "ready", complete: true }] };
105
+ const completedEvents = [{ cursor: 1, messageId: "done", offset: 5, text: "", complete: true }];
106
+ assert.throws(() => assembleStream(completedSnapshot, completedEvents));
107
+ break;
108
+ }
109
+ case "transactional-config-merge": {
110
+ const base = { label: "old", legacy: true };
111
+ const layers = [{ label: "new", legacy: { $delete: true } }];
112
+ const schema = {
113
+ label: { type: "string", required: true },
114
+ legacy: { type: "boolean" }
115
+ };
116
+ const before = structuredClone([base, layers, schema]);
117
+ const result = planConfigTransaction(
118
+ base,
119
+ layers,
120
+ schema
121
+ );
122
+ assert.equal(canonicalJson(result.next), canonicalJson({ label: "new" }));
123
+ assert.ok(result.changes.some((item) => item.path === "/label" && item.kind === "replace"));
124
+ assert.ok(result.changes.some((item) => item.path === "/legacy" && item.kind === "delete"));
125
+ assert.match(result.digest, /^[a-f0-9]{64}$/);
126
+ assert.deepEqual([base, layers, schema], before);
127
+ break;
128
+ }
129
+ case "temporal-usage-billing-close": {
130
+ const period = { start: "2025-01-01T00:00:00.000Z", end: "2025-02-01T00:00:00.000Z" };
131
+ const plans = [{
132
+ id: "basic",
133
+ tenantId: "customer-1",
134
+ currency: "USD",
135
+ effectiveAt: "2024-12-01T00:00:00.000Z",
136
+ meters: { requests: [{ upTo: null, unitPriceMicros: 1_000_000 }] }
137
+ }];
138
+ const normalized = normalizePlanTimeline(plans, period);
139
+ assert.equal(canonicalJson(normalized), canonicalJson(plans));
140
+ assert.notEqual(normalized[0], plans[0]);
141
+ const result = closeUsagePeriod({
142
+ period,
143
+ plans,
144
+ events: [{
145
+ id: "usage-1",
146
+ kind: "usage",
147
+ tenantId: "customer-1",
148
+ meter: "requests",
149
+ at: "2025-01-02T00:00:00.000Z",
150
+ units: 2
151
+ }]
152
+ });
153
+ assert.equal(result.invoices.length, 1);
154
+ assert.equal(result.invoices[0].subtotalMicros, "2000000");
155
+ assert.equal(result.invoices[0].totalMinor, "2");
156
+ assert.match(result.invoices[0].digest, /^[a-f0-9]{64}$/);
157
+ assert.deepEqual(billingSummary(result).split("\n"), [
158
+ `period=${period.start}..${period.end}; invoices=1; reversed=0`,
159
+ `tenant="customer-1"; currency=USD; lines=1; subtotalMicros=2000000; totalMinor=2; digest=${result.invoices[0].digest}`
160
+ ]);
161
+ break;
162
+ }
163
+ default:
164
+ assert.fail(`unsupported benchmark scenario ${scenario}`);
165
+ }
166
+ });
@@ -2,5 +2,5 @@
2
2
  "name": "piagent-deep-logic-fixture",
3
3
  "private": true,
4
4
  "type": "module",
5
- "scripts": { "test": "node --test test/*.test.js" }
5
+ "scripts": { "test": "node --test benchmark-contract/*.test.js test/*.test.js" }
6
6
  }
@@ -0,0 +1,3 @@
1
+ export function closeUsagePeriod(_input) {
2
+ throw new Error("not implemented");
3
+ }
@@ -0,0 +1,3 @@
1
+ export function normalizePlanTimeline(_plans, _period) {
2
+ throw new Error("not implemented");
3
+ }
@@ -7,6 +7,9 @@ import { reconcileSession } from "../src/event-reconcile.js";
7
7
  import { decideAccess } from "../src/policy.js";
8
8
  import { scheduleJobs } from "../src/scheduler.js";
9
9
  import { assembleStream } from "../src/stream.js";
10
+ import { billingSummary } from "../apps/admin/src/billing-summary.js";
11
+ import { closeUsagePeriod } from "../packages/billing/src/close-period.js";
12
+ import { normalizePlanTimeline } from "../packages/billing/src/plan-timeline.js";
10
13
 
11
14
  test("every benchmark entrypoint loads before private grading", () => {
12
15
  for (const entrypoint of [
@@ -15,6 +18,9 @@ test("every benchmark entrypoint loads before private grading", () => {
15
18
  reconcileSession,
16
19
  decideAccess,
17
20
  scheduleJobs,
18
- assembleStream
21
+ assembleStream,
22
+ normalizePlanTimeline,
23
+ closeUsagePeriod,
24
+ billingSummary
19
25
  ]) assert.equal(typeof entrypoint, "function");
20
26
  });
@@ -11,3 +11,5 @@ Required behavior:
11
11
  - Return `{ selected, usedTokens, omitted, confidence }`; selected/omitted entries include `id`, `path`, `tokens`, `directScore`, `score`. Confidence is `none`, `low`, `medium`, or `high` using maximum selected score thresholds 0, <0.34, <0.67, otherwise high.
12
12
 
13
13
  The hidden graph contains ties, cycles (propagation is still one hop), Unicode terms, required zero-score nodes, and candidates larger than the remaining budget.
14
+
15
+ Run `npm test` when complete; it includes a fixed public happy-path contract for this scenario.
@@ -11,3 +11,5 @@ Required invariants:
11
11
  - Never mutate the input. Every job appears exactly once, or the function throws.
12
12
 
13
13
  The hidden cases include dependencies that unlock across waves, weights that temporarily do not fit, punctuation in IDs, cycles, and three-tenant fairness. Add tests if useful.
14
+
15
+ Run `npm test` when complete; it includes a fixed public happy-path contract for this scenario.
@@ -12,3 +12,5 @@ Semantics:
12
12
  - Return `{ allowed, effect, layer, ruleIndex, protected, reason }` without exposing or changing inputs.
13
13
 
14
14
  Keep matching deterministic and do not use shell or filesystem glob expansion.
15
+
16
+ Run `npm test` when complete; it includes a fixed public happy-path contract for this scenario.
@@ -7,7 +7,9 @@ Rules:
7
7
  - Do not mutate inputs. Validate safe non-negative integer cursors/offsets and well-formed strings.
8
8
  - Starting at `snapshot.cursor + 1`, consume only a contiguous cursor prefix. Report later events as `buffered`, ordered by cursor then arrival order, and return `gap` as `null` or `{ expected, observed }`.
9
9
  - Events at or below the snapshot cursor are replay and ignored. Conflicting duplicate cursors inside the provided event list throw; exact duplicates count once.
10
- - For each consumed event, `offset` must equal the current text length of its message. A completed message cannot receive more text. The event text is appended and `complete` is sticky.
10
+ - For each consumed event, `offset` must equal the current JavaScript UTF-16 `String.length` of its message text. An empty final chunk may complete a message that is still open. Once a message is complete, any later event for it must throw, including an empty chunk. The event text is appended and `complete` is sticky.
11
11
  - Return `{ cursor, messages, appliedCursors, replayedCursors, buffered, gap }`. Preserve original message order and append newly seen messages in first-applied order.
12
12
 
13
13
  The hidden cases cover reconnect replay, interleaved messages, gaps, empty final chunks, surrogate-pair text length, and conflict rejection.
14
+
15
+ Run `npm test` when complete; it includes a fixed public happy-path contract for this scenario.
@@ -12,3 +12,5 @@ Requirements:
12
12
  - Reject malformed objects, unsafe sequence numbers, non-string IDs/revisions, arrays as patches, and prototype-pollution keys (`__proto__`, `prototype`, `constructor`). Fail closed.
13
13
 
14
14
  Add focused tests if useful. Keep the implementation dependency-free and deterministic.
15
+
16
+ Run `npm test` when complete; it includes a fixed public happy-path contract for this scenario.