@zaip-ai/cli 1.3.0 → 1.3.2

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 +3 -2
  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-BXy6nti1.js} +268 -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-CTH2wDs5.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-CuMcoUni.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 +293 -0
  156. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-operations-work-list.ts +371 -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 +92 -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
@@ -1,7 +1,6 @@
1
1
  import { createHash, randomBytes } from "node:crypto";
2
2
  import { readFile } from "node:fs/promises";
3
3
  import { resolve } from "node:path";
4
- import { fileURLToPath } from "node:url";
5
4
  import { serve } from "@hono/node-server";
6
5
  import { serveStatic } from "@hono/node-server/serve-static";
7
6
  import { loadWorkspaceLayout, WorkspaceFileSystem, type WorkspaceLayout } from "@zaip-ai/core";
@@ -18,17 +17,11 @@ import {
18
17
  readSpecDetail,
19
18
  readTaskDetail,
20
19
  } from "@zaip-ai/dashboard-operations/server";
21
- import {
22
- buildTaskTrace,
23
- buildTraceStageRecords,
24
- classifyTraceDocument,
25
- timestampsFromContent,
26
- } from "@zaip-ai/dashboard-traceability/server";
27
- import { type CompactRunMetric, LifecycleManifestStore } from "@zaip-ai/lifecycle-manifests";
20
+ import { buildTaskTrace } from "@zaip-ai/dashboard-traceability/server";
21
+ import { LifecycleManifestStore } from "@zaip-ai/lifecycle-manifests";
28
22
  import { type Context, Hono } from "hono";
29
23
  import { getCookie, setCookie } from "hono/cookie";
30
24
  import type { ContentfulStatusCode } from "hono/utils/http-status";
31
- import type { TaskTrace, TraceDocument } from "../../../modules/traceability/src/contracts.js";
32
25
  import { runAgentAdapterSync } from "./agent-adapter-sync.js";
33
26
  import {
34
27
  type AgentAdapterSyncResult,
@@ -57,21 +50,28 @@ import {
57
50
  } from "./server-operations-pages.js";
58
51
  import { registerOperationsRunsRoute } from "./server-operations-runs-route.js";
59
52
  import { projectionPeriodFilter } from "./server-period.js";
53
+ import { periodMacroMetric, projectAllTimeTopTasks } from "./server-period-macro-metric.js";
60
54
  import {
61
55
  buildCanonicalOperationsProjection,
62
56
  buildCurrentCostProjection,
63
57
  buildManifestCostRunPage,
64
58
  buildManifestFirstCostProjection,
65
- filterCostRunRows,
66
59
  isUnfilteredCostRequest,
67
60
  knownWorkIds,
68
61
  normalizeWorkPriority,
69
- pageCostRunRows,
70
62
  readCurrentFxRate,
71
63
  readWorkPriority,
72
64
  } from "./server-projections.js";
65
+ import { isAllowedDashboardAuthority } from "./server-request-authority.js";
66
+ import { refreshSqliteSnapshotInWorker } from "./server-snapshot-refresh.js";
67
+ import { buildSqliteTaskTrace } from "./server-sqlite-task-trace.js";
73
68
  import { queryValues, selectedCurrency } from "./server-summary-helpers.js";
74
- import { dashboardIdentity, dashboardWorkspaceKey, reconcileOpenWork } from "./server-workspace.js";
69
+ import {
70
+ dashboardIdentity,
71
+ dashboardWorkspaceKey,
72
+ type ReconciliationResult,
73
+ reconcileOpenWork,
74
+ } from "./server-workspace.js";
75
75
  import {
76
76
  rebuildSqliteReadModel,
77
77
  SqliteReadModelFileSystem,
@@ -80,6 +80,7 @@ import {
80
80
 
81
81
  export { manifestRun, mergeManifestRuns } from "./manifest-runs.js";
82
82
  export { projectionPeriodFilter } from "./server-period.js";
83
+ export { projectAllTimeTopTasks } from "./server-period-macro-metric.js";
83
84
  export {
84
85
  dashboardWorkspaceKey,
85
86
  githubRepositoryFromRemote,
@@ -93,28 +94,14 @@ export {
93
94
  validateSqliteReadModelSchema,
94
95
  } from "./sqlite-read-model.js";
95
96
 
96
- type AllTimeTopTaskFact = {
97
- task_id: string;
98
- total_duration_ms: number | null;
99
- total_tokens: number | null;
100
- total_cost_usd: number | null;
101
- total_cost_brl: number | null;
102
- total_run_count: number;
103
- };
104
-
105
- export function projectAllTimeTopTasks(
106
- tasks: readonly AllTimeTopTaskFact[],
107
- ): NonNullable<NonNullable<OperationsProjection["summary"]["overview_metrics"]>["top_tasks"]> {
108
- return tasks.map((task) => ({
109
- task_id: task.task_id,
110
- duration_ms: task.total_duration_ms,
111
- total_tokens: task.total_tokens,
112
- total_cost_usd: task.total_cost_usd,
113
- cost_brl: task.total_cost_brl,
114
- run_count: task.total_run_count,
115
- }));
97
+ export function reconciliationNeedsRefresh(result: ReconciliationResult): boolean {
98
+ return result.reconciled_ids.length > 0 || result.blocked.length > 0;
116
99
  }
117
100
 
101
+ type OverviewMacroMetrics = NonNullable<
102
+ NonNullable<OperationsProjection["summary"]["overview_metrics"]>["macro_metrics"]
103
+ >;
104
+
118
105
  const API_PREFIX = "/api/v2";
119
106
  const HOST = "127.0.0.1";
120
107
  const RUNTIME_KIND = "zaip-dashboard-sqlite-runtime";
@@ -171,6 +158,8 @@ interface DashboardAppOptions {
171
158
  reconcileOpenWork?: typeof reconcileOpenWork;
172
159
  refreshReadModel?: () => Promise<void>;
173
160
  snapshotState?: "current" | "updating";
161
+ /** Test seam: health and reconciliation only require the asserted V2 layout. */
162
+ layout?: WorkspaceLayout;
174
163
  }
175
164
 
176
165
  class V2WorkspaceRequiredError extends Error {
@@ -233,7 +222,6 @@ function headOrJson(
233
222
  if (context.req.method === "HEAD") return context.body(null, status);
234
223
  return context.json(payload, status);
235
224
  }
236
-
237
225
  /**
238
226
  * Keep the entity/macro details on the snapshot filesystem, while making the
239
227
  * first-fold counters and token/cost facts come from SQL.
@@ -309,15 +297,23 @@ function buildSqliteOperationsProjection(
309
297
  const allTime =
310
298
  fullAllTime ??
311
299
  (filter.from === null && filter.to === null ? filesystem.firstFoldViewportAllTime() : null);
300
+ const allTimeBoard =
301
+ filter.from === null && filter.to === null && options.includeBoardTasks === true
302
+ ? filesystem.firstFoldBoardAllTime()
303
+ : null;
312
304
  const page = <T extends "task" | "spec" | "prd">(kind: T) =>
313
- cachedMonthlyQuick?.pages[kind] ?? filesystem.summaryPage(kind, filter, 0, 10_000).rows;
305
+ cachedMonthlyQuick?.pages[kind] ??
306
+ allTimeBoard?.pages[kind] ??
307
+ filesystem.canonicalWorkPage(kind, filter, 0, 10_000).rows;
314
308
  const includeEntities = allTime === null || options.includeBoardTasks === true;
315
309
  const tasks = includeEntities ? page("task") : [];
316
310
  const specs = includeEntities ? page("spec") : [];
317
311
  const prds = includeEntities ? page("prd") : [];
318
312
  const aggregate =
319
313
  allTime?.aggregate ?? cachedMonthlyQuick?.aggregate ?? filesystem.firstFoldAggregate(filter);
320
- const taskCount = allTime?.aggregate.task_count ?? tasks.length;
314
+ const legacyDraftCount = allTime?.task_status_counts.draft ?? 0;
315
+ const taskCount =
316
+ allTime === null ? tasks.length : Math.max(0, allTime.aggregate.task_count - legacyDraftCount);
321
317
  const allTimeTopTasks =
322
318
  filter.from === null &&
323
319
  filter.to === null &&
@@ -336,7 +332,10 @@ function buildSqliteOperationsProjection(
336
332
  : projectAllTimeTopTasks(allTimeTopTasks);
337
333
  const macroMetrics =
338
334
  fullAllTime === null
339
- ? undefined
335
+ ? ({
336
+ specs: periodMacroMetric(specs, "spec"),
337
+ prds: periodMacroMetric(prds, "prd"),
338
+ } satisfies OverviewMacroMetrics)
340
339
  : (Object.fromEntries(
341
340
  (["specs", "prds"] as const).map((kind) => {
342
341
  const metric = fullAllTime.macro_metrics[kind];
@@ -372,7 +371,9 @@ function buildSqliteOperationsProjection(
372
371
  ) as NonNullable<
373
372
  NonNullable<OperationsProjection["summary"]["overview_metrics"]>["macro_metrics"]
374
373
  >);
375
- const statuses: Record<string, number> = allTime?.task_status_counts ?? {};
374
+ const statuses: Record<string, number> = Object.fromEntries(
375
+ Object.entries(allTime?.task_status_counts ?? {}).filter(([status]) => status !== "draft"),
376
+ );
376
377
  const risks: Record<string, number> = allTime?.task_risk_counts ?? {};
377
378
  if (allTime === null) {
378
379
  for (const task of tasks) {
@@ -417,7 +418,7 @@ function buildSqliteOperationsProjection(
417
418
  schema_version: 2 as const,
418
419
  id: String(row.id),
419
420
  revision: 1,
420
- change_id: "",
421
+ change_id: typeof row.change_id === "string" ? row.change_id : "",
421
422
  title: String(row.title ?? row.id),
422
423
  short_name: String(row.title ?? row.id),
423
424
  objective: "",
@@ -437,7 +438,8 @@ function buildSqliteOperationsProjection(
437
438
  result: null,
438
439
  artifact_path: ".agents/records",
439
440
  progress: 0,
440
- spec_id: null,
441
+ spec_id: typeof row.spec_id === "string" ? row.spec_id : null,
442
+ origin: typeof row.origin === "string" ? row.origin : null,
441
443
  execution_started: false,
442
444
  telemetry: {
443
445
  duration_ms: nullableMetric(row.total_duration),
@@ -551,9 +553,6 @@ function buildSqliteOperationsProjection(
551
553
  origin_tokens_match_total: true,
552
554
  },
553
555
  usage,
554
- // The period tabs are part of the first-fold contract too. Supplying an
555
- // empty object for the all-time fast path made Daily, Monthly and Annual
556
- // look empty even though the SQLite daily aggregate already existed.
557
556
  periods: { daily: options.includePeriods === true ? dailyPeriods : {} },
558
557
  },
559
558
  origins: { conversation: usage, task: usage, spec: usage, prd: usage },
@@ -642,88 +641,6 @@ async function firstFoldCostSource(
642
641
  } satisfies CostSummarySource;
643
642
  }
644
643
 
645
- function buildSqliteTaskTrace(
646
- filesystem: SqliteReadModelFileSystem,
647
- layout: WorkspaceLayout,
648
- taskId: string,
649
- ): TaskTrace {
650
- const initial = filesystem.canonicalTraceFiles([taskId]);
651
- const taskFile = initial.find(
652
- (row) =>
653
- row.path.includes("/.agents/records/tasks/") || row.path.includes(".agents/records/tasks/"),
654
- );
655
- if (taskFile === undefined) throw new Error(`Canonical Task ${taskId} was not found`);
656
- const scalar = (key: string): string | null => {
657
- const frontMatter = /^---\r?\n([\s\S]*?)\r?\n---/.exec(taskFile.content)?.[1] ?? "";
658
- const value = new RegExp(`^${key}:\\s*(.+?)\\s*$`, "m").exec(frontMatter)?.[1]?.trim();
659
- if (value === undefined || value === "null") return null;
660
- return value.replace(/^['"]|['"]$/g, "");
661
- };
662
- const relatedId = (key: string, prefix: string): string | null =>
663
- scalar(key) ?? new RegExp(`\\b${prefix}[A-Z0-9-]+`).exec(taskFile.content)?.[0] ?? null;
664
- const changeId = relatedId("change_id", "CHG-") ?? taskId;
665
- const specId = relatedId("spec_id", "SPEC-");
666
- const prdId = relatedId("prd_id", "PRD-");
667
- const markers = [taskId, changeId, specId, prdId].filter(
668
- (value): value is string => value !== null,
669
- );
670
- const documents = filesystem
671
- .canonicalTraceFiles(markers)
672
- .filter(
673
- (row) => /\.(?:md|json|jsonl|txt|log)$/i.test(row.path) && row.content.length <= 250_000,
674
- )
675
- .map((row) => {
676
- const classification = classifyTraceDocument(row.path);
677
- const timestamps = timestampsFromContent(row.content);
678
- return {
679
- id: createHash("sha256").update(row.path).digest("hex").slice(0, 16),
680
- path: row.path,
681
- title:
682
- row.content.match(/^#\s+(.+)$/m)?.[1]?.trim() ?? row.path.split("/").at(-1) ?? row.path,
683
- ...classification,
684
- content: row.content,
685
- ...timestamps,
686
- } satisfies TraceDocument;
687
- });
688
- const stages = buildTraceStageRecords(documents);
689
- const coveredStages = new Set(documents.map((document) => document.stage));
690
- const risk = scalar("risk");
691
- const status = scalar("status");
692
- return {
693
- schema_version: 2,
694
- layout_version: layout.version as 2 | 3,
695
- readonly: true,
696
- source: ".agents/records",
697
- task_id: taskId,
698
- task_title: scalar("title") ?? taskFile.content.match(/^#\s+(.+)$/m)?.[1]?.trim() ?? taskId,
699
- task_risk: risk === "lite" || risk === "standard" || risk === "critical" ? risk : null,
700
- task_created_at: scalar("created_at"),
701
- task_progress:
702
- status === "completed"
703
- ? 100
704
- : status === "verifying"
705
- ? 80
706
- : status === "executing"
707
- ? 50
708
- : status === "blocked" || status === "failed" || status === "cancelled"
709
- ? 0
710
- : 10,
711
- spec_id: specId,
712
- prd_id: prdId,
713
- change_id: changeId,
714
- usage: filesystem.taskTraceUsage(taskId),
715
- documents,
716
- stages,
717
- coverage: {
718
- stages_with_documents: coveredStages.size,
719
- stages_with_timestamps: stages.filter((stage) => stage.status === "recorded").length,
720
- total_stages: 6,
721
- evidence_count: documents.filter((document) => document.kind === "evidence").length,
722
- test_count: documents.filter((document) => document.kind === "test").length,
723
- },
724
- };
725
- }
726
-
727
644
  export function createDashboardApp(
728
645
  workspaceRoot = process.env.ZAIP_WORKSPACE ?? process.cwd(),
729
646
  sessionToken = defaultSessionToken(),
@@ -737,7 +654,8 @@ export function createDashboardApp(
737
654
  // its V2 layout repeatedly costs hundreds of milliseconds per navigation;
738
655
  // canonical filesystems remain deliberately uncached.
739
656
  const sqliteLayout = isSqliteSnapshot ? requireV2Layout(filesystem) : null;
740
- const readLayout = () => sqliteLayout ?? requireV2Layout(filesystem);
657
+ const readLayout = async () =>
658
+ options.layout ?? sqliteLayout ?? (await requireV2Layout(filesystem));
741
659
  const snapshotReadOnly = options.snapshotReadOnly ?? isSqliteSnapshot;
742
660
  const snapshotState = options.snapshotState ?? "current";
743
661
  const canonicalFilesystem = options.canonicalFilesystem ?? new WorkspaceFileSystem(workspaceRoot);
@@ -745,13 +663,7 @@ export function createDashboardApp(
745
663
  const refreshReadModel =
746
664
  options.refreshReadModel ??
747
665
  (async () => {
748
- if (!isSqliteSnapshot) return;
749
- // Explicit user refreshes must replace the immutable generation with the
750
- // current canonical records. Keep stale-seed fallback exclusively for
751
- // startup warmth, never for this visible convergence request.
752
- await filesystem.rebuildAndRefresh(() =>
753
- rebuildSqliteReadModel(workspaceRoot, { forceCanonical: true }),
754
- );
666
+ if (isSqliteSnapshot) await refreshSqliteSnapshotInWorker(workspaceRoot, filesystem);
755
667
  });
756
668
  const workspaceKey = dashboardWorkspaceKey(workspaceRoot);
757
669
  const syncAgentAdapters = options.syncAgentAdapters ?? (() => runAgentAdapterSync(workspaceRoot));
@@ -839,6 +751,9 @@ export function createDashboardApp(
839
751
  };
840
752
 
841
753
  app.use("*", async (context, next) => {
754
+ if (!isAllowedDashboardAuthority(context.req.url, context.req.header("host"))) {
755
+ return context.json({ error: "Host rejected" }, 403);
756
+ }
842
757
  context.header("Cache-Control", "no-store");
843
758
  context.header("X-Content-Type-Options", "nosniff");
844
759
  context.header("Referrer-Policy", "no-referrer");
@@ -1089,6 +1004,7 @@ export function createDashboardApp(
1089
1004
  { from, to },
1090
1005
  {
1091
1006
  includeTopTasks: from === null && to === null,
1007
+ includeExtended: from === null && to === null,
1092
1008
  },
1093
1009
  ),
1094
1010
  context.req.query("fresh") === "1",
@@ -1194,6 +1110,13 @@ export function createDashboardApp(
1194
1110
  const value = context.req.query(name);
1195
1111
  return value === undefined ? {} : { [name]: value };
1196
1112
  };
1113
+ const workListKind = context.req.query("kind");
1114
+ if (
1115
+ view === "operations_work_list" &&
1116
+ workListKind !== undefined &&
1117
+ !["all", "task", "standalone-task", "prd", "spec"].includes(workListKind)
1118
+ )
1119
+ return headOrJson(context, { error: "Unsupported Operations work-list kind" }, 400);
1197
1120
  return headOrJson(
1198
1121
  context,
1199
1122
  filesystem.viewPage(view as keyof typeof sqliteViewMetadata, {
@@ -1209,6 +1132,9 @@ export function createDashboardApp(
1209
1132
  ...optional("origin"),
1210
1133
  ...optional("workType"),
1211
1134
  ...optional("priority"),
1135
+ ...(workListKind === undefined
1136
+ ? {}
1137
+ : { kind: workListKind as "all" | "task" | "standalone-task" | "prd" | "spec" }),
1212
1138
  offset: Math.max(0, Number.parseInt(context.req.query("offset") ?? "0", 10) || 0),
1213
1139
  limit,
1214
1140
  }),
@@ -1295,13 +1221,19 @@ export function createDashboardApp(
1295
1221
  try {
1296
1222
  await readLayout();
1297
1223
  // The served snapshot is immutable by design. Reconciliation must use
1298
- // canonical records, then atomically rebuild and reopen the snapshot
1299
- // before the response exposes fresh projections.
1224
+ // canonical records. When those records changed, atomically rebuild and
1225
+ // reopen the snapshot before the response exposes fresh projections.
1300
1226
  const reconciledIds = await reconcileOpen(canonicalFilesystem, workspaceRoot);
1301
- await rebuildProjection();
1227
+ // A clean reconciliation is the normal startup path. It changed no
1228
+ // canonical data, so rebuilding the immutable SQLite snapshot only adds
1229
+ // seconds of redundant work before the dashboard can settle.
1230
+ if (reconciliationNeedsRefresh(reconciledIds)) {
1231
+ await rebuildProjection();
1232
+ }
1302
1233
  return context.json({
1303
1234
  ...(await readSnapshot(context)),
1304
1235
  reconciliation: reconciledIds,
1236
+ projection_refresh_completed: reconciliationNeedsRefresh(reconciledIds),
1305
1237
  });
1306
1238
  } catch (error) {
1307
1239
  if (error instanceof V2WorkspaceRequiredError) throw error;
@@ -1409,6 +1341,50 @@ export function createDashboardApp(
1409
1341
  }
1410
1342
  });
1411
1343
 
1344
+ /** Compact factual attribution view used by both Cost Monitor and Operations.
1345
+ * It is intentionally read-only and is available only from a coherent SQLite
1346
+ * generation, never by reopening mutable JSONL streams on a request. */
1347
+ app.on(["GET", "HEAD"], `${API_PREFIX}/developers`, async (context) => {
1348
+ if (!(filesystem instanceof SqliteReadModelFileSystem))
1349
+ return headOrJson(
1350
+ context,
1351
+ { error: "Developer attribution requires the SQLite read model" },
1352
+ 409,
1353
+ );
1354
+ const { from, to } = projectionPeriodFilter(context);
1355
+ return headOrJson(
1356
+ context,
1357
+ filesystem.developerCostAggregate({
1358
+ from,
1359
+ to,
1360
+ accountIds: queryValues(context, "account_id"),
1361
+ }),
1362
+ 200,
1363
+ true,
1364
+ );
1365
+ });
1366
+
1367
+ /** Operations keeps its developer facts separate from Cost Monitor columns. */
1368
+ app.on(["GET", "HEAD"], `${API_PREFIX}/operations/developers`, async (context) => {
1369
+ if (!(filesystem instanceof SqliteReadModelFileSystem))
1370
+ return headOrJson(
1371
+ context,
1372
+ { error: "Developer attribution requires the SQLite read model" },
1373
+ 409,
1374
+ );
1375
+ const { from, to } = projectionPeriodFilter(context);
1376
+ return headOrJson(
1377
+ context,
1378
+ filesystem.operationsDeveloperAggregate({
1379
+ from,
1380
+ to,
1381
+ accountIds: queryValues(context, "account_id"),
1382
+ }),
1383
+ 200,
1384
+ true,
1385
+ );
1386
+ });
1387
+
1412
1388
  app.on(["GET", "HEAD"], `${API_PREFIX}/pricing`, async (context) =>
1413
1389
  headOrJson(
1414
1390
  context,
@@ -0,0 +1,28 @@
1
+ export function developerArtifactCountsCte() {
2
+ return `, artifacts AS (
3
+ SELECT a.developer_account_id,
4
+ COUNT(DISTINCT CASE WHEN artifact.artifact_kind='validation' THEN artifact.source_path END) AS test_count,
5
+ COUNT(DISTINCT CASE WHEN artifact.artifact_kind='evidence' THEN artifact.source_path END) AS evidence_count
6
+ FROM selected a
7
+ JOIN canonical_artifacts artifact ON artifact.run_id=a.run_id
8
+ AND artifact.task_id=a.task_id
9
+ AND (artifact.change_id IS NULL OR artifact.change_id=a.change_id)
10
+ WHERE artifact.occurred_day_sp IS NOT NULL
11
+ AND (? IS NULL OR artifact.occurred_day_sp >= ?)
12
+ AND (? IS NULL OR artifact.occurred_day_sp <= ?)
13
+ GROUP BY a.developer_account_id
14
+ )`;
15
+ }
16
+
17
+ export function artifactStructuralRowsSql() {
18
+ return `UNION ALL
19
+ SELECT occurred_day_sp,
20
+ 0, 0, 0,
21
+ SUM(CASE WHEN artifact_kind='validation' THEN 1 ELSE 0 END),
22
+ SUM(CASE WHEN artifact_kind='evidence' THEN 1 ELSE 0 END)
23
+ FROM canonical_artifacts
24
+ WHERE occurred_day_sp IS NOT NULL
25
+ AND (? IS NULL OR occurred_day_sp >= ?)
26
+ AND (? IS NULL OR occurred_day_sp <= ?)
27
+ GROUP BY occurred_day_sp`;
28
+ }
@@ -0,0 +1,63 @@
1
+ import { DatabaseSync } from "node:sqlite";
2
+ import { describe, expect, it } from "vitest";
3
+ import { isDateOnlyCostPage, readDateOnlyCostDimensionPage } from "./sqlite-cost-agent-page.js";
4
+
5
+ describe("date-only Cost page", () => {
6
+ it("accepts only period input and keeps all custom filters on the canonical path", () => {
7
+ expect(isDateOnlyCostPage({ from: "2026-08-01", to: "2026-08-31" })).toBe(true);
8
+ expect(isDateOnlyCostPage({ from: "2026-08-01", agent: "leo" })).toBe(false);
9
+ expect(isDateOnlyCostPage({ query: "leo" })).toBe(false);
10
+ });
11
+
12
+ it("keeps residuals, multi-dimension membership, and global totals aligned", () => {
13
+ const database = new DatabaseSync(":memory:");
14
+ try {
15
+ database.exec(
16
+ `CREATE TABLE zaip_aios_activities (payload_type TEXT, activity_source_kind TEXT, occurred_day_sp TEXT, agent_invocation_id TEXT, task_id TEXT, duration_ms REAL, source_event_count INTEGER, input_tokens REAL, cached_input_tokens REAL, output_tokens REAL, reasoning_tokens REAL, total_tokens REAL, cost_usd REAL, cost_brl REAL, model TEXT, provider TEXT, zaip_agent TEXT, zaip_agent_persona TEXT, zaip_agent_job TEXT, occurred_at TEXT); CREATE TABLE cost_invocation_dimension_facts (agent_invocation_id TEXT, occurred_day_sp TEXT, model TEXT, provider TEXT, input_tokens REAL, cached_input_tokens REAL, output_tokens REAL, reasoning_tokens REAL, total_tokens REAL, cost_usd REAL, cost_brl REAL); CREATE TABLE zaip_dim_zaip_agent (zaip_agent TEXT, persona TEXT, job TEXT);`,
17
+ );
18
+ const activity = database.prepare(
19
+ "INSERT INTO zaip_aios_activities(payload_type,activity_source_kind,occurred_day_sp,agent_invocation_id,task_id,duration_ms,source_event_count,total_tokens,cost_usd,cost_brl) VALUES (NULL,'agent_run','2026-08-01',?,?,?,?,?,?,?)",
20
+ );
21
+ activity.run("a", "task-a", 100, 1, 100, 6, 10);
22
+ activity.run("b", "task-b", 200, 2, 100, 10, 10);
23
+ const fact = database.prepare(
24
+ "INSERT INTO cost_invocation_dimension_facts VALUES (?,?,?,?,NULL,NULL,NULL,NULL,?,?,?)",
25
+ );
26
+ fact.run("a", "2026-08-01", "gpt", "openai", 60, 6, 60);
27
+ fact.run("b", "2026-08-01", "gpt", "openai", 50, 5, 50);
28
+ fact.run("b", "2026-08-01", "claude", "anthropic", 50, 5, 50);
29
+ for (const dimension of ["model", "provider"] as const) {
30
+ const page = readDateOnlyCostDimensionPage(
31
+ database,
32
+ dimension,
33
+ "2026-08-01",
34
+ "2026-08-31",
35
+ 10,
36
+ 0,
37
+ );
38
+ expect(page.totals).toMatchObject({
39
+ invocation_count: 2,
40
+ task_count: 2,
41
+ total_duration: 300,
42
+ source_event_count: 3,
43
+ total_tokens: 200,
44
+ total_cost_brl: 20,
45
+ });
46
+ const known = dimension === "model" ? "gpt" : "openai";
47
+ expect(page.rows.find((row) => row.key === known)).toMatchObject({
48
+ invocation_count: 1,
49
+ task_count: 2,
50
+ total_duration: 100,
51
+ total_tokens: 110,
52
+ });
53
+ expect(page.rows.find((row) => row.key === "unavailable")).toMatchObject({
54
+ invocation_count: 0,
55
+ task_count: 2,
56
+ total_tokens: 40,
57
+ });
58
+ }
59
+ } finally {
60
+ database.close();
61
+ }
62
+ });
63
+ });
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Date-only Cost pages can use immutable, indexed activity facts. Any
3
+ * additional filter deliberately falls back to the canonical SQL path, whose
4
+ * relationship semantics need the richer views.
5
+ */
6
+ export type DateOnlyCostPageInput = {
7
+ from?: string | null;
8
+ to?: string | null;
9
+ query?: string;
10
+ status?: string;
11
+ risk?: string;
12
+ provider?: string;
13
+ model?: string;
14
+ agent?: string;
15
+ phase?: string;
16
+ origin?: string;
17
+ workType?: string;
18
+ priority?: string;
19
+ };
20
+
21
+ export function isDateOnlyCostPage(input: DateOnlyCostPageInput): boolean {
22
+ return (
23
+ (input.from !== undefined || input.to !== undefined) &&
24
+ !input.query &&
25
+ !input.status &&
26
+ !input.risk &&
27
+ !input.provider &&
28
+ !input.model &&
29
+ !input.agent &&
30
+ !input.phase &&
31
+ !input.origin &&
32
+ !input.workType &&
33
+ !input.priority
34
+ );
35
+ }
36
+
37
+ /** SQL is built only from normalized Sao Paulo day keys. */
38
+ export function costDimensionFactDateFilter(input: DateOnlyCostPageInput): string {
39
+ const day = (value: string | null | undefined) =>
40
+ value && /^\d{4}-\d{2}-\d{2}/.test(value) ? value.slice(0, 10) : null;
41
+ const clauses = [
42
+ day(input.from) && `occurred_day_sp >= '${day(input.from)}'`,
43
+ day(input.to) && `occurred_day_sp <= '${day(input.to)}'`,
44
+ ].filter((value): value is string => Boolean(value));
45
+ return clauses.length === 0 ? "" : ` AND ${clauses.join(" AND ")}`;
46
+ }
47
+
48
+ type CostDimension = "agent" | "model" | "provider";
49
+ type SqliteQuery = {
50
+ prepare(sql: string): {
51
+ get(...values: Array<string | number>): Record<string, unknown> | undefined;
52
+ all(...values: Array<string | number>): Array<Record<string, unknown>>;
53
+ };
54
+ };
55
+
56
+ /** Reads the date picker population from schema-67 physical facts only. */
57
+ export function readDateOnlyCostDimensionPage(
58
+ database: SqliteQuery,
59
+ dimension: CostDimension,
60
+ from: string,
61
+ to: string,
62
+ limit: number,
63
+ offset: number,
64
+ ) {
65
+ if (dimension === "agent") {
66
+ const facts = `WITH facts AS (SELECT zaip_agent AS key, zaip_agent_persona AS persona, zaip_agent_job AS job, occurred_at, task_id, duration_ms, source_event_count, total_tokens, cost_usd, cost_brl FROM zaip_aios_activities WHERE payload_type IS NULL AND occurred_day_sp>=? AND occurred_day_sp<=? AND zaip_agent IS NOT NULL AND (activity_source_kind='agent_run' OR (activity_source_kind<>'agent_run' AND (total_tokens IS NOT NULL OR cost_usd IS NOT NULL OR cost_brl IS NOT NULL))))`;
67
+ const total_count = Number(
68
+ database.prepare(`${facts} SELECT COUNT(DISTINCT key) AS count FROM facts`).get(from, to)
69
+ ?.count ?? 0,
70
+ );
71
+ const totals =
72
+ database
73
+ .prepare(
74
+ `${facts} SELECT COUNT(*) AS invocation_count, COUNT(DISTINCT task_id) AS task_count, SUM(duration_ms) AS total_duration, SUM(CASE WHEN duration_ms IS NULL THEN 1 ELSE 0 END) AS duration_unknown, SUM(CASE WHEN duration_ms IS NULL THEN 0 ELSE duration_ms END) AS duration_known, SUM(source_event_count) AS source_event_count, SUM(total_tokens) AS total_tokens, SUM(cost_usd) AS total_cost_usd, SUM(cost_brl) AS total_cost_brl FROM facts`,
75
+ )
76
+ .get(from, to) ?? {};
77
+ const rows = database
78
+ .prepare(
79
+ `${facts} SELECT key, key AS label, COALESCE(MAX(persona), (SELECT persona FROM zaip_dim_zaip_agent d WHERE d.zaip_agent=key)) AS persona, COALESCE(MAX(job), (SELECT job FROM zaip_dim_zaip_agent d WHERE d.zaip_agent=key)) AS job, MAX(occurred_at) AS latest_started_at, COUNT(*) AS invocation_count, COUNT(DISTINCT task_id) AS task_count, SUM(duration_ms) AS total_duration, SUM(CASE WHEN duration_ms IS NULL THEN 1 ELSE 0 END) AS duration_unknown, SUM(CASE WHEN duration_ms IS NULL THEN 0 ELSE duration_ms END) AS duration_known, SUM(source_event_count) AS source_event_count, SUM(total_tokens) AS total_tokens, SUM(cost_usd) AS total_cost_usd, SUM(cost_brl) AS total_cost_brl FROM facts GROUP BY key ORDER BY total_cost_brl DESC, key LIMIT ? OFFSET ?`,
80
+ )
81
+ .all(from, to, limit, offset);
82
+ return { total_count, totals, rows };
83
+ }
84
+ const field = dimension === "model" ? "model" : "provider";
85
+ const directFinancial =
86
+ dimension === "model"
87
+ ? ` UNION ALL SELECT CASE WHEN a.${field} IS NULL OR trim(a.${field})='' OR lower(a.${field}) IN ('unavailable','unknown','mixed') THEN 'unavailable' ELSE a.${field} END,a.total_tokens,a.cost_usd,a.cost_brl FROM zaip_aios_activities a WHERE a.payload_type IS NULL AND a.activity_source_kind<>'agent_run' AND a.occurred_day_sp>=? AND a.occurred_day_sp<=? AND (a.total_tokens IS NOT NULL OR a.cost_usd IS NOT NULL OR a.cost_brl IS NOT NULL)`
88
+ : "";
89
+ const cte = `WITH base AS (SELECT agent_invocation_id, task_id, duration_ms, source_event_count, input_tokens, cached_input_tokens, output_tokens, reasoning_tokens, total_tokens, cost_usd, cost_brl FROM zaip_aios_activities WHERE payload_type IS NULL AND activity_source_kind='agent_run' AND occurred_day_sp>=? AND occurred_day_sp<=?), segment_totals AS (SELECT f.agent_invocation_id,SUM(f.input_tokens) input_tokens,SUM(f.cached_input_tokens) cached_input_tokens,SUM(f.output_tokens) output_tokens,SUM(f.reasoning_tokens) reasoning_tokens,SUM(f.total_tokens) total_tokens,SUM(f.cost_usd) cost_usd,SUM(f.cost_brl) cost_brl FROM cost_invocation_dimension_facts f JOIN base USING(agent_invocation_id) WHERE f.occurred_day_sp>=? AND f.occurred_day_sp<=? AND (f.total_tokens IS NOT NULL OR f.cost_usd IS NOT NULL OR f.cost_brl IS NOT NULL) GROUP BY f.agent_invocation_id), segments AS (SELECT f.agent_invocation_id,CASE WHEN f.${field} IS NULL OR trim(f.${field})='' OR lower(f.${field}) IN ('unavailable','unknown','mixed') THEN 'unavailable' ELSE f.${field} END key,SUM(f.input_tokens) input_tokens,SUM(f.cached_input_tokens) cached_input_tokens,SUM(f.output_tokens) output_tokens,SUM(f.reasoning_tokens) reasoning_tokens,SUM(f.total_tokens) total_tokens,SUM(f.cost_usd) total_cost_usd,SUM(f.cost_brl) total_cost_brl FROM cost_invocation_dimension_facts f JOIN base USING(agent_invocation_id) WHERE f.occurred_day_sp>=? AND f.occurred_day_sp<=? AND (f.total_tokens IS NOT NULL OR f.cost_usd IS NOT NULL OR f.cost_brl IS NOT NULL) GROUP BY f.agent_invocation_id,key UNION ALL SELECT b.agent_invocation_id,'unavailable',b.input_tokens-COALESCE(t.input_tokens,0),b.cached_input_tokens-COALESCE(t.cached_input_tokens,0),b.output_tokens-COALESCE(t.output_tokens,0),b.reasoning_tokens-COALESCE(t.reasoning_tokens,0),b.total_tokens-COALESCE(t.total_tokens,0),b.cost_usd-COALESCE(t.cost_usd,0),b.cost_brl-COALESCE(t.cost_brl,0) FROM base b LEFT JOIN segment_totals t USING(agent_invocation_id) WHERE (b.total_tokens IS NOT NULL OR b.cost_usd IS NOT NULL OR b.cost_brl IS NOT NULL) AND (b.total_tokens<>COALESCE(t.total_tokens,0) OR b.cost_usd<>COALESCE(t.cost_usd,0) OR b.cost_brl<>COALESCE(t.cost_brl,0))), dimension_segments AS (SELECT agent_invocation_id,key,SUM(input_tokens) input_tokens,SUM(cached_input_tokens) cached_input_tokens,SUM(output_tokens) output_tokens,SUM(reasoning_tokens) reasoning_tokens,SUM(total_tokens) total_tokens,SUM(total_cost_usd) total_cost_usd,SUM(total_cost_brl) total_cost_brl FROM segments GROUP BY agent_invocation_id,key), known_dimensions AS (SELECT agent_invocation_id,COUNT(DISTINCT key) dimension_count FROM dimension_segments WHERE key<>'unavailable' GROUP BY agent_invocation_id), filtered_segments AS (SELECT s.*,b.task_id,b.duration_ms,b.source_event_count,d.dimension_count FROM segments s JOIN base b USING(agent_invocation_id) LEFT JOIN known_dimensions d USING(agent_invocation_id)), financial_components AS (SELECT key,total_tokens,total_cost_usd,total_cost_brl FROM filtered_segments${directFinancial}), financial AS (SELECT key,SUM(total_tokens) total_tokens,SUM(total_cost_usd) total_cost_usd,SUM(total_cost_brl) total_cost_brl FROM financial_components GROUP BY key), population_dimensions AS (SELECT agent_invocation_id,key,MAX(task_id) task_id,MAX(duration_ms) duration_ms,MAX(source_event_count) source_event_count,MAX(dimension_count) dimension_count FROM filtered_segments GROUP BY agent_invocation_id,key), population_ranked AS (SELECT *,ROW_NUMBER() OVER (PARTITION BY agent_invocation_id ORDER BY key) dimension_rank FROM population_dimensions), population AS (SELECT key,COUNT(*) invocation_count,SUM(duration_ms) total_duration,SUM(source_event_count) source_event_count FROM population_ranked WHERE dimension_rank=1 AND dimension_count=1 GROUP BY key), task_population AS (SELECT key,COUNT(*) task_count FROM (SELECT task_id,key FROM population_dimensions WHERE task_id IS NOT NULL GROUP BY task_id,key) GROUP BY key), keys AS (SELECT key FROM financial UNION SELECT key FROM population UNION SELECT key FROM task_population)`;
90
+ const args =
91
+ dimension === "model"
92
+ ? [from, to, from, to, from, to, from, to]
93
+ : [from, to, from, to, from, to];
94
+ const total_count = Number(
95
+ database.prepare(`${cte} SELECT COUNT(*) AS count FROM keys`).get(...args)?.count ?? 0,
96
+ );
97
+ const totals =
98
+ database
99
+ .prepare(
100
+ `${cte} SELECT (SELECT COUNT(*) FROM population_ranked WHERE dimension_rank=1) invocation_count, (SELECT COUNT(DISTINCT task_id) FROM population_dimensions) task_count, (SELECT SUM(duration_ms) FROM population_ranked WHERE dimension_rank=1) total_duration, (SELECT SUM(CASE WHEN duration_ms IS NULL THEN 1 ELSE 0 END) FROM population_ranked WHERE dimension_rank=1) duration_unknown, (SELECT SUM(CASE WHEN duration_ms IS NULL THEN 0 ELSE duration_ms END) FROM population_ranked WHERE dimension_rank=1) duration_known, (SELECT SUM(source_event_count) FROM population_ranked WHERE dimension_rank=1) source_event_count, (SELECT SUM(input_tokens) FROM filtered_segments) total_input_tokens, (SELECT SUM(cached_input_tokens) FROM filtered_segments) total_cached_input_tokens, (SELECT SUM(output_tokens) FROM filtered_segments) total_output_tokens, (SELECT SUM(reasoning_tokens) FROM filtered_segments) total_reasoning_tokens, (SELECT SUM(total_tokens) FROM filtered_segments) total_tokens, (SELECT SUM(total_cost_usd) FROM filtered_segments) total_cost_usd, (SELECT SUM(total_cost_brl) FROM filtered_segments) total_cost_brl`,
101
+ )
102
+ .get(...args) ?? {};
103
+ const rows = database
104
+ .prepare(
105
+ `${cte} SELECT keys.key,keys.key AS label,COALESCE(population.invocation_count,0) invocation_count,COALESCE(task_population.task_count,0) task_count,population.total_duration,population.source_event_count,financial.total_tokens,financial.total_cost_usd,financial.total_cost_brl FROM keys LEFT JOIN population USING(key) LEFT JOIN task_population USING(key) LEFT JOIN financial USING(key) ORDER BY financial.total_cost_brl DESC,keys.key LIMIT ? OFFSET ?`,
106
+ )
107
+ .all(...args, limit, offset);
108
+ return { total_count, totals, rows };
109
+ }