@zaip-ai/cli 1.3.0 → 1.3.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 (258) hide show
  1. package/README.md +1 -1
  2. package/dist/services/agent-adapters.js +1 -1
  3. package/dist/services/agent-adapters.js.map +1 -1
  4. package/dist/services/agent-adapters.test.js +2 -1
  5. package/dist/services/agent-adapters.test.js.map +1 -1
  6. package/dist/services/codex-pre-tool-hook-template.d.ts.map +1 -1
  7. package/dist/services/codex-pre-tool-hook-template.js +89 -7
  8. package/dist/services/codex-pre-tool-hook-template.js.map +1 -1
  9. package/dist/services/codex-rules.d.ts +1 -1
  10. package/dist/services/codex-rules.d.ts.map +1 -1
  11. package/dist/services/codex-rules.js +7 -0
  12. package/dist/services/codex-rules.js.map +1 -1
  13. package/dist/services/install-agents-template.d.ts.map +1 -1
  14. package/dist/services/install-agents-template.js +1 -0
  15. package/dist/services/install-agents-template.js.map +1 -1
  16. package/dist/services/runtime-template.test.js +31 -7
  17. package/dist/services/runtime-template.test.js.map +1 -1
  18. package/dist/services/template-manifest.d.ts.map +1 -1
  19. package/dist/services/template-manifest.js +7 -3
  20. package/dist/services/template-manifest.js.map +1 -1
  21. package/package.json +1 -1
  22. package/templates/.agents/config/agents/catalog.yaml +7 -7
  23. package/templates/.agents/config/clients/codex/agents/zaip-agent-backend.toml +2 -2
  24. package/templates/.agents/config/clients/codex/agents/zaip-agent-designer.toml +2 -2
  25. package/templates/.agents/config/clients/codex/agents/zaip-agent-frontend.toml +8 -8
  26. package/templates/.agents/dashboard/.zaip-managed.json +373 -205
  27. package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-DxR_pgSV.js → CostCalculationGuidePage-ovxurkIk.js} +1 -1
  28. package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog-4jZLTWE_.js → DashboardHelpDialog-BgVgrfhQ.js} +240 -86
  29. package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-CKcsqb-r.js → PricingSettingsDialog-vSvvOHQp.js} +1 -1
  30. package/templates/.agents/dashboard/apps/frontend/dist/assets/{ProjectSettingsPage-DmmJD2Th.js → ProjectSettingsPage-zH758vfs.js} +1 -1
  31. package/templates/.agents/dashboard/apps/frontend/dist/assets/TraceabilityWorkspace-DLn-Mz2M.js +1 -0
  32. package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-CaKwQP43.css → TraceabilityWorkspace-DvvavDLY.css} +1 -1
  33. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-DKwP1stV.js +68 -0
  34. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-PoJA2PI1.css +1 -0
  35. package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-C0fj2uU9.js → icons-vendor-B_lDDHn1.js} +1 -1
  36. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-B31GMG5O.css +1 -0
  37. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-CZQlsLV9.js +2 -0
  38. package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-BEsJ-FYH.js → react-vendor-B3TJrD4O.js} +1 -1
  39. package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-B6y6Gw12.js → traceability-7f1j5wx3.js} +1 -1
  40. package/templates/.agents/dashboard/apps/frontend/dist/index.html +7 -7
  41. package/templates/.agents/dashboard/apps/frontend/playwright.dom.config.ts +32 -0
  42. package/templates/.agents/dashboard/apps/frontend/src/App.tsx +132 -245
  43. package/templates/.agents/dashboard/apps/frontend/src/AppOverlays.tsx +0 -21
  44. package/templates/.agents/dashboard/apps/frontend/src/DashboardAlternateContent.tsx +9 -0
  45. package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.dom.e2e.ts +56 -0
  46. package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.test.ts +18 -1
  47. package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.tsx +75 -8
  48. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.test.ts +25 -1
  49. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.ts +7 -9
  50. package/templates/.agents/dashboard/apps/frontend/src/operations-period-render.test.ts +4 -4
  51. package/templates/.agents/dashboard/apps/frontend/src/overview-status-page.test.ts +1 -1
  52. package/templates/.agents/dashboard/apps/frontend/src/snapshot-banner-semantics.test.ts +7 -0
  53. package/templates/.agents/dashboard/apps/frontend/src/snapshot-coherence.test.ts +20 -8
  54. package/templates/.agents/dashboard/apps/frontend/src/sqlite-page-export.test.ts +32 -0
  55. package/templates/.agents/dashboard/apps/frontend/src/sqlite-page-export.ts +21 -0
  56. package/templates/.agents/dashboard/apps/frontend/src/styles.css +19 -0
  57. package/templates/.agents/dashboard/apps/frontend/src/traceability-task-pagination.test.ts +32 -0
  58. package/templates/.agents/dashboard/apps/frontend/src/traceability-task-pagination.ts +36 -0
  59. package/templates/.agents/dashboard/apps/frontend/src/traceability-workspace.css +6 -2
  60. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.test.ts +48 -0
  61. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.ts +31 -27
  62. package/templates/.agents/dashboard/apps/frontend/src/use-app-trace-documents.ts +7 -1
  63. package/templates/.agents/dashboard/apps/frontend/src/use-app-work-list-page.test.ts +163 -0
  64. package/templates/.agents/dashboard/apps/frontend/src/use-app-work-list-page.ts +136 -0
  65. package/templates/.agents/dashboard/apps/frontend/src/use-operations-detail-export.ts +76 -0
  66. package/templates/.agents/dashboard/apps/frontend/src/work-list-load-dedup.test.ts +23 -0
  67. package/templates/.agents/dashboard/apps/frontend/src/work-list-load-dedup.ts +27 -0
  68. package/templates/.agents/dashboard/apps/frontend/src/work-list-pagination.test.ts +38 -14
  69. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostDeveloperAttributionPanel.tsx +60 -0
  70. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostDeveloperAttributionTable.tsx +191 -0
  71. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostFilters.loading.test.ts +14 -0
  72. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostFilters.run-page.test.ts +18 -0
  73. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostFilters.tsx +18 -3
  74. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorContent.tsx +8 -2
  75. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorLoadErrors.tsx +35 -0
  76. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.test.ts +47 -1
  77. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.tsx +50 -58
  78. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTableLoading.test.ts +132 -0
  79. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTableLoading.tsx +70 -0
  80. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.test.ts +128 -138
  81. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.tsx +17 -67
  82. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-copy.ts +2 -0
  83. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-views.ts +3 -1
  84. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-page-range.ts +35 -0
  85. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-compact-cost-page.ts +16 -4
  86. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-group-page.test.ts +37 -2
  87. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-group-page.ts +44 -8
  88. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-run-page.ts +6 -1
  89. package/templates/.agents/dashboard/modules/operations/src/contracts.ts +20 -3
  90. package/templates/.agents/dashboard/modules/operations/src/react/DeveloperAttributionPanel.test.ts +54 -0
  91. package/templates/.agents/dashboard/modules/operations/src/react/DeveloperAttributionPanel.tsx +247 -0
  92. package/templates/.agents/dashboard/modules/operations/src/react/OperationsMainContent.tsx +39 -13
  93. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.helpers.tsx +107 -2
  94. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.macro-metrics.test.ts +55 -0
  95. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.test.ts +20 -28
  96. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.tsx +162 -165
  97. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverviewTopPanel.tsx +127 -0
  98. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPage.tsx +13 -46
  99. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPageOverview.tsx +3 -0
  100. package/templates/.agents/dashboard/modules/operations/src/react/PagedWorkList.test.tsx +43 -0
  101. package/templates/.agents/dashboard/modules/operations/src/react/PagedWorkList.tsx +181 -0
  102. package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkList.test.ts +4 -2
  103. package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkList.tsx +13 -0
  104. package/templates/.agents/dashboard/modules/operations/src/react/developer-identities.test.ts +18 -0
  105. package/templates/.agents/dashboard/modules/operations/src/react/developer-identities.ts +30 -0
  106. package/templates/.agents/dashboard/modules/operations/src/react/execution-metrics.ts +1 -0
  107. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.test.ts +8 -0
  108. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.tsx +3 -0
  109. package/templates/.agents/dashboard/modules/operations/src/react/export/export-layout.test.ts +14 -5
  110. package/templates/.agents/dashboard/modules/operations/src/react/index.ts +1 -0
  111. package/templates/.agents/dashboard/modules/operations/src/react/operations-coverage-toast.test.ts +4 -3
  112. package/templates/.agents/dashboard/modules/operations/src/react/operations-date-filters.css +1 -1
  113. package/templates/.agents/dashboard/modules/operations/src/react/operations-overview-period-filter.test.ts +21 -11
  114. package/templates/.agents/dashboard/modules/operations/src/react/operations-overview.css +4 -0
  115. package/templates/.agents/dashboard/modules/operations/src/react/operations-page-contract.ts +8 -1
  116. package/templates/.agents/dashboard/modules/operations/src/react/operations-view-order.test.ts +1 -0
  117. package/templates/.agents/dashboard/modules/operations/src/react/period-summary.ts +1 -0
  118. package/templates/.agents/dashboard/modules/operations/src/server/metric-coverage.ts +1 -0
  119. package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +527 -335
  120. package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +501 -309
  121. package/templates/.agents/dashboard/packages/local-runtime/gitignore.template +2 -0
  122. package/templates/.agents/dashboard/packages/local-runtime/scripts/rebuild-sqlite-read-model.mjs +3 -1
  123. package/templates/.agents/dashboard/packages/local-runtime/scripts/test-sqlite-read-model.mjs +3 -3
  124. package/templates/.agents/dashboard/packages/local-runtime/scripts/validate-sqlite-read-model.mjs +25 -15
  125. package/templates/.agents/dashboard/packages/local-runtime/server.mjs +527 -335
  126. package/templates/.agents/dashboard/packages/local-runtime/src/server-board.test.ts +34 -0
  127. package/templates/.agents/dashboard/packages/local-runtime/src/server-board.ts +15 -8
  128. package/templates/.agents/dashboard/packages/local-runtime/src/server-classification-page.test.ts +87 -0
  129. package/templates/.agents/dashboard/packages/local-runtime/src/server-period-macro-metric.test.ts +44 -0
  130. package/templates/.agents/dashboard/packages/local-runtime/src/server-period-macro-metric.ts +125 -0
  131. package/templates/.agents/dashboard/packages/local-runtime/src/server-reconciliation-refresh.test.ts +66 -0
  132. package/templates/.agents/dashboard/packages/local-runtime/src/server-request-authority.test.ts +61 -0
  133. package/templates/.agents/dashboard/packages/local-runtime/src/server-request-authority.ts +18 -0
  134. package/templates/.agents/dashboard/packages/local-runtime/src/server-run-projections.test.ts +42 -0
  135. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-refresh.ts +97 -0
  136. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot.test.ts +3 -1
  137. package/templates/.agents/dashboard/packages/local-runtime/src/server-sqlite-task-trace.ts +91 -0
  138. package/templates/.agents/dashboard/packages/local-runtime/src/server.test-fixtures.ts +36 -0
  139. package/templates/.agents/dashboard/packages/local-runtime/src/server.test.ts +98 -123
  140. package/templates/.agents/dashboard/packages/local-runtime/src/server.ts +111 -135
  141. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-artifact-counts.ts +28 -0
  142. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-agent-page.test.ts +63 -0
  143. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-agent-page.ts +109 -0
  144. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-aggregates.ts +497 -0
  145. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-fast-page-parity.test-support.ts +65 -0
  146. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-group-page.test.ts +176 -0
  147. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-group-page.ts +119 -0
  148. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-group-projection.ts +41 -0
  149. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-page.test.ts +16 -0
  150. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-page.ts +29 -0
  151. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-first-fold-cache.ts +355 -0
  152. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-first-fold-persistence.ts +121 -0
  153. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-first-fold-timing.ts +27 -0
  154. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-operations-projection.ts +15 -0
  155. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-operations-work-list.test.ts +223 -0
  156. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-operations-work-list.ts +304 -0
  157. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-cache.test-support.ts +23 -0
  158. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-first-fold-types.ts +89 -0
  159. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider-fixture.ts +227 -0
  160. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider.test.ts +691 -0
  161. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-source-fingerprint.ts +81 -0
  162. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.test.ts +440 -863
  163. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.ts +1120 -1086
  164. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-cache.ts +129 -0
  165. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-install.test.ts +26 -0
  166. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-install.ts +28 -0
  167. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-seed.test.ts +116 -0
  168. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.mjs +146 -0
  169. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.test.mjs +165 -0
  170. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-refresh.mjs +140 -0
  171. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.mjs +188 -299
  172. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.test.mjs +51 -70
  173. package/templates/.agents/dashboard/packages/windows-launcher/Program.cs +5 -2
  174. package/templates/.agents/dashboard/tests/e2e/playwright.config.ts +4 -1
  175. package/templates/.agents/dashboard/tests/e2e/specs/cost-monitor-loading.spec.ts +142 -0
  176. package/templates/.agents/dashboard/tests/e2e/specs/cost-monitor-pagination.spec.ts +232 -0
  177. package/templates/.agents/dashboard/tests/e2e/specs/dashboard-performance-matrix.spec.ts +743 -0
  178. package/templates/.agents/dashboard/tests/e2e/specs/dashboard.spec.ts +183 -210
  179. package/templates/.agents/dashboard/zaip-dashboard-windows.exe +0 -0
  180. package/templates/.agents/requirements/autonomous-assurance.md +6 -6
  181. package/templates/.agents/requirements/comparative-audit.md +1 -1
  182. package/templates/.agents/requirements/context-engine.md +1 -1
  183. package/templates/.agents/requirements/cost-observability.md +1 -1
  184. package/templates/.agents/requirements/dashboard-launch.md +1 -1
  185. package/templates/.agents/requirements/data-export.md +1 -1
  186. package/templates/.agents/requirements/design-system.md +1 -1
  187. package/templates/.agents/requirements/execution-engine.md +3 -3
  188. package/templates/.agents/requirements/kanban-dashboard.md +10 -2
  189. package/templates/.agents/requirements/model-orchestration.md +1 -1
  190. package/templates/.agents/requirements/public-distribution.md +1 -1
  191. package/templates/.agents/requirements/software-architecture.md +1 -1
  192. package/templates/.agents/requirements/strategic-improvements.md +1 -1
  193. package/templates/.agents/requirements/work-lifecycle.md +4 -4
  194. package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
  195. package/templates/.agents/rules/dashboard-ui-consistency.md +14 -0
  196. package/templates/.agents/rules/frontend-lite.md +4 -4
  197. package/templates/.agents/rules/run-lifecycle-closure.md +4 -4
  198. package/templates/.agents/runtime/bin/zaip.mjs +1137 -1037
  199. package/templates/.agents/runtime/checksums.sha256 +2 -2
  200. package/templates/.agents/runtime/provenance.json +2 -2
  201. package/templates/.agents/skills/criar-pasta-obsidian/scripts/generate-obsidian-vault.test.mjs +20 -16
  202. package/templates/.agents/skills/zaip-skill-agent-catalog/SKILL.md +22 -0
  203. package/templates/.agents/skills/zaip-skill-agent-catalog/agents/openai.yaml +4 -0
  204. package/templates/.agents/skills/zaip-skill-author-skills/SKILL.md +3 -2
  205. package/templates/.agents/skills/zaip-skill-author-skills/scripts/audit-skill-catalog.mjs +49 -0
  206. package/templates/.agents/skills/zaip-skill-author-skills/scripts/author-skill-scripts.test.mjs +27 -0
  207. package/templates/.agents/skills/zaip-skill-author-skills/scripts/run-skill-tests.mjs +37 -0
  208. package/templates/.agents/skills/zaip-skill-backend/SKILL.md +1 -0
  209. package/templates/.agents/skills/zaip-skill-change/SKILL.md +1 -1
  210. package/templates/.agents/skills/zaip-skill-change/references/observable-evidence.md +83 -0
  211. package/templates/.agents/skills/zaip-skill-dashboard-diagnostics/SKILL.md +22 -0
  212. package/templates/.agents/skills/zaip-skill-dashboard-diagnostics/agents/openai.yaml +4 -0
  213. package/templates/.agents/skills/zaip-skill-execution/SKILL.md +1 -1
  214. package/templates/.agents/skills/zaip-skill-frontend/SKILL.md +4 -3
  215. package/templates/.agents/skills/zaip-skill-frontend/agents/openai.yaml +2 -2
  216. package/templates/.agents/skills/zaip-skill-github-deploy/SKILL.md +10 -10
  217. package/templates/.agents/skills/zaip-skill-lifecycle-closure/SKILL.md +27 -0
  218. package/templates/.agents/skills/zaip-skill-lifecycle-closure/agents/openai.yaml +4 -0
  219. package/templates/.agents/skills/zaip-skill-prd/SKILL.md +4 -0
  220. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-local-telemetry.mjs +72 -22
  221. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-explicit-recovery.mjs +223 -0
  222. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-lib.mjs +42 -1
  223. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-lib.test.mjs +101 -0
  224. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-remeasurement.test.mjs +53 -0
  225. package/templates/.agents/skills/zaip-skill-release-qualification/SKILL.md +20 -0
  226. package/templates/.agents/skills/zaip-skill-release-qualification/agents/openai.yaml +4 -0
  227. package/templates/.agents/skills/zaip-skill-review/SKILL.md +5 -0
  228. package/templates/.agents/skills/zaip-skill-rules/SKILL.md +1 -1
  229. package/templates/.agents/skills/zaip-skill-spec/SKILL.md +4 -0
  230. package/templates/.agents/skills/zaip-skill-sqlite-read-model/SKILL.md +22 -90
  231. package/templates/.agents/zaip-aios/INDEX.md +57 -32
  232. package/templates/.agents/zaip-aios/README.md +22 -12
  233. package/templates/.agents/zaip-aios/guides/02-requirements.md +5 -0
  234. package/templates/.agents/zaip-aios/guides/06-execucao.md +13 -4
  235. package/templates/.agents/zaip-aios/guides/07-revisao-e-validacao.md +7 -0
  236. package/templates/.agents/zaip-aios/guides/10-seguranca.md +23 -0
  237. package/templates/.agents/zaip-aios/guides/11-observabilidade-e-custos.md +6 -6
  238. package/templates/.agents/zaip-aios/guides/13-boas-praticas.md +3 -0
  239. package/templates/.agents/zaip-aios/guides/16-lifecycle-e-reconciliacao.md +12 -3
  240. package/templates/.agents/zaip-aios/guides/17-como-o-custo-e-calculado.md +5 -5
  241. package/templates/.agents/zaip-aios/guides/19-assurance-e-autonomia.md +23 -0
  242. package/templates/.agents/zaip-aios/guides/20-instalacao-detalhada.md +44 -8
  243. package/templates/.agents/zaip-aios/guides/21-telemetria-monitor-custos.md +8 -14
  244. package/templates/.agents/zaip-aios/guides/22-fluxos-e-roteamento.md +5 -0
  245. package/templates/.agents/zaip-aios/guides/23-orquestracao-de-modelos.md +6 -6
  246. package/templates/.agents/zaip-aios/guides/24-calculo-de-custos-openai.md +8 -8
  247. package/templates/.agents/zaip-aios/guides/25-fontes-de-precos-openai.md +3 -2
  248. package/templates/.agents/zaip-aios/guides/26-qualidade-e-release.md +77 -10
  249. package/templates/.agents/zaip-aios/guides/27-telemetria-git-native.md +8 -3
  250. package/templates/.agents/zaip-aios/guides/28-lifecycle-completo.md +5 -0
  251. package/templates/template-manifest.json +555 -170
  252. package/templates/.agents/dashboard/apps/frontend/dist/assets/TraceabilityWorkspace-BwtitWa_.js +0 -1
  253. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-BZ5WdaMN.css +0 -1
  254. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-N5Csr0RP.js +0 -68
  255. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-C4aIB1PV.js +0 -2
  256. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-DK_enB5Z.css +0 -1
  257. package/templates/.agents/dashboard/apps/frontend/src/use-idle-cost-prefetch.ts +0 -45
  258. package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs.metafile.json +0 -7333
@@ -92,6 +92,40 @@ describe("boardPage", () => {
92
92
  expect(linkedPage.tasks).toEqual([linked]);
93
93
  expect(standalonePage.totals_by_kind).toMatchObject({ task: 1, "standalone-task": 1 });
94
94
  });
95
+
96
+ it("returns every Task for an Overview status dialog", () => {
97
+ const standalone = {
98
+ id: "TASK-STANDALONE",
99
+ title: "Standalone",
100
+ status: "executing",
101
+ spec_id: null,
102
+ updated_at: "2026-08-08T10:00:00Z",
103
+ };
104
+ const linked = {
105
+ id: "TASK-LINKED",
106
+ title: "Linked",
107
+ status: "executing",
108
+ spec_id: "SPEC-1",
109
+ updated_at: "2026-08-08T10:00:00Z",
110
+ };
111
+ const projection = {
112
+ changes: [],
113
+ tasks: [standalone, linked],
114
+ prds: [],
115
+ specs: [],
116
+ } as unknown as Parameters<typeof boardPage>[0];
117
+
118
+ const page = boardPage(projection, "executing", 0, 10, [], {
119
+ query: "",
120
+ status: "",
121
+ risk: "",
122
+ origin: "",
123
+ kind: "all-tasks",
124
+ });
125
+
126
+ expect(page.total).toBe(2);
127
+ expect(page.tasks.map((task) => task.id)).toEqual([linked.id, standalone.id]);
128
+ });
95
129
  });
