@zaip-ai/cli 1.3.8 → 1.4.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 (315) hide show
  1. package/README.md +58 -3
  2. package/dist/commands/init.d.ts +1 -0
  3. package/dist/commands/init.d.ts.map +1 -1
  4. package/dist/commands/init.js +17 -6
  5. package/dist/commands/init.js.map +1 -1
  6. package/dist/index.d.ts +1 -0
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +2 -0
  9. package/dist/index.js.map +1 -1
  10. package/dist/services/adapters.js +4 -4
  11. package/dist/services/adapters.js.map +1 -1
  12. package/dist/services/codex-config-template.d.ts +2 -2
  13. package/dist/services/codex-config-template.d.ts.map +1 -1
  14. package/dist/services/codex-config-template.js +3 -3
  15. package/dist/services/codex-config-template.js.map +1 -1
  16. package/dist/services/codex-rules.d.ts +1 -1
  17. package/dist/services/codex-rules.d.ts.map +1 -1
  18. package/dist/services/codex-rules.js +9 -24
  19. package/dist/services/codex-rules.js.map +1 -1
  20. package/dist/services/delivery-git-preflight.d.ts +23 -0
  21. package/dist/services/delivery-git-preflight.d.ts.map +1 -0
  22. package/dist/services/delivery-git-preflight.js +69 -0
  23. package/dist/services/delivery-git-preflight.js.map +1 -0
  24. package/dist/services/doctor.d.ts.map +1 -1
  25. package/dist/services/doctor.js +13 -1
  26. package/dist/services/doctor.js.map +1 -1
  27. package/dist/services/install-agents-template.d.ts.map +1 -1
  28. package/dist/services/install-agents-template.js +22 -28
  29. package/dist/services/install-agents-template.js.map +1 -1
  30. package/dist/services/install-profile.d.ts +8 -0
  31. package/dist/services/install-profile.d.ts.map +1 -0
  32. package/dist/services/install-profile.js +56 -0
  33. package/dist/services/install-profile.js.map +1 -0
  34. package/dist/services/logger.d.ts.map +1 -1
  35. package/dist/services/logger.js +6 -5
  36. package/dist/services/logger.js.map +1 -1
  37. package/dist/services/stack-git.d.ts +9 -0
  38. package/dist/services/stack-git.d.ts.map +1 -0
  39. package/dist/services/stack-git.js +188 -0
  40. package/dist/services/stack-git.js.map +1 -0
  41. package/dist/services/stack-plan.d.ts +3 -1
  42. package/dist/services/stack-plan.d.ts.map +1 -1
  43. package/dist/services/stack-plan.js +15 -1
  44. package/dist/services/stack-plan.js.map +1 -1
  45. package/dist/services/stack-terraform.d.ts +13 -0
  46. package/dist/services/stack-terraform.d.ts.map +1 -0
  47. package/dist/services/stack-terraform.js +274 -0
  48. package/dist/services/stack-terraform.js.map +1 -0
  49. package/dist/services/template-manifest.d.ts.map +1 -1
  50. package/dist/services/template-manifest.js +14 -1
  51. package/dist/services/template-manifest.js.map +1 -1
  52. package/dist/types.d.ts +1 -0
  53. package/dist/types.d.ts.map +1 -1
  54. package/package.json +1 -1
  55. package/templates/.agents/.env.example +34 -0
  56. package/templates/{README.md → .agents/README.md} +124 -60
  57. package/templates/.agents/config/agents/catalog.yaml +23 -23
  58. package/templates/.agents/config/clients/codex/agents/zaip-agent-analyst.toml +1 -0
  59. package/templates/.agents/config/clients/codex/agents/zaip-agent-auditor.toml +1 -0
  60. package/templates/.agents/config/clients/codex/agents/zaip-agent-backend.toml +5 -4
  61. package/templates/.agents/config/clients/codex/agents/zaip-agent-brand-strategist.toml +1 -0
  62. package/templates/.agents/config/clients/codex/agents/zaip-agent-builder.toml +1 -0
  63. package/templates/.agents/config/clients/codex/agents/zaip-agent-cloud-engineer.toml +10 -7
  64. package/templates/.agents/config/clients/codex/agents/zaip-agent-data-analyst.toml +1 -0
  65. package/templates/.agents/config/clients/codex/agents/zaip-agent-designer.toml +1 -0
  66. package/templates/.agents/config/clients/codex/agents/zaip-agent-finalizer.toml +1 -0
  67. package/templates/.agents/config/clients/codex/agents/zaip-agent-frontend.toml +5 -4
  68. package/templates/.agents/config/clients/codex/agents/zaip-agent-general-operations.toml +1 -0
  69. package/templates/.agents/config/clients/codex/agents/zaip-agent-governance-compliance.toml +1 -0
  70. package/templates/.agents/config/clients/codex/agents/zaip-agent-growth-strategist.toml +1 -0
  71. package/templates/.agents/config/clients/codex/agents/zaip-agent-integrator.toml +1 -0
  72. package/templates/.agents/config/clients/codex/agents/zaip-agent-marketing-analyst.toml +5 -4
  73. package/templates/.agents/config/clients/codex/agents/zaip-agent-orchestrator.toml +1 -0
  74. package/templates/.agents/config/clients/codex/agents/zaip-agent-refactoring-specialist.toml +1 -0
  75. package/templates/.agents/config/clients/codex/agents/zaip-agent-reviewer.toml +1 -0
  76. package/templates/.agents/config/clients/codex/agents/zaip-agent-sales-specialist.toml +1 -0
  77. package/templates/.agents/config/clients/codex/agents/zaip-agent-security-auditor.toml +1 -0
  78. package/templates/.agents/config/clients/codex/agents/zaip-agent-task-executor.toml +1 -0
  79. package/templates/.agents/config/clients/codex/agents/zaip-agent-website-developer.toml +5 -4
  80. package/templates/.agents/config/clients/codex/agents/zaip-agent-writer.toml +1 -0
  81. package/templates/.agents/config/pricing/models.json +25 -1
  82. package/templates/.agents/config/project.yaml +2 -0
  83. package/templates/.agents/dashboard/.zaip-managed.json +200 -88
  84. package/templates/.agents/dashboard/DATA-DICTIONARY.md +1 -1
  85. package/templates/.agents/dashboard/README.md +58 -19
  86. package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-DwVZOmMV.js → CostCalculationGuidePage-snUCQYTC.js} +1 -1
  87. package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog-VcLx9iOO.js → DashboardHelpDialog-oZVlf-l5.js} +72 -8
  88. package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-CyjMXkm8.js → PricingSettingsDialog-BPtVUGgs.js} +1 -1
  89. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-3Awyaovx.js +3 -0
  90. package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-DFZwIXSC.js → TraceabilityWorkspace-BYjVOHI_.js} +1 -1
  91. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-DbaLbU2q.js +68 -0
  92. package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-DXBOtWNI.js → icons-vendor-TPUT2IUt.js} +1 -1
  93. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-Cmlx8OYH.css +1 -0
  94. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-ZJFXmK7m.js +2 -0
  95. package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-DaNXQPyk.js → react-vendor-Ct48XHGL.js} +1 -1
  96. package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-I4AKqVDX.js → traceability-BDe4_Ubl.js} +1 -1
  97. package/templates/.agents/dashboard/apps/frontend/dist/index.html +6 -6
  98. package/templates/.agents/dashboard/apps/frontend/src/App.tsx +73 -67
  99. package/templates/.agents/dashboard/apps/frontend/src/AppLoadingBoundary.tsx +35 -7
  100. package/templates/.agents/dashboard/apps/frontend/src/AppWorkBoard.ts +55 -0
  101. package/templates/.agents/dashboard/apps/frontend/src/ProjectStackEditor.tsx +11 -0
  102. package/templates/.agents/dashboard/apps/frontend/src/app-loading-boundary.css +98 -0
  103. package/templates/.agents/dashboard/apps/frontend/src/cost-period-refresh.test.ts +5 -5
  104. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api-reconciliation.ts +33 -0
  105. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.test.ts +75 -0
  106. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.ts +7 -8
  107. package/templates/.agents/dashboard/apps/frontend/src/project-settings-model.ts +14 -2
  108. package/templates/.agents/dashboard/apps/frontend/src/project-settings.test.ts +35 -0
  109. package/templates/.agents/dashboard/apps/frontend/src/snapshot-coherence.test.ts +78 -2
  110. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.test.ts +28 -0
  111. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.ts +8 -5
  112. package/templates/.agents/dashboard/apps/frontend/src/use-app-effects.ts +180 -1
  113. package/templates/.agents/dashboard/modules/cost-monitor/src/contracts.ts +7 -4
  114. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorContent.tsx +13 -7
  115. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.tsx +14 -0
  116. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTableDisplay.ts +211 -0
  117. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.test.ts +152 -157
  118. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.tsx +155 -142
  119. package/templates/.agents/dashboard/modules/cost-monitor/src/react/InvocationManifestQuery.test.ts +145 -0
  120. package/templates/.agents/dashboard/modules/cost-monitor/src/react/period-cost-groups.ts +7 -5
  121. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-compact-cost-page.ts +4 -1
  122. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-group-page.ts +4 -1
  123. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-run-page.ts +4 -1
  124. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-projection.ts +4 -4
  125. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.test.ts +79 -1
  126. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.tsx +105 -11
  127. package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +482 -332
  128. package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +430 -285
  129. package/templates/.agents/dashboard/packages/local-runtime/scripts/build-sqlite-runtime.mjs +14 -2
  130. package/templates/.agents/dashboard/packages/local-runtime/scripts/rebuild-sqlite-read-model.mjs +6 -26
  131. package/templates/.agents/dashboard/packages/local-runtime/scripts/validate-sqlite-read-model.mjs +1 -1
  132. package/templates/.agents/dashboard/packages/local-runtime/scripts/verify-persistent-launch.mjs +268 -0
  133. package/templates/.agents/dashboard/packages/local-runtime/server.mjs +482 -332
  134. package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.test.ts +90 -1
  135. package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.ts +112 -22
  136. package/templates/.agents/dashboard/packages/local-runtime/src/server-agent-assets.ts +39 -0
  137. package/templates/.agents/dashboard/packages/local-runtime/src/server-browser-lease.ts +33 -0
  138. package/templates/.agents/dashboard/packages/local-runtime/src/server-reconciliation-refresh.test.ts +148 -11
  139. package/templates/.agents/dashboard/packages/local-runtime/src/server-reconciliation-result.test.ts +68 -0
  140. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-refresh.test.ts +19 -0
  141. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-refresh.ts +39 -43
  142. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-revision.test.ts +42 -0
  143. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-revision.ts +26 -0
  144. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-synchronization.ts +34 -0
  145. package/templates/.agents/dashboard/packages/local-runtime/src/server-startup.ts +30 -0
  146. package/templates/.agents/dashboard/packages/local-runtime/src/server-work-reconciliation.test.ts +89 -0
  147. package/templates/.agents/dashboard/packages/local-runtime/src/server-work-reconciliation.ts +88 -0
  148. package/templates/.agents/dashboard/packages/local-runtime/src/server-workspace.ts +68 -7
  149. package/templates/.agents/dashboard/packages/local-runtime/src/server.test.ts +11 -5
  150. package/templates/.agents/dashboard/packages/local-runtime/src/server.ts +129 -99
  151. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-agent-productivity.test.ts +111 -0
  152. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-agent-productivity.ts +140 -0
  153. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-agent-page.test.ts +127 -4
  154. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-agent-page.ts +145 -5
  155. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-aggregates.ts +156 -23
  156. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-group-projection.ts +18 -9
  157. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-projection.ts +52 -0
  158. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-first-fold-persistence.ts +35 -13
  159. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-force-canonical.test.ts +56 -0
  160. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-incremental-cache.test.ts +29 -0
  161. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-incremental-cache.ts +136 -0
  162. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-incremental-refresh.test.ts +251 -0
  163. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-operations-projection.ts +22 -7
  164. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-cache.test-support.ts +1 -1
  165. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-canonical-parsing.ts +42 -0
  166. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-canonical.ts +793 -0
  167. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider-duration.test.ts +270 -0
  168. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider-fixture.ts +3 -2
  169. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider.test.ts +18 -4
  170. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-source-fingerprint.test.ts +87 -0
  171. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-source-fingerprint.ts +105 -16
  172. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.test.ts +131 -59
  173. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.ts +1000 -1115
  174. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-cache.ts +13 -92
  175. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-claims.test.mjs +79 -0
  176. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.mjs +27 -0
  177. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-lifecycle.mjs +6 -6
  178. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-prerequisites.mjs +1 -1
  179. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-refresh.mjs +2 -10
  180. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-server.mjs +142 -0
  181. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.mjs +231 -239
  182. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.test.mjs +18 -29
  183. package/templates/.agents/dashboard/packages/windows-launcher/Program.cs +6 -0
  184. package/templates/.agents/dashboard/zaip-dashboard-windows.exe +0 -0
  185. package/templates/.agents/requirements/autonomous-assurance.md +1 -1
  186. package/templates/.agents/requirements/cloud-hostinger-autonomy.md +47 -0
  187. package/templates/.agents/requirements/comparative-audit.md +1 -1
  188. package/templates/.agents/requirements/context-engine.md +1 -1
  189. package/templates/.agents/requirements/cost-observability.md +1 -1
  190. package/templates/.agents/requirements/dashboard-launch.md +2 -2
  191. package/templates/.agents/requirements/data-export.md +1 -1
  192. package/templates/.agents/requirements/design-system.md +1 -1
  193. package/templates/.agents/requirements/execution-engine.md +1 -1
  194. package/templates/.agents/requirements/kanban-dashboard.md +1 -1
  195. package/templates/.agents/requirements/model-orchestration.md +1 -1
  196. package/templates/.agents/requirements/public-distribution.md +1 -1
  197. package/templates/.agents/requirements/software-architecture.md +1 -1
  198. package/templates/.agents/requirements/strategic-improvements.md +1 -1
  199. package/templates/.agents/requirements/work-lifecycle.md +1 -1
  200. package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
  201. package/templates/.agents/rules/autonomy-decision.md +1 -1
  202. package/templates/.agents/rules/cloud-hostinger-autonomy.md +21 -0
  203. package/templates/.agents/rules/core.md +5 -4
  204. package/templates/.agents/rules/local-sqlite-storage.md +18 -0
  205. package/templates/.agents/rules/meta-mcp.md +21 -0
  206. package/templates/.agents/rules/workflow.md +1 -0
  207. package/templates/.agents/runtime/bin/zaip.mjs +305 -137
  208. package/templates/.agents/runtime/checksums.sha256 +2 -2
  209. package/templates/.agents/runtime/provenance.json +2 -2
  210. package/templates/.agents/skills/zaip-skill-api-to-mcp/SKILL.md +4 -0
  211. package/templates/.agents/skills/zaip-skill-brand-governance/SKILL.md +25 -9
  212. package/templates/.agents/skills/zaip-skill-brand-governance/references/brandbook-framework.md +2 -2
  213. package/templates/.agents/skills/zaip-skill-brand-governance/references/brandbook-standards.md +2 -2
  214. package/templates/.agents/skills/zaip-skill-change/references/observable-evidence.md +15 -0
  215. package/templates/.agents/skills/zaip-skill-dashboard-sqlite/SKILL.md +13 -5
  216. package/templates/.agents/skills/zaip-skill-data-analyst/SKILL.md +10 -5
  217. package/templates/.agents/skills/zaip-skill-execution/SKILL.md +5 -4
  218. package/templates/.agents/skills/zaip-skill-frontend-launcher/SKILL.md +4 -3
  219. package/templates/.agents/skills/zaip-skill-frontend-launcher/references/launcher-contract.md +5 -3
  220. package/templates/.agents/skills/zaip-skill-github-deploy/SKILL.md +5 -5
  221. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/preflight.mjs +78 -13
  222. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/preflight.test.mjs +59 -0
  223. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/yaml-parser.mjs +157 -0
  224. package/templates/.agents/skills/zaip-skill-growth-strategy-revops/SKILL.md +11 -6
  225. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/SKILL.md +17 -0
  226. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/agents/openai.yaml +7 -0
  227. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/references/autonomous-cloud-inputs.example.json +41 -0
  228. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/references/autonomous-runner.md +84 -0
  229. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/references/operational-contract.md +19 -0
  230. package/templates/.agents/skills/zaip-skill-local-credentials/SKILL.md +43 -0
  231. package/templates/.agents/skills/zaip-skill-local-credentials/agents/openai.yaml +6 -0
  232. package/templates/.agents/skills/zaip-skill-local-credentials/references/dotenv-resolution.md +47 -0
  233. package/templates/.agents/skills/zaip-skill-marketing/SKILL.md +8 -3
  234. package/templates/.agents/skills/zaip-skill-meta-ads/SKILL.md +16 -0
  235. package/templates/.agents/skills/zaip-skill-meta-ads/agents/openai.yaml +4 -0
  236. package/templates/.agents/skills/zaip-skill-meta-ads/references/setup.md +3 -0
  237. package/templates/.agents/skills/zaip-skill-meta-ads-setup/SKILL.md +24 -0
  238. package/templates/.agents/skills/zaip-skill-meta-ads-setup/agents/openai.yaml +4 -0
  239. package/templates/.agents/skills/zaip-skill-meta-ads-setup/references/diagnostics.md +13 -0
  240. package/templates/.agents/skills/zaip-skill-meta-ads-setup/references/official-sources.md +10 -0
  241. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/SKILL.md +12 -0
  242. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/agents/openai.yaml +4 -0
  243. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/references/LICENSE +21 -0
  244. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/references/upstream.md +77 -0
  245. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/SKILL.md +12 -0
  246. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/agents/openai.yaml +4 -0
  247. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/references/LICENSE +21 -0
  248. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/references/upstream.md +126 -0
  249. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/SKILL.md +12 -0
  250. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/agents/openai.yaml +4 -0
  251. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/references/LICENSE +21 -0
  252. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/references/upstream.md +67 -0
  253. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/SKILL.md +12 -0
  254. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/agents/openai.yaml +4 -0
  255. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/references/LICENSE +21 -0
  256. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/references/upstream.md +69 -0
  257. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/SKILL.md +12 -0
  258. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/agents/openai.yaml +4 -0
  259. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/references/LICENSE +21 -0
  260. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/references/upstream.md +79 -0
  261. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/SKILL.md +14 -0
  262. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/agents/openai.yaml +4 -0
  263. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/references/LICENSE +21 -0
  264. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/references/upstream.md +144 -0
  265. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/SKILL.md +12 -0
  266. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/agents/openai.yaml +4 -0
  267. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/references/LICENSE +21 -0
  268. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/references/upstream.md +83 -0
  269. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/SKILL.md +10 -0
  270. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/agents/openai.yaml +4 -0
  271. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/references/LICENSE +21 -0
  272. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/references/setup.md +11 -0
  273. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/references/upstream.md +55 -0
  274. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/SKILL.md +12 -0
  275. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/agents/openai.yaml +4 -0
  276. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/references/LICENSE +21 -0
  277. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/references/upstream.md +56 -0
  278. package/templates/.agents/skills/zaip-skill-sales-methodology/SKILL.md +12 -7
  279. package/templates/.agents/skills/zaip-skill-sqlite-read-model/SKILL.md +11 -4
  280. package/templates/.agents/skills/zaip-skill-tool-setup/SKILL.md +6 -4
  281. package/templates/.agents/skills/zaip-skill-tool-setup/references/codex-config.md +4 -2
  282. package/templates/.agents/skills/zaip-skill-tool-setup/references/secrets.md +1 -0
  283. package/templates/.agents/tools/catalog.yaml +6 -0
  284. package/templates/.agents/tools/cloud-runner-dns.mjs +28 -0
  285. package/templates/.agents/tools/cloud-runner-terraform.mjs +212 -0
  286. package/templates/.agents/tools/cloud-runner.integration.test.mjs +239 -0
  287. package/templates/.agents/tools/cloud-runner.mjs +786 -0
  288. package/templates/.agents/tools/cloud-runner.test.mjs +534 -0
  289. package/templates/.agents/tools/cloud-terraform-plan-policy.mjs +262 -0
  290. package/templates/.agents/tools/mcp/development/meta-devtools/README.md +14 -0
  291. package/templates/.agents/tools/mcp/development/meta-devtools/manifest.yaml +28 -0
  292. package/templates/.agents/tools/mcp/infra/cloudflare/manifest.yaml +2 -3
  293. package/templates/.agents/tools/mcp/infra/hostinger/manifest.yaml +1 -3
  294. package/templates/.agents/tools/mcp/marketing/meta-ads/README.md +15 -13
  295. package/templates/.agents/tools/mcp/marketing/meta-ads/manifest.yaml +15 -10
  296. package/templates/.agents/tools/profiles/development.yaml +3 -0
  297. package/templates/infra/terraform/.terraform.lock.hcl +42 -0
  298. package/templates/infra/terraform/README.md +31 -0
  299. package/templates/infra/terraform/backend.tf.json +13 -0
  300. package/templates/infra/terraform/deployment-inputs.example.yaml +22 -0
  301. package/templates/infra/terraform/fixtures/approved-targets.json +32 -0
  302. package/templates/infra/terraform/fixtures/plan-approved.json +67 -0
  303. package/templates/infra/terraform/main.tf +77 -0
  304. package/templates/infra/terraform/outputs.tf +20 -0
  305. package/templates/infra/terraform/terraform.tfvars.example +26 -0
  306. package/templates/infra/terraform/variables.tf +91 -0
  307. package/templates/infra/terraform/versions.tf +18 -0
  308. package/templates/scripts/validate-cloud-hostinger-contract.mjs +89 -0
  309. package/templates/scripts/validate-cloud-terraform-plan.mjs +1 -0
  310. package/templates/template-manifest.json +599 -167
  311. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-BwnJpl02.js +0 -3
  312. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-C09KmlSw.js +0 -68
  313. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-Dyo_wpL4.js +0 -2
  314. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-hPPfVGQv.css +0 -1
  315. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-stale-seed.ts +0 -50