96
130
 
97
131
  function macro(id: string, kind: "prd" | "spec", taskIds: string[]) {
@@ -62,15 +62,20 @@ export function boardPage(
62
62
  const sourceByChange = new Map(
63
63
  projection.changes.map((change) => [change.id, change.source.type] as const),
64
64
  );
65
+ const taskOrigin = (task: (typeof projection.tasks)[number]) => {
66
+ const origin = (task as typeof task & { origin?: unknown }).origin;
67
+ return (
68
+ sourceByChange.get(task.change_id) ??
69
+ (typeof origin === "string" ? origin : task.spec_id === null ? "task" : "spec")
70
+ );
71
+ };
65
72
  const matchesQuery = (values: readonly (string | null | undefined)[]) =>
66
73
  filters.query === "" ||
67
74
  values.some((value) => value?.toLocaleLowerCase().includes(filters.query));
68
75
  const taskMatches = (task: (typeof projection.tasks)[number]) =>
69
76
  (filters.status === "" || taskWorkStage(task) === filters.status) &&
70
77
  (filters.risk === "" || task.risk === filters.risk) &&
71
- (filters.origin === "" ||
72
- (sourceByChange.get(task.change_id) ?? (task.spec_id === null ? "task" : "spec")) ===
73
- filters.origin) &&
78
+ (filters.origin === "" || taskOrigin(task) === filters.origin) &&
74
79
  matchesQuery([
75
80
  task.id,
76
81
  task.title,
@@ -116,11 +121,13 @@ export function boardPage(
116
121
  const stageItems =
117
122
  filters.kind === ""
118
123
  ? allStageItems
119
- : filters.kind === "standalone-task"
120
- ? allStageItems.filter((item) => item.kind === "task" && item.row.spec_id === null)
121
- : filters.kind === "task"
122
- ? allStageItems.filter((item) => item.kind === "task" && item.row.spec_id !== null)
123
- : allStageItems.filter((item) => item.kind === filters.kind);
124
+ : filters.kind === "all-tasks"
125
+ ? allStageItems.filter((item) => item.kind === "task")
126
+ : filters.kind === "standalone-task"
127
+ ? allStageItems.filter((item) => item.kind === "task" && item.row.spec_id === null)
128
+ : filters.kind === "task"
129
+ ? allStageItems.filter((item) => item.kind === "task" && item.row.spec_id !== null)
130
+ : allStageItems.filter((item) => item.kind === filters.kind);
124
131
  const page = stageItems.slice(offset, offset + limit);
125
132
  const ids = new Set(page.map((item) => item.row.id));
126
133
  return {
@@ -0,0 +1,87 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { classificationPage } from "./server-board.js";
3
+
4
+ describe("dashboard classification pages", () => {
5
+ it.each([
6
+ ["task", "lite", "TASK-LITE"],
7
+ ["task", "standard", "TASK-STANDARD"],
8
+ ["task", "critical", "TASK-CRITICAL"],
9
+ ["spec", "empty", "SPEC-EMPTY"],
10
+ ["spec", "structured", "SPEC-STRUCTURED"],
11
+ ["spec", "rework", "SPEC-REWORK"],
12
+ ["prd", "empty", "PRD-EMPTY"],
13
+ ["prd", "structured", "PRD-STRUCTURED"],
14
+ ["prd", "rework", "PRD-REWORK"],
15
+ ] as const)("returns only the canonical %s %s classification page", (kind, classification, expectedId) => {
16
+ const projection = {
17
+ tasks: [
18
+ { id: "TASK-LITE", risk: "lite", created_at: "2026-08-21T00:00:00.000Z", updated_at: null },
19
+ {
20
+ id: "TASK-STANDARD",
21
+ risk: "standard",
22
+ created_at: "2026-08-21T00:00:00.000Z",
23
+ updated_at: null,
24
+ },
25
+ {
26
+ id: "TASK-CRITICAL",
27
+ risk: "critical",
28
+ created_at: "2026-08-21T00:00:00.000Z",
29
+ updated_at: null,
30
+ },
31
+ ],
32
+ specs: [
33
+ {
34
+ id: "SPEC-EMPTY",
35
+ task_count: 0,
36
+ rework_run_count: 0,
37
+ created_at: "2026-08-21T00:00:00.000Z",
38
+ updated_at: null,
39
+ },
40
+ {
41
+ id: "SPEC-STRUCTURED",
42
+ task_count: 1,
43
+ rework_run_count: 0,
44
+ created_at: "2026-08-21T00:00:00.000Z",
45
+ updated_at: null,
46
+ },
47
+ {
48
+ id: "SPEC-REWORK",
49
+ task_count: 1,
50
+ rework_run_count: 1,
51
+ created_at: "2026-08-21T00:00:00.000Z",
52
+ updated_at: null,
53
+ },
54
+ ],
55
+ prds: [
56
+ {
57
+ id: "PRD-EMPTY",
58
+ task_count: 0,
59
+ rework_run_count: 0,
60
+ created_at: "2026-08-21T00:00:00.000Z",
61
+ updated_at: null,
62
+ },
63
+ {
64
+ id: "PRD-STRUCTURED",
65
+ task_count: 1,
66
+ rework_run_count: 0,
67
+ created_at: "2026-08-21T00:00:00.000Z",
68
+ updated_at: null,
69
+ },
70
+ {
71
+ id: "PRD-REWORK",
72
+ task_count: 1,
73
+ rework_run_count: 1,
74
+ created_at: "2026-08-21T00:00:00.000Z",
75
+ updated_at: null,
76
+ },
77
+ ],
78
+ changes: [],
79
+ } as unknown as Parameters<typeof classificationPage>[0];
80
+ const page = classificationPage(projection, kind, classification, 0, 1);
81
+ const items = kind === "task" ? page.tasks : kind === "spec" ? page.specs : page.prds;
82
+ expect(page).toMatchObject({ offset: 0, total: 1 });
83
+ expect(items.map((item) => item.id)).toEqual([expectedId]);
84
+ expect(kind === "task" ? page.specs : page.tasks).toEqual([]);
85
+ expect(kind === "prd" ? page.specs : page.prds).toEqual([]);
86
+ });
87
+ });
@@ -0,0 +1,44 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { boardPage } from "./server-board.js";
3
+ import { periodMacroMetric } from "./server-period-macro-metric.js";
4
+
5
+ const noBoardFilters = { query: "", status: "", risk: "", origin: "", kind: "prd" };
6
+
7
+ describe("periodMacroMetric", () => {
8
+ it("keeps a draft PRD counter and its status-dialog board page in Definition", () => {
9
+ const prd = {
10
+ id: "PRD-DRAFT",
11
+ kind: "prd",
12
+ title: "Draft PRD",
13
+ status: "draft",
14
+ lifecycle_status: "draft",
15
+ work_status: "draft",
16
+ task_ids: [],
17
+ parent_id: null,
18
+ updated_at: "2026-08-29T16:00:00Z",
19
+ };
20
+ const projection = { changes: [], tasks: [], prds: [prd], specs: [] } as unknown as Parameters<
21
+ typeof boardPage
22
+ >[0];
23
+
24
+ expect(periodMacroMetric([prd], "prd").status_counts).toEqual({ definition: 1 });
25
+ expect(boardPage(projection, "definition", 0, 10, [], noBoardFilters)).toMatchObject({
26
+ total: 1,
27
+ prds: [{ id: "PRD-DRAFT" }],
28
+ });
29
+ expect(boardPage(projection, "planning", 0, 10, [], noBoardFilters).total).toBe(0);
30
+ });
31
+
32
+ it.each([
33
+ ["spec", "draft", "planning"],
34
+ ["prd", "ready", "ready"],
35
+ ["spec", "planned", "ready"],
36
+ ["prd", "active", "executing"],
37
+ ["spec", "delivered", "completed"],
38
+ ["prd", "cancelled", "cancelled"],
39
+ ] as const)("maps %s %s to %s", (kind, status, expectedStage) => {
40
+ expect(periodMacroMetric([{ id: `${kind}-${status}`, status }], kind).status_counts).toEqual({
41
+ [expectedStage]: 1,
42
+ });
43
+ });
44
+ });
@@ -0,0 +1,125 @@
1
+ import type { OperationsProjection } from "@zaip-ai/dashboard-operations/contracts";
2
+
3
+ type AllTimeTopTaskFact = {
4
+ task_id: string;
5
+ total_duration_ms: number | null;
6
+ total_tokens: number | null;
7
+ total_cost_usd: number | null;
8
+ total_cost_brl: number | null;
9
+ total_run_count: number;
10
+ };
11
+
12
+ export function projectAllTimeTopTasks(
13
+ tasks: readonly AllTimeTopTaskFact[],
14
+ ): NonNullable<NonNullable<OperationsProjection["summary"]["overview_metrics"]>["top_tasks"]> {
15
+ return tasks.map((task) => ({
16
+ task_id: task.task_id,
17
+ duration_ms: task.total_duration_ms,
18
+ total_tokens: task.total_tokens,
19
+ total_cost_usd: task.total_cost_usd,
20
+ cost_brl: task.total_cost_brl,
21
+ run_count: task.total_run_count,
22
+ }));
23
+ }
24
+
25
+ type OverviewMacroMetric = NonNullable<
26
+ NonNullable<OperationsProjection["summary"]["overview_metrics"]>["macro_metrics"]
27
+ >["specs"];
28
+
29
+ const nullableMetric = (value: unknown): number | null =>
30
+ typeof value === "number" && Number.isFinite(value) ? value : null;
31
+
32
+ type MacroKind = "spec" | "prd";
33
+
34
+ /**
35
+ * Keeps narrowed-period Overview counters aligned with the stage used by the
36
+ * board endpoint that supplies the status dialog.
37
+ */
38
+ export const periodMacroStage = (kind: MacroKind, value: unknown) => {
39
+ const status = String(value);
40
+ if (status === "draft") return kind === "prd" ? "definition" : "planning";
41
+ if (status === "ready" || status === "planned") return "ready";
42
+ if (status === "active") return "executing";
43
+ if (status === "delivered") return "completed";
44
+ return ["executing", "verifying", "completed", "blocked", "failed", "cancelled"].includes(status)
45
+ ? status
46
+ : "planning";
47
+ };
48
+
49
+ /** Projects narrowed-period SPEC/PRD rows without falling back to Task totals. */
50
+ export function periodMacroMetric(
51
+ rows: readonly Record<string, unknown>[],
52
+ kind: MacroKind,
53
+ ): OverviewMacroMetric {
54
+ const knownSum = (field: string): number | null => {
55
+ const values = rows
56
+ .map((row) => row[field])
57
+ .filter((value): value is number => typeof value === "number" && Number.isFinite(value));
58
+ return values.length === 0 ? null : values.reduce((total, value) => total + value, 0);
59
+ };
60
+ const number = (row: Record<string, unknown>, field: string) => nullableMetric(row[field]) ?? 0;
61
+ const totalDuration = knownSum("total_duration");
62
+ const totalTokens = knownSum("total_tokens");
63
+ const totalCostUsd = knownSum("total_cost_usd");
64
+ const totalCostBrl = knownSum("total_cost_brl");
65
+ const statusCounts: Record<string, number> = {};
66
+ const classificationCounts: Record<string, number> = { empty: 0, structured: 0, rework: 0 };
67
+ for (const row of rows) {
68
+ const status = periodMacroStage(kind, row.status);
69
+ statusCounts[status] = (statusCounts[status] ?? 0) + 1;
70
+ const classification =
71
+ number(row, "rework_run_count") > 0
72
+ ? "rework"
73
+ : number(row, "task_count") > 0
74
+ ? "structured"
75
+ : "empty";
76
+ classificationCounts[classification] = (classificationCounts[classification] ?? 0) + 1;
77
+ }
78
+ const topEntities = rows
79
+ .map((row) => ({
80
+ entity_id: String(row.id),
81
+ title: String(row.title ?? row.id),
82
+ duration_ms: nullableMetric(row.total_duration),
83
+ total_tokens: nullableMetric(row.total_tokens),
84
+ total_cost_usd: nullableMetric(row.total_cost_usd),
85
+ cost_brl: nullableMetric(row.total_cost_brl),
86
+ run_count: number(row, "invocation_count"),
87
+ }))
88
+ .toSorted(
89
+ (left, right) =>
90
+ (right.duration_ms ?? -1) - (left.duration_ms ?? -1) ||
91
+ left.entity_id.localeCompare(right.entity_id),
92
+ )
93
+ .slice(0, 5)
94
+ .map((entity) => ({
95
+ ...entity,
96
+ duration_share:
97
+ totalDuration === null || totalDuration === 0 || entity.duration_ms === null
98
+ ? 0
99
+ : entity.duration_ms / totalDuration,
100
+ }));
101
+ const latest =
102
+ rows
103
+ .flatMap((row) => (typeof row.occurred_at === "string" ? [row.occurred_at] : []))
104
+ .toSorted()
105
+ .at(-1) ?? null;
106
+ return {
107
+ status_counts: statusCounts,
108
+ classification_counts: classificationCounts,
109
+ entity_count: rows.length,
110
+ total_run_count: rows.reduce((total, row) => total + number(row, "invocation_count"), 0),
111
+ total_duration_ms: totalDuration,
112
+ total_tokens: totalTokens,
113
+ total_cost_usd: totalCostUsd,
114
+ total_cost_brl: totalCostBrl,
115
+ average_duration_ms:
116
+ totalDuration === null || rows.length === 0 ? null : totalDuration / rows.length,
117
+ average_tokens: totalTokens === null || rows.length === 0 ? null : totalTokens / rows.length,
118
+ average_cost_usd:
119
+ totalCostUsd === null || rows.length === 0 ? null : totalCostUsd / rows.length,
120
+ average_cost_brl:
121
+ totalCostBrl === null || rows.length === 0 ? null : totalCostBrl / rows.length,
122
+ latest_created_at: latest,
123
+ top_entities: topEntities,
124
+ };
125
+ }
@@ -0,0 +1,66 @@
1
+ import { resolve } from "node:path";
2
+ import { WorkspaceFileSystem, type WorkspaceLayout } from "@zaip-ai/core";
3
+ import { describe, expect, it, vi } from "vitest";
4
+ import { createDashboardApp, reconciliationNeedsRefresh } from "./server.js";
5
+
6
+ const workspaceRoot = resolve(import.meta.dirname, "../../../../..");
7
+ const v2Layout = { version: 3 } as WorkspaceLayout;
8
+
9
+ describe("reconciliation snapshot refresh", () => {
10
+ it("refreshes only when reconciliation changed canonical work", () => {
11
+ expect(
12
+ reconciliationNeedsRefresh({ reconciled_ids: [], blocked: [], not_reconciled: [] }),
13
+ ).toBe(false);
14
+ expect(
15
+ reconciliationNeedsRefresh({
16
+ reconciled_ids: [],
17
+ blocked: [{ id: "TASK-BLOCKED", reason: "Finalization evidence is missing." }],
18
+ not_reconciled: [],
19
+ }),
20
+ ).toBe(true);
21
+ expect(
22
+ reconciliationNeedsRefresh({
23
+ reconciled_ids: ["TASK-CHANGED"],
24
+ blocked: [],
25
+ not_reconciled: [],
26
+ }),
27
+ ).toBe(true);
28
+ });
29
+
30
+ it("serves health while a changed reconciliation refreshes in a worker", async () => {
31
+ let rejectRefresh: ((error: Error) => void) | undefined;
32
+ const refresh = vi.fn(
33
+ () =>
34
+ new Promise<void>((_resolveRefresh, reject) => {
35
+ rejectRefresh = reject;
36
+ }),
37
+ );
38
+ const app = createDashboardApp(workspaceRoot, "test-session", undefined, {
39
+ snapshotReadOnly: true,
40
+ canonicalFilesystem: new WorkspaceFileSystem(workspaceRoot),
41
+ reconcileOpenWork: async () => ({
42
+ reconciled_ids: ["TASK-CHANGED"],
43
+ blocked: [],
44
+ not_reconciled: [],
45
+ }),
46
+ refreshReadModel: refresh,
47
+ layout: v2Layout,
48
+ });
49
+ const reconciliation = app.request("/api/v2/reconcile-open-work", {
50
+ method: "POST",
51
+ headers: { "x-zaip-session": "test-session" },
52
+ });
53
+ await vi.waitFor(() => expect(refresh).toHaveBeenCalledOnce());
54
+ await expect(
55
+ app.request("/api/v2/health", { headers: { "x-zaip-session": "test-session" } }),
56
+ ).resolves.toMatchObject({
57
+ status: 200,
58
+ });
59
+ rejectRefresh?.(new Error("simulated worker refresh failure"));
60
+ const response = await reconciliation;
61
+ expect(response.status).toBe(400);
62
+ await expect(response.json()).resolves.toMatchObject({
63
+ error: "simulated worker refresh failure",
64
+ });
65
+ });
66
+ });
@@ -0,0 +1,61 @@
1
+ import { resolve } from "node:path";
2
+ import { describe, expect, it } from "vitest";
3
+ import { createDashboardApp } from "./server.js";
4
+ import { isAllowedDashboardAuthority } from "./server-request-authority.js";
5
+
6
+ const workspaceRoot = resolve(import.meta.dirname, "../../../../..");
7
+
8
+ describe("dashboard loopback authority", () => {
9
+ it.each([
10
+ ["http://127.0.0.1:4317/", undefined],
11
+ ["http://localhost/", "localhost"],
12
+ ["http://localhost:4317/", "localhost:4317"],
13
+ ["http://127.0.0.1:4317/", "127.0.0.1:4317"],
14
+ ])("accepts a loopback authority %s / %s", (url, host) => {
15
+ expect(isAllowedDashboardAuthority(url, host)).toBe(true);
16
+ });
17
+
18
+ it.each([
19
+ ["http://untrusted.example:4317/", undefined],
20
+ ["http://localhost.evil.invalid/", undefined],
21
+ ["http://localhost/", "untrusted.example:4317"],
22
+ ["http://localhost/", "localhost:99999"],
23
+ ["http://localhost/", "localhost/evil"],
24
+ ["http://localhost/", "localhost@evil.invalid"],
25
+ ["http://localhost/", "localhost,evil.invalid"],
26
+ ["http://user:synthetic@localhost/", undefined],
27
+ ["https://localhost/", undefined],
28
+ ["not-a-url", undefined],
29
+ ])("rejects untrusted or malformed authority %s / %s", (url, host) => {
30
+ expect(isAllowedDashboardAuthority(url, host)).toBe(false);
31
+ });
32
+
33
+ it("rejects arbitrary hosts before bootstrap and API even with a valid session", async () => {
34
+ const app = createDashboardApp(workspaceRoot, "test-session");
35
+ for (const path of ["/", "/index.html", "/api/v2/fx-rate", "/api/v2/health"]) {
36
+ const response = await app.request(`http://untrusted.example:4317${path}`, {
37
+ headers: { "sec-fetch-site": "same-origin", "x-zaip-session": "test-session" },
38
+ });
39
+ expect(response.status).toBe(403);
40
+ expect(response.headers.get("set-cookie")).toBeNull();
41
+ }
42
+ const spoofed = await app.request("http://localhost/", {
43
+ headers: { host: "untrusted.example", "sec-fetch-site": "none" },
44
+ });
45
+ expect(spoofed.status).toBe(403);
46
+ expect(spoofed.headers.get("set-cookie")).toBeNull();
47
+ });
48
+
49
+ it("preserves normal local bootstrap and cross-site denial", async () => {
50
+ const app = createDashboardApp(workspaceRoot, "test-session");
51
+ const local = await app.request("http://127.0.0.1:4317/", {
52
+ headers: { "sec-fetch-site": "none" },
53
+ });
54
+ expect(local.status).not.toBe(403);
55
+ expect(local.headers.get("set-cookie")).toContain("HttpOnly");
56
+ const crossSite = await app.request("http://127.0.0.1:4317/", {
57
+ headers: { "sec-fetch-site": "cross-site", origin: "http://untrusted.example" },
58
+ });
59
+ expect(crossSite.headers.get("set-cookie")).toBeNull();
60
+ });
61
+ });
@@ -0,0 +1,18 @@
1
+ /** The dashboard is a loopback service, not a reverse-proxy/public host endpoint. */
2
+ export function isAllowedDashboardAuthority(requestUrl: string, hostHeader?: string): boolean {
3
+ const allowedHost = /^(?:localhost|127\.0\.0\.1)(?::[0-9]{1,5})?$/i;
4
+ try {
5
+ const url = new URL(requestUrl);
6
+ if (url.protocol !== "http:" || url.username || url.password || !allowedHost.test(url.host)) {
7
+ return false;
8
+ }
9
+ if (hostHeader !== undefined) {
10
+ if (!allowedHost.test(hostHeader)) return false;
11
+ // Parse as well to reject invalid port numbers, not merely malformed syntax.
12
+ new URL(`http://${hostHeader}`);
13
+ }
14
+ return true;
15
+ } catch {
16
+ return false;
17
+ }
18
+ }
@@ -0,0 +1,42 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { compactRunProjection, runTimestampInRange } from "./server-projections.js";
3
+
4
+ describe("dashboard run projections", () => {
5
+ it("keeps Runs on the final all-time summary day inside the page-first range", () => {
6
+ expect(runTimestampInRange("2026-08-07T23:59:59.000Z", "2026-08-01", "2026-08-07")).toBe(true);
7
+ expect(runTimestampInRange("2026-08-08T00:00:00.000Z", "2026-08-01", "2026-08-07")).toBe(false);
8
+ });
9
+
10
+ it("keeps a lifecycle Run with no InvocationSummary as a null-telemetry page row", () => {
11
+ const run = compactRunProjection({
12
+ id: "RUN-NO-SUMMARY",
13
+ started_at: "2026-08-07T02:59:59.999Z",
14
+ finished_at: null,
15
+ task_id: null,
16
+ execution_id: null,
17
+ conversation_id: null,
18
+ agent: null,
19
+ agent_name: null,
20
+ provider: null,
21
+ model: null,
22
+ spec_id: null,
23
+ prd_id: null,
24
+ change_id: null,
25
+ phase: "execution",
26
+ parent_run_id: null,
27
+ status: "completed",
28
+ duration_ms: null,
29
+ total_tokens: null,
30
+ cost: null,
31
+ currency: null,
32
+ events: null,
33
+ });
34
+ expect(run).toMatchObject({
35
+ id: "RUN-NO-SUMMARY",
36
+ duration_ms: null,
37
+ source_duration_ms: null,
38
+ });
39
+ expect(run.usage.total.tokens.total).toBeNull();
40
+ expect(run.usage.total.cost.brl).toBeNull();
41
+ });
42
+ });
@@ -0,0 +1,97 @@
1
+ import { spawn } from "node:child_process";
2
+ import { randomBytes } from "node:crypto";
3
+ import { existsSync } from "node:fs";
4
+ import { rm } from "node:fs/promises";
5
+ import { dirname, resolve } from "node:path";
6
+ import {
7
+ type SqliteReadModelFileSystem,
8
+ sqliteReadModelPath,
9
+ validateSqliteReadModelSchema,
10
+ } from "./sqlite-read-model.js";
11
+ import { activateStagedSqliteSnapshot, stageSqliteSnapshot } from "./sqlite-snapshot-install.js";
12
+
13
+ /** Rebuilds an isolated SQLite generation so the serving event loop stays available. */
14
+ export async function rebuildSnapshotInWorker(workspaceRoot: string, currentPath: string) {
15
+ const target = resolve(
16
+ dirname(currentPath),
17
+ `refresh-server-${process.pid}-${randomBytes(4).toString("hex")}.sqlite`,
18
+ );
19
+ // A temporary/canonical workspace contains data, not necessarily a second
20
+ // dashboard runtime checkout. The explicit candidates work for a launcher
21
+ // opened at the workspace root and for fixture tests launched from the
22
+ // runtime's source checkout, without relying on import.meta in the CJS build.
23
+ const scriptCandidates = [
24
+ resolve(
25
+ workspaceRoot,
26
+ ".agents/dashboard/packages/local-runtime/scripts/rebuild-sqlite-read-model.mjs",
27
+ ),
28
+ resolve(process.cwd(), "scripts/rebuild-sqlite-read-model.mjs"),
29
+ resolve(
30
+ process.cwd(),
31
+ ".agents/dashboard/packages/local-runtime/scripts/rebuild-sqlite-read-model.mjs",
32
+ ),
33
+ ];
34
+ const script = scriptCandidates.find((candidate) => existsSync(candidate));
35
+ if (script === undefined)
36
+ throw new Error("SQLite refresh worker script is unavailable from this runtime checkout");
37
+ await new Promise<void>((resolveWorker, rejectWorker) => {
38
+ let stderr = "";
39
+ const worker = spawn(process.execPath, [script], {
40
+ cwd: workspaceRoot,
41
+ env: {
42
+ ...process.env,
43
+ // The script prioritizes ZAIP_WORKSPACE over cwd. Do not inherit a
44
+ // launcher's root when this refresh was requested for another
45
+ // canonical workspace (notably an isolated test fixture).
46
+ ZAIP_WORKSPACE: workspaceRoot,
47
+ ZAIP_SQLITE_DB_PATH: target,
48
+ ZAIP_SQLITE_PREFER_LAST_VALID_SEED: "0",
49
+ ZAIP_DASHBOARD_LAUNCH_PROGRESS: "0",
50
+ },
51
+ stdio: ["ignore", "ignore", "pipe"],
52
+ windowsHide: true,
53
+ });
54
+ worker.stderr?.setEncoding("utf8");
55
+ worker.stderr?.on("data", (chunk: string) => {
56
+ stderr += chunk;
57
+ });
58
+ worker.once("error", rejectWorker);
59
+ worker.once("exit", (code) =>
60
+ code === 0
61
+ ? resolveWorker()
62
+ : rejectWorker(
63
+ new Error(`SQLite refresh worker failed (${code}): ${stderr.trim() || "no stderr"}`),
64
+ ),
65
+ );
66
+ });
67
+ return target;
68
+ }
69
+
70
+ export async function removeRefreshSnapshot(path: string) {
71
+ await rm(path, { force: true }).catch(() => {});
72
+ }
73
+
74
+ /**
75
+ * Builds and validates a replacement outside the serving process, then asks
76
+ * the read-model filesystem to perform only the short close/rename/reopen
77
+ * handoff. The current immutable generation remains readable while staging.
78
+ */
79
+ export async function refreshSqliteSnapshotInWorker(
80
+ workspaceRoot: string,
81
+ filesystem: SqliteReadModelFileSystem,
82
+ ) {
83
+ const destination = sqliteReadModelPath(workspaceRoot);
84
+ const refreshedPath = await rebuildSnapshotInWorker(workspaceRoot, destination);
85
+ try {
86
+ const stagedPath = await stageSqliteSnapshot(
87
+ refreshedPath,
88
+ destination,
89
+ validateSqliteReadModelSchema,
90
+ );
91
+ await filesystem.rebuildAndRefresh(async () => {
92
+ activateStagedSqliteSnapshot(stagedPath, destination);
93
+ });
94
+ } finally {
95
+ await removeRefreshSnapshot(refreshedPath);
96
+ }
97
+ }
@@ -582,7 +582,9 @@ describe("Cost Monitor daily summary route", () => {
582
582
  it("uses only the bounded SQL aggregate for the Cost first fold", () => {
583
583
  const source = readFileSync(new URL("./server.ts", import.meta.url), "utf8");
584
584
  const start = source.indexOf("async function firstFoldCostSource(");
585
- const end = source.indexOf("function buildSqliteTaskTrace(", start);
585
+ const end = source.indexOf("export function createDashboardApp(", start);
586
+ expect(start).toBeGreaterThanOrEqual(0);
587
+ expect(end).toBeGreaterThan(start);
586
588
  const firstFoldCostSource = source.slice(start, end);
587
589
  expect(firstFoldCostSource).toContain("costMonitorSummaryAggregate(filters)");
588
590
  expect(firstFoldCostSource).not.toContain("costMonitorFinancialActivityRows(");