@@ -1,6 +1,11 @@
1
1
  import { DatabaseSync } from "node:sqlite";
2
2
  import { describe, expect, it } from "vitest";
3
- import { isDateOnlyCostPage, readDateOnlyCostDimensionPage } from "./sqlite-cost-agent-page.js";
3
+ import {
4
+ creationProductivityByModel,
5
+ isDateOnlyCostPage,
6
+ readDateOnlyCostDimensionPage,
7
+ sumKnownProductivity,
8
+ } from "./sqlite-cost-agent-page.js";
4
9
 
5
10
  describe("date-only Cost page", () => {
6
11
  it("accepts only period input and keeps all custom filters on the canonical path", () => {
@@ -13,7 +18,7 @@ describe("date-only Cost page", () => {
13
18
  const database = new DatabaseSync(":memory:");
14
19
  try {
15
20
  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);`,
21
+ `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, run_id TEXT, payload_json 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
22
  );
18
23
  const activity = database.prepare(
19
24
  "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',?,?,?,?,?,?,?)",
@@ -37,7 +42,7 @@ describe("date-only Cost page", () => {
37
42
  );
38
43
  expect(page.totals).toMatchObject({
39
44
  invocation_count: 2,
40
- task_count: 2,
45
+ task_count: dimension === "model" ? 3 : 2,
41
46
  total_duration: 300,
42
47
  source_event_count: 3,
43
48
  total_tokens: 200,
@@ -52,12 +57,130 @@ describe("date-only Cost page", () => {
52
57
  });
53
58
  expect(page.rows.find((row) => row.key === "unavailable")).toMatchObject({
54
59
  invocation_count: 0,
55
- task_count: 2,
60
+ task_count: 0,
56
61
  total_tokens: 40,
57
62
  });
63
+ expect(page.totals).toMatchObject({
64
+ total_tokens: page.rows.reduce((sum, row) => sum + Number(row.total_tokens ?? 0), 0),
65
+ total_cost_brl: page.rows.reduce((sum, row) => sum + Number(row.total_cost_brl ?? 0), 0),
66
+ });
67
+ if (dimension === "model") {
68
+ for (const row of page.rows)
69
+ for (const field of [
70
+ "task_count",
71
+ "prd_count",
72
+ "spec_count",
73
+ "validation_count",
74
+ "evidence_count",
75
+ "total_duration",
76
+ "total_tokens",
77
+ "total_cost_brl",
78
+ ])
79
+ expect(typeof row[field]).toBe("number");
80
+ for (const field of [
81
+ "task_count",
82
+ "prd_count",
83
+ "spec_count",
84
+ "validation_count",
85
+ "evidence_count",
86
+ "total_duration",
87
+ "total_tokens",
88
+ "total_cost_brl",
89
+ ])
90
+ expect(typeof page.totals[field]).toBe("number");
91
+ }
58
92
  }
59
93
  } finally {
60
94
  database.close();
61
95
  }
62
96
  });
97
+
98
+ it("filters creation and artifact productivity by their own canonical days", () => {
99
+ const database = new DatabaseSync(":memory:");
100
+ try {
101
+ database.exec(
102
+ `CREATE TABLE zaip_aios_activities (payload_type TEXT, activity_source_kind TEXT, occurred_day_sp TEXT, agent_invocation_id TEXT, run_id TEXT, payload_json TEXT); CREATE TABLE cost_invocation_dimension_facts (agent_invocation_id TEXT, model TEXT, provider TEXT, total_tokens REAL, cost_usd REAL, cost_brl REAL);`,
103
+ );
104
+ database
105
+ .prepare(
106
+ "INSERT INTO zaip_aios_activities VALUES (NULL, 'agent_run', '2026-08-01', 'AINV-GPT', 'RUN-GPT', NULL)",
107
+ )
108
+ .run();
109
+ database
110
+ .prepare(
111
+ "INSERT INTO zaip_aios_activities VALUES (NULL, 'agent_run', '2026-08-01', 'AINV-CLAUDE', 'RUN-CLAUDE', NULL)",
112
+ )
113
+ .run();
114
+ database
115
+ .prepare("INSERT INTO cost_invocation_dimension_facts VALUES (?, ?, ?, ?, ?, ?)")
116
+ .run("AINV-GPT", "gpt", "openai", 1, 1, 1);
117
+ database
118
+ .prepare("INSERT INTO cost_invocation_dimension_facts VALUES (?, ?, ?, ?, ?, ?)")
119
+ .run("AINV-CLAUDE", "claude", "anthropic", 1, 1, 1);
120
+ const payload = (value: object) => JSON.stringify(value);
121
+ database
122
+ .prepare(
123
+ "INSERT INTO zaip_aios_activities VALUES ('work_item', 'technical', NULL, NULL, NULL, ?)",
124
+ )
125
+ .run(
126
+ payload({
127
+ entity_kind: "prd",
128
+ id: "PRD-GPT",
129
+ creation_run_id: "RUN-GPT",
130
+ occurred_day_sp: "2026-08-01",
131
+ }),
132
+ );
133
+ database
134
+ .prepare(
135
+ "INSERT INTO zaip_aios_activities VALUES ('file', 'technical', NULL, NULL, NULL, ?)",
136
+ )
137
+ .run(
138
+ payload({
139
+ artifact_kind: "validation",
140
+ artifact_run_id: "RUN-GPT",
141
+ artifact_occurred_day_sp: "2026-08-02",
142
+ path: ".agents/records/changes/x/artifacts/tests/TEST-GPT.md",
143
+ }),
144
+ );
145
+ database
146
+ .prepare(
147
+ "INSERT INTO zaip_aios_activities VALUES ('file', 'technical', NULL, NULL, NULL, ?)",
148
+ )
149
+ .run(
150
+ payload({
151
+ artifact_kind: "evidence",
152
+ artifact_run_id: "RUN-CLAUDE",
153
+ artifact_occurred_day_sp: "2026-08-01",
154
+ path: ".agents/records/changes/x/artifacts/evidence/EVD-CLAUDE.md",
155
+ }),
156
+ );
157
+ expect(creationProductivityByModel(database, "2026-08-01", "2026-08-01")).toMatchObject([
158
+ {
159
+ key: "claude",
160
+ task_count: null,
161
+ spec_count: null,
162
+ prd_count: null,
163
+ validation_count: 0,
164
+ evidence_count: 1,
165
+ },
166
+ {
167
+ key: "gpt",
168
+ task_count: 0,
169
+ spec_count: 0,
170
+ prd_count: 1,
171
+ validation_count: null,
172
+ evidence_count: null,
173
+ },
174
+ ]);
175
+ } finally {
176
+ database.close();
177
+ }
178
+ });
179
+
180
+ it("keeps internal attribution unavailable until the Models surface normalizes it", () => {
181
+ expect(
182
+ sumKnownProductivity([{ task_count: null }, { task_count: null }], "task_count"),
183
+ ).toBeNull();
184
+ expect(sumKnownProductivity([{ task_count: 0 }, { task_count: null }], "task_count")).toBe(0);
185
+ });
63
186
  });
@@ -48,11 +48,93 @@ export function costDimensionFactDateFilter(input: DateOnlyCostPageInput): strin
48
48
  type CostDimension = "agent" | "model" | "provider";
49
49
  type SqliteQuery = {
50
50
  prepare(sql: string): {
51
- get(...values: Array<string | number>): Record<string, unknown> | undefined;
52
- all(...values: Array<string | number>): Array<Record<string, unknown>>;
51
+ get(...values: Array<string | number | null>): Record<string, unknown> | undefined;
52
+ all(...values: Array<string | number | null>): Array<Record<string, unknown>>;
53
53
  };
54
54
  };
55
55
 
56
+ export function creationProductivityByModel(
57
+ database: SqliteQuery,
58
+ from: string,
59
+ to: string,
60
+ filter: { model?: string; provider?: string } = {},
61
+ ) {
62
+ return database
63
+ .prepare(`
64
+ WITH creation_models AS (
65
+ SELECT a.run_id, MIN(f.model) AS model
66
+ FROM zaip_aios_activities a JOIN cost_invocation_dimension_facts f
67
+ ON f.agent_invocation_id=a.agent_invocation_id
68
+ WHERE a.payload_type IS NULL AND a.activity_source_kind='agent_run'
69
+ AND f.model IS NOT NULL AND trim(f.model)<>''
70
+ AND lower(f.model) NOT IN ('unavailable','unknown','mixed')
71
+ AND (f.total_tokens IS NOT NULL OR f.cost_usd IS NOT NULL OR f.cost_brl IS NOT NULL)
72
+ GROUP BY a.run_id HAVING COUNT(DISTINCT f.model)=1
73
+ ), entities AS (
74
+ SELECT json_extract(payload_json, '$.entity_kind') AS kind,
75
+ json_extract(payload_json, '$.id') AS id,
76
+ json_extract(payload_json, '$.creation_run_id') AS creation_run_id,
77
+ json_extract(payload_json, '$.occurred_day_sp') AS day
78
+ FROM zaip_aios_activities WHERE payload_type='work_item'
79
+ ), artifacts AS (
80
+ SELECT json_extract(payload_json, '$.artifact_kind') AS kind,
81
+ json_extract(payload_json, '$.artifact_run_id') AS run_id,
82
+ json_extract(payload_json, '$.path') AS path,
83
+ json_extract(payload_json, '$.artifact_occurred_day_sp') AS day
84
+ FROM zaip_aios_activities WHERE payload_type='file'
85
+ ), entity_counts AS (
86
+ SELECT c.model AS key,
87
+ COUNT(DISTINCT CASE WHEN e.kind='task' THEN e.id END) AS task_count,
88
+ COUNT(DISTINCT CASE WHEN e.kind='spec' THEN e.id END) AS spec_count,
89
+ COUNT(DISTINCT CASE WHEN e.kind='prd' THEN e.id END) AS prd_count
90
+ FROM entities e JOIN creation_models c ON c.run_id=e.creation_run_id
91
+ WHERE e.day>=? AND e.day<=?
92
+ AND (? IS NULL OR c.model=?)
93
+ AND (? IS NULL OR EXISTS (
94
+ SELECT 1 FROM zaip_aios_activities run
95
+ JOIN cost_invocation_dimension_facts usage ON usage.agent_invocation_id=run.agent_invocation_id
96
+ WHERE run.run_id=e.creation_run_id AND usage.provider=?
97
+ ))
98
+ GROUP BY c.model
99
+ ), artifact_counts AS (
100
+ SELECT c.model AS key,
101
+ COUNT(CASE WHEN artifact.kind='validation' AND artifact.path LIKE '%/artifacts/tests/%' THEN 1 END) AS validation_count,
102
+ COUNT(CASE WHEN artifact.kind='evidence' THEN 1 END) AS evidence_count
103
+ FROM artifacts artifact JOIN creation_models c ON c.run_id=artifact.run_id
104
+ WHERE artifact.day>=? AND artifact.day<=?
105
+ AND (? IS NULL OR c.model=?)
106
+ AND (? IS NULL OR EXISTS (
107
+ SELECT 1 FROM zaip_aios_activities run
108
+ JOIN cost_invocation_dimension_facts usage ON usage.agent_invocation_id=run.agent_invocation_id
109
+ WHERE run.run_id=artifact.run_id AND usage.provider=?
110
+ ))
111
+ GROUP BY c.model
112
+ ), keys AS (SELECT key FROM entity_counts UNION SELECT key FROM artifact_counts)
113
+ SELECT keys.key, entity_counts.task_count, entity_counts.spec_count, entity_counts.prd_count,
114
+ artifact_counts.validation_count, artifact_counts.evidence_count
115
+ FROM keys LEFT JOIN entity_counts USING(key) LEFT JOIN artifact_counts USING(key)`)
116
+ .all(
117
+ from,
118
+ to,
119
+ filter.model ?? null,
120
+ filter.model ?? null,
121
+ filter.provider ?? null,
122
+ filter.provider ?? null,
123
+ from,
124
+ to,
125
+ filter.model ?? null,
126
+ filter.model ?? null,
127
+ filter.provider ?? null,
128
+ filter.provider ?? null,
129
+ );
130
+ }
131
+
132
+ /** A missing attribution remains unavailable even when other model keys exist. */
133
+ export function sumKnownProductivity(rows: readonly Record<string, unknown>[], field: string) {
134
+ const known = rows.filter((row) => typeof row[field] === "number");
135
+ return known.length === 0 ? null : known.reduce((sum, row) => sum + Number(row[field]), 0);
136
+ }
137
+
56
138
  /** Reads the date picker population from schema-67 physical facts only. */
57
139
  export function readDateOnlyCostDimensionPage(
58
140
  database: SqliteQuery,
@@ -86,24 +168,82 @@ export function readDateOnlyCostDimensionPage(
86
168
  dimension === "model"
87
169
  ? ` 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
170
  : "";
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)`;
171
+ 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 AND key<>'unavailable' 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
172
  const args =
91
173
  dimension === "model"
92
174
  ? [from, to, from, to, from, to, from, to]
93
175
  : [from, to, from, to, from, to];
176
+ const productivity =
177
+ dimension === "model"
178
+ ? "(SELECT SUM(task_count) FROM task_population) AS task_count, 0 AS prd_count, 0 AS spec_count, 0 AS validation_count, 0 AS evidence_count"
179
+ : "(SELECT COUNT(DISTINCT task_id) FROM population_dimensions) task_count";
180
+ const rowProductivity =
181
+ dimension === "model"
182
+ ? "COALESCE(task_population.task_count,0) AS task_count,0 AS prd_count,0 AS spec_count,0 AS validation_count,0 AS evidence_count"
183
+ : "COALESCE(task_population.task_count,0) task_count";
94
184
  const total_count = Number(
95
185
  database.prepare(`${cte} SELECT COUNT(*) AS count FROM keys`).get(...args)?.count ?? 0,
96
186
  );
97
187
  const totals =
98
188
  database
99
189
  .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`,
190
+ `${cte} SELECT (SELECT COUNT(*) FROM population_ranked WHERE dimension_rank=1) invocation_count, ${productivity}, (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 financial) total_tokens, (SELECT SUM(total_cost_usd) FROM financial) total_cost_usd, (SELECT SUM(total_cost_brl) FROM financial) total_cost_brl`,
101
191
  )
102
192
  .get(...args) ?? {};
103
193
  const rows = database
104
194
  .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 ?`,
195
+ `${cte} SELECT keys.key,keys.key AS label,COALESCE(population.invocation_count,0) invocation_count,${rowProductivity},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
196
  )
107
197
  .all(...args, limit, offset);
198
+ if (dimension === "model") {
199
+ const productivity = new Map(
200
+ creationProductivityByModel(database, from, to).map((row) => [String(row.key), row]),
201
+ );
202
+ for (const row of rows) {
203
+ const extra = productivity.get(String(row.key));
204
+ Object.assign(row, {
205
+ prd_count: extra?.prd_count ?? 0,
206
+ spec_count: extra?.spec_count ?? 0,
207
+ validation_count: extra?.validation_count ?? 0,
208
+ evidence_count: extra?.evidence_count ?? 0,
209
+ });
210
+ }
211
+ const known = [...productivity.values()];
212
+ Object.assign(totals, {
213
+ prd_count: sumKnownProductivity(known, "prd_count") ?? 0,
214
+ spec_count: sumKnownProductivity(known, "spec_count") ?? 0,
215
+ validation_count: sumKnownProductivity(known, "validation_count") ?? 0,
216
+ evidence_count: sumKnownProductivity(known, "evidence_count") ?? 0,
217
+ });
218
+ const numericFields = [
219
+ "invocation_count",
220
+ "task_count",
221
+ "prd_count",
222
+ "spec_count",
223
+ "validation_count",
224
+ "evidence_count",
225
+ "total_duration",
226
+ "duration_unknown",
227
+ "source_event_count",
228
+ "total_input_tokens",
229
+ "total_cached_input_tokens",
230
+ "total_output_tokens",
231
+ "total_reasoning_tokens",
232
+ "total_tokens",
233
+ "total_cost_usd",
234
+ "total_cost_brl",
235
+ ];
236
+ for (const row of rows) {
237
+ const hasKnownDuration = typeof row.total_duration === "number";
238
+ for (const field of numericFields)
239
+ row[field] = typeof row[field] === "number" ? row[field] : 0;
240
+ if (typeof row.duration_known !== "number" && !hasKnownDuration) row.duration_known = 0;
241
+ }
242
+ const totalsHaveKnownDuration = typeof totals.total_duration === "number";
243
+ for (const field of numericFields)
244
+ totals[field] = typeof totals[field] === "number" ? totals[field] : 0;
245
+ if (typeof totals.duration_known !== "number" && !totalsHaveKnownDuration)
246
+ totals.duration_known = 0;
247
+ }
108
248
  return { total_count, totals, rows };
109
249
  }
@@ -9,7 +9,12 @@ type CostDimensionAggregateRow = {
9
9
  key: string;
10
10
  label: string;
11
11
  invocation_count: number;
12
- task_count: number;
12
+ task_count: number | null;
13
+ conversation_count: number;
14
+ prd_count: number | null;
15
+ spec_count: number | null;
16
+ test_count: number | null;
17
+ evidence_count: number | null;
13
18
  total_duration: number | null;
14
19
  source_event_count: number | null;
15
20
  total_tokens: number | null;
@@ -72,7 +77,7 @@ export function buildCostDimensionAggregates(
72
77
  );
73
78
  const activities = database
74
79
  .prepare(`SELECT activity_row_id, agent_invocation_id, task_id, duration_ms,
75
- source_event_count, provider, model, total_tokens, cost_usd, cost_brl,
80
+ source_event_count, provider, model, total_tokens, cost_usd, cost_brl, run_id,
76
81
  activity_source_kind, zaip_agent, zaip_agent_persona, zaip_agent_job,
77
82
  execution_id, conversation_id, occurred_at, occurred_day_sp, llm_call_count,
78
83
  input_tokens, cached_input_tokens, output_tokens, reasoning_tokens
@@ -99,6 +104,11 @@ export function buildCostDimensionAggregates(
99
104
  label: key,
100
105
  invocation_count: 0,
101
106
  task_count: 0,
107
+ conversation_count: 0,
108
+ prd_count: 0,
109
+ spec_count: 0,
110
+ test_count: 0,
111
+ evidence_count: 0,
102
112
  total_duration: null,
103
113
  source_event_count: null,
104
114
  total_tokens: null,
@@ -174,31 +184,127 @@ export function buildCostDimensionAggregates(
174
184
  }
175
185
  }
176
186
  }
177
- const taskKeys = new Map<"model" | "provider", Map<string, Set<string>>>(
178
- dimensions.map((dimension) => [dimension, new Map()]),
187
+ // Productivity is a creation fact, never inferred from a later Run that
188
+ // happened to reference the Task. A multi-model creation Run has no
189
+ // defensible single owner, so its entities remain unavailable by model.
190
+ const modelsByCreationRun = new Map<string, Set<string>>();
191
+ for (const [id, invocationSegments] of segmentsByInvocation) {
192
+ const keys = [
193
+ ...new Set(
194
+ invocationSegments
195
+ .filter((segment) => {
196
+ const model = observedDimensionValue(segment.model);
197
+ const provider = observedDimensionValue(segment.provider);
198
+ return (
199
+ model !== null &&
200
+ provider !== null &&
201
+ allowedProviders.has(provider) &&
202
+ (typeof segment.total_tokens === "number" ||
203
+ typeof segment.cost_usd === "number" ||
204
+ typeof segment.cost_brl === "number")
205
+ );
206
+ })
207
+ .map((segment) => observedDimensionValue(segment.model)!),
208
+ ),
209
+ ];
210
+ const runId = activityByInvocation.get(id)?.run_id;
211
+ if (typeof runId !== "string") continue;
212
+ const observed = modelsByCreationRun.get(runId) ?? new Set<string>();
213
+ for (const key of keys) observed.add(key);
214
+ modelsByCreationRun.set(runId, observed);
215
+ }
216
+ const modelByCreationRun = new Map(
217
+ [...modelsByCreationRun].flatMap(([runId, models]) =>
218
+ models.size === 1 ? [[runId, [...models][0]!] as const] : [],
219
+ ),
179
220
  );
221
+ const createdSpecsByModel = new Map<string, Set<string>>();
222
+ const createdPrdsByModel = new Map<string, Set<string>>();
223
+ for (const item of database
224
+ .prepare(
225
+ "SELECT entity_kind, id, creation_run_id FROM canonical_work_items WHERE creation_run_id IS NOT NULL",
226
+ )
227
+ .all() as Array<{ entity_kind: string; id: string; creation_run_id: string }>) {
228
+ const model = modelByCreationRun.get(item.creation_run_id);
229
+ if (model === undefined) continue;
230
+ const target =
231
+ item.entity_kind === "spec"
232
+ ? createdSpecsByModel
233
+ : item.entity_kind === "prd"
234
+ ? createdPrdsByModel
235
+ : null;
236
+ if (target === null) continue;
237
+ const values = target.get(model) ?? new Set<string>();
238
+ values.add(item.id);
239
+ target.set(model, values);
240
+ }
241
+ const artifactsByModel = new Map<string, { tests: number; evidences: number }>();
242
+ for (const artifact of database
243
+ .prepare(
244
+ "SELECT path, artifact_kind, artifact_run_id FROM files WHERE artifact_run_id IS NOT NULL AND artifact_kind IN ('validation', 'evidence')",
245
+ )
246
+ .all() as Array<{ path: string; artifact_kind: string; artifact_run_id: string }>) {
247
+ if (artifact.artifact_kind === "validation" && !artifact.path.includes("/artifacts/tests/"))
248
+ continue;
249
+ const model = modelByCreationRun.get(artifact.artifact_run_id);
250
+ if (model === undefined) continue;
251
+ const current = artifactsByModel.get(model) ?? { tests: 0, evidences: 0 };
252
+ if (artifact.artifact_kind === "validation") current.tests += 1;
253
+ else current.evidences += 1;
254
+ artifactsByModel.set(model, current);
255
+ }
256
+ for (const [model, artifacts] of artifactsByModel) {
257
+ const row = ensure("model", model);
258
+ row.test_count = artifacts.tests;
259
+ row.evidence_count = artifacts.evidences;
260
+ }
261
+ for (const [model, specs] of createdSpecsByModel) ensure("model", model).spec_count = specs.size;
262
+ for (const [model, prds] of createdPrdsByModel) ensure("model", model).prd_count = prds.size;
263
+ const modelTasks = new Map<string, Set<string>>();
264
+ const modelConversations = new Map<string, Set<string>>();
265
+ const providerTasks = new Map<string, Set<string>>();
180
266
  for (const [id, invocationSegments] of segmentsByInvocation) {
181
- const taskId = activityByInvocation.get(id)?.task_id;
182
- if (typeof taskId !== "string") continue;
183
- for (const dimension of dimensions) {
184
- const byTask = taskKeys.get(dimension)!;
185
- const keys = byTask.get(taskId) ?? new Set<string>();
186
- for (const segment of invocationSegments) {
187
- const key = observedDimensionValue(segment[dimension]);
188
- const provider = observedDimensionValue(segment.provider);
189
- const hasFinancialFact =
190
- typeof segment.total_tokens === "number" ||
191
- typeof segment.cost_usd === "number" ||
192
- typeof segment.cost_brl === "number";
193
- if (key !== null && provider !== null && allowedProviders.has(provider) && hasFinancialFact)
194
- keys.add(key);
267
+ const activity = activityByInvocation.get(id);
268
+ const taskId = activity?.task_id;
269
+ for (const segment of invocationSegments) {
270
+ const provider = observedDimensionValue(segment.provider);
271
+ const model = observedDimensionValue(segment.model);
272
+ const hasFinancialFact =
273
+ typeof segment.total_tokens === "number" ||
274
+ typeof segment.cost_usd === "number" ||
275
+ typeof segment.cost_brl === "number";
276
+ if (provider === null || !allowedProviders.has(provider) || !hasFinancialFact) continue;
277
+ if (model !== null) {
278
+ if (typeof taskId === "string" && activity?.activity_source_kind !== "unassisted_turn") {
279
+ const tasks = modelTasks.get(model) ?? new Set<string>();
280
+ tasks.add(taskId);
281
+ modelTasks.set(model, tasks);
282
+ }
195
283
  }
196
- byTask.set(taskId, keys);
284
+ const conversationId = activity?.conversation_id;
285
+ const isConversation =
286
+ typeof conversationId === "string" &&
287
+ (activity?.activity_source_kind === "unassisted_turn" || typeof taskId !== "string");
288
+ if (isConversation) {
289
+ const conversationModel = model ?? "unavailable";
290
+ const conversations = modelConversations.get(conversationModel) ?? new Set<string>();
291
+ conversations.add(conversationId);
292
+ modelConversations.set(conversationModel, conversations);
293
+ }
294
+ if (typeof taskId !== "string") continue;
295
+ const tasks = providerTasks.get(provider) ?? new Set<string>();
296
+ tasks.add(taskId);
297
+ providerTasks.set(provider, tasks);
197
298
  }
198
299
  }
199
- for (const dimension of dimensions)
200
- for (const keys of taskKeys.get(dimension)!.values())
201
- for (const key of keys) ensure(dimension, key).task_count += 1;
300
+ // A Task participates once per observed model. It can therefore appear in
301
+ // more than one Modelos LLM row, and the model footer is intentionally the
302
+ // sum of those row memberships.
303
+ for (const [model, tasks] of modelTasks) ensure("model", model).task_count = tasks.size;
304
+ for (const [model, conversations] of modelConversations)
305
+ ensure("model", model).conversation_count = conversations.size;
306
+ for (const [provider, tasks] of providerTasks)
307
+ ensure("provider", provider).task_count = tasks.size;
202
308
  for (const activity of activities) {
203
309
  if (typeof activity.agent_invocation_id === "string") continue;
204
310
  const hasFinancialFact =
@@ -212,7 +318,6 @@ export function buildCostDimensionAggregates(
212
318
  const key = observedDimensionValue(activity[dimension]) ?? "unavailable";
213
319
  const target = ensure(dimension, key);
214
320
  target.invocation_count += 1;
215
- if (typeof activity.task_id === "string") target.task_count += 1;
216
321
  if (typeof activity.duration_ms === "number")
217
322
  target.total_duration = (target.total_duration ?? 0) + activity.duration_ms;
218
323
  if (typeof activity.source_event_count === "number")
@@ -221,6 +326,29 @@ export function buildCostDimensionAggregates(
221
326
  add(target, "total_cost_usd", activity.cost_usd);
222
327
  add(target, "total_cost_brl", activity.cost_brl);
223
328
  }
329
+ const model = observedDimensionValue(activity.model);
330
+ const conversationId = activity.conversation_id;
331
+ if (model !== null) {
332
+ if (
333
+ typeof activity.task_id === "string" &&
334
+ activity.activity_source_kind !== "unassisted_turn"
335
+ ) {
336
+ const tasks = modelTasks.get(model) ?? new Set<string>();
337
+ tasks.add(activity.task_id);
338
+ modelTasks.set(model, tasks);
339
+ ensure("model", model).task_count = tasks.size;
340
+ }
341
+ }
342
+ if (
343
+ typeof conversationId === "string" &&
344
+ (activity.activity_source_kind === "unassisted_turn" || typeof activity.task_id !== "string")
345
+ ) {
346
+ const conversationModel = model ?? "unavailable";
347
+ const conversations = modelConversations.get(conversationModel) ?? new Set<string>();
348
+ conversations.add(conversationId);
349
+ modelConversations.set(conversationModel, conversations);
350
+ ensure("model", conversationModel).conversation_count = conversations.size;
351
+ }
224
352
  }
225
353
  const base = Object.fromEntries(
226
354
  dimensions.map((dimension) => [
@@ -256,6 +384,11 @@ export function buildCostDimensionAggregates(
256
384
  occurred_at: "",
257
385
  invocation_count: 0,
258
386
  task_count: 0,
387
+ conversation_count: 0,
388
+ prd_count: null,
389
+ spec_count: null,
390
+ test_count: null,
391
+ evidence_count: null,
259
392
  llm_call_count: null,
260
393
  total_duration: null,
261
394
  duration_unknown: 0,
@@ -3,39 +3,48 @@ import type { DatabaseSync } from "node:sqlite";
3
3
  /** Freezes legacy Cost facts and their entity labels once for fast date-only pages. */
4
4
  export function materializeCostGroupDailyProjection(database: DatabaseSync): void {
5
5
  database.exec(`
6
- CREATE TABLE cost_group_daily_projection AS SELECT * FROM zaip_vw_cost_monitor_group_daily_cache;
7
- CREATE INDEX cost_group_daily_projection_lookup ON cost_group_daily_projection(group_kind, day, group_value);
6
+ CREATE TABLE IF NOT EXISTS cost_group_daily_projection AS SELECT * FROM zaip_vw_cost_monitor_group_daily_cache LIMIT 0;
7
+ DELETE FROM cost_group_daily_projection;
8
+ INSERT INTO cost_group_daily_projection SELECT * FROM zaip_vw_cost_monitor_group_daily_cache;
9
+ CREATE INDEX IF NOT EXISTS cost_group_daily_projection_lookup ON cost_group_daily_projection(group_kind, day, group_value);
8
10
 
9
11
  -- The public entity views expand canonical records and activities. Reading
10
12
  -- them for every grouped page was the remaining multi-second cost, most
11
13
  -- visibly for conversations. Persist exactly the three presentation fields
12
14
  -- the old LEFT JOIN supplied, keyed by the already-materialized population.
13
- CREATE TABLE cost_group_entity_projection AS
15
+ CREATE TABLE IF NOT EXISTS cost_group_entity_projection(group_kind TEXT, group_value TEXT, title TEXT, status TEXT, risk TEXT);
16
+ DELETE FROM cost_group_entity_projection;
17
+ INSERT INTO cost_group_entity_projection
18
+ WITH tasks AS MATERIALIZED (SELECT * FROM zaip_vw_tasks),
19
+ specs AS MATERIALIZED (SELECT * FROM zaip_vw_specs),
20
+ prds AS MATERIALIZED (SELECT * FROM zaip_vw_prds),
21
+ executions AS MATERIALIZED (SELECT * FROM zaip_vw_executions),
22
+ conversations AS MATERIALIZED (SELECT * FROM zaip_vw_conversations)
14
23
  SELECT 'task' AS group_kind, groups.group_value, entity.title, entity.status, entity.risk
15
24
  FROM (SELECT DISTINCT group_value FROM cost_group_daily_projection
16
25
  WHERE group_kind = 'task' AND group_value IS NOT NULL) groups
17
- LEFT JOIN zaip_vw_tasks entity ON entity.id = groups.group_value
26
+ LEFT JOIN tasks entity ON entity.id = groups.group_value
18
27
  UNION ALL
19
28
  SELECT 'spec', groups.group_value, entity.title, entity.status, entity.risk
20
29
  FROM (SELECT DISTINCT group_value FROM cost_group_daily_projection
21
30
  WHERE group_kind = 'spec' AND group_value IS NOT NULL) groups
22
- LEFT JOIN zaip_vw_specs entity ON entity.id = groups.group_value
31
+ LEFT JOIN specs entity ON entity.id = groups.group_value
23
32
  UNION ALL
24
33
  SELECT 'prd', groups.group_value, entity.title, entity.status, entity.risk
25
34
  FROM (SELECT DISTINCT group_value FROM cost_group_daily_projection
26
35
  WHERE group_kind = 'prd' AND group_value IS NOT NULL) groups
27
- LEFT JOIN zaip_vw_prds entity ON entity.id = groups.group_value
36
+ LEFT JOIN prds entity ON entity.id = groups.group_value
28
37
  UNION ALL
29
38
  SELECT 'execution', groups.group_value, NULL, entity.status, NULL
30
39
  FROM (SELECT DISTINCT group_value FROM cost_group_daily_projection
31
40
  WHERE group_kind = 'execution' AND group_value IS NOT NULL) groups
32
- LEFT JOIN zaip_vw_executions entity ON entity.id = groups.group_value
41
+ LEFT JOIN executions entity ON entity.id = groups.group_value
33
42
  UNION ALL
34
43
  SELECT 'conversation', groups.group_value, NULL, entity.status, NULL
35
44
  FROM (SELECT DISTINCT group_value FROM cost_group_daily_projection
36
45
  WHERE group_kind = 'conversation' AND group_value IS NOT NULL) groups
37
- LEFT JOIN zaip_vw_conversations entity ON entity.id = groups.group_value;
38
- CREATE UNIQUE INDEX cost_group_entity_projection_lookup
46
+ LEFT JOIN conversations entity ON entity.id = groups.group_value;
47
+ CREATE UNIQUE INDEX IF NOT EXISTS cost_group_entity_projection_lookup
39
48
  ON cost_group_entity_projection(group_kind, group_value);
40
49
  `);
41
50
  }