@zaip-ai/cli 1.3.7 → 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 (332) hide show
  1. package/README.md +86 -1
  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 +26 -0
  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 +3 -1
  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-install.d.ts +14 -0
  42. package/dist/services/stack-install.d.ts.map +1 -0
  43. package/dist/services/stack-install.js +176 -0
  44. package/dist/services/stack-install.js.map +1 -0
  45. package/dist/services/stack-node.d.ts +12 -0
  46. package/dist/services/stack-node.d.ts.map +1 -0
  47. package/dist/services/stack-node.js +150 -0
  48. package/dist/services/stack-node.js.map +1 -0
  49. package/dist/services/stack-package-manager.d.ts +12 -0
  50. package/dist/services/stack-package-manager.d.ts.map +1 -0
  51. package/dist/services/stack-package-manager.js +178 -0
  52. package/dist/services/stack-package-manager.js.map +1 -0
  53. package/dist/services/stack-plan.d.ts +35 -0
  54. package/dist/services/stack-plan.d.ts.map +1 -0
  55. package/dist/services/stack-plan.js +130 -0
  56. package/dist/services/stack-plan.js.map +1 -0
  57. package/dist/services/stack-terraform.d.ts +13 -0
  58. package/dist/services/stack-terraform.d.ts.map +1 -0
  59. package/dist/services/stack-terraform.js +274 -0
  60. package/dist/services/stack-terraform.js.map +1 -0
  61. package/dist/services/template-manifest.d.ts.map +1 -1
  62. package/dist/services/template-manifest.js +14 -1
  63. package/dist/services/template-manifest.js.map +1 -1
  64. package/dist/types.d.ts +4 -0
  65. package/dist/types.d.ts.map +1 -1
  66. package/package.json +1 -1
  67. package/templates/.agents/.env.example +34 -0
  68. package/templates/{README.md → .agents/README.md} +124 -60
  69. package/templates/.agents/config/agents/catalog.yaml +23 -23
  70. package/templates/.agents/config/clients/codex/agents/zaip-agent-analyst.toml +1 -0
  71. package/templates/.agents/config/clients/codex/agents/zaip-agent-auditor.toml +1 -0
  72. package/templates/.agents/config/clients/codex/agents/zaip-agent-backend.toml +5 -4
  73. package/templates/.agents/config/clients/codex/agents/zaip-agent-brand-strategist.toml +1 -0
  74. package/templates/.agents/config/clients/codex/agents/zaip-agent-builder.toml +1 -0
  75. package/templates/.agents/config/clients/codex/agents/zaip-agent-cloud-engineer.toml +10 -7
  76. package/templates/.agents/config/clients/codex/agents/zaip-agent-data-analyst.toml +1 -0
  77. package/templates/.agents/config/clients/codex/agents/zaip-agent-designer.toml +1 -0
  78. package/templates/.agents/config/clients/codex/agents/zaip-agent-finalizer.toml +1 -0
  79. package/templates/.agents/config/clients/codex/agents/zaip-agent-frontend.toml +5 -4
  80. package/templates/.agents/config/clients/codex/agents/zaip-agent-general-operations.toml +1 -0
  81. package/templates/.agents/config/clients/codex/agents/zaip-agent-governance-compliance.toml +1 -0
  82. package/templates/.agents/config/clients/codex/agents/zaip-agent-growth-strategist.toml +1 -0
  83. package/templates/.agents/config/clients/codex/agents/zaip-agent-integrator.toml +1 -0
  84. package/templates/.agents/config/clients/codex/agents/zaip-agent-marketing-analyst.toml +5 -4
  85. package/templates/.agents/config/clients/codex/agents/zaip-agent-orchestrator.toml +1 -0
  86. package/templates/.agents/config/clients/codex/agents/zaip-agent-refactoring-specialist.toml +1 -0
  87. package/templates/.agents/config/clients/codex/agents/zaip-agent-reviewer.toml +1 -0
  88. package/templates/.agents/config/clients/codex/agents/zaip-agent-sales-specialist.toml +1 -0
  89. package/templates/.agents/config/clients/codex/agents/zaip-agent-security-auditor.toml +1 -0
  90. package/templates/.agents/config/clients/codex/agents/zaip-agent-task-executor.toml +1 -0
  91. package/templates/.agents/config/clients/codex/agents/zaip-agent-website-developer.toml +5 -4
  92. package/templates/.agents/config/clients/codex/agents/zaip-agent-writer.toml +1 -0
  93. package/templates/.agents/config/pricing/models.json +25 -1
  94. package/templates/.agents/config/project.yaml +2 -0
  95. package/templates/.agents/dashboard/.zaip-managed.json +214 -94
  96. package/templates/.agents/dashboard/DATA-DICTIONARY.md +1 -1
  97. package/templates/.agents/dashboard/README.md +58 -19
  98. package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-DwVZOmMV.js → CostCalculationGuidePage-snUCQYTC.js} +1 -1
  99. package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog--czwAlcH.js → DashboardHelpDialog-oZVlf-l5.js} +787 -723
  100. package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-CyjMXkm8.js → PricingSettingsDialog-BPtVUGgs.js} +1 -1
  101. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-3Awyaovx.js +3 -0
  102. package/templates/.agents/dashboard/apps/frontend/dist/assets/{ProjectSettingsPage-BomxqCBU.css → ProjectSettingsPage-GgzpZ_PY.css} +1 -1
  103. package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-CAqYVin1.js → TraceabilityWorkspace-BYjVOHI_.js} +1 -1
  104. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-DbaLbU2q.js +68 -0
  105. package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-DXBOtWNI.js → icons-vendor-TPUT2IUt.js} +1 -1
  106. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-Cmlx8OYH.css +1 -0
  107. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-ZJFXmK7m.js +2 -0
  108. package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-DaNXQPyk.js → react-vendor-Ct48XHGL.js} +1 -1
  109. package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-I4AKqVDX.js → traceability-BDe4_Ubl.js} +1 -1
  110. package/templates/.agents/dashboard/apps/frontend/dist/index.html +6 -6
  111. package/templates/.agents/dashboard/apps/frontend/src/App.tsx +73 -67
  112. package/templates/.agents/dashboard/apps/frontend/src/AppLoadingBoundary.tsx +35 -7
  113. package/templates/.agents/dashboard/apps/frontend/src/AppWorkBoard.ts +55 -0
  114. package/templates/.agents/dashboard/apps/frontend/src/ProjectSettingsPage.tsx +15 -2
  115. package/templates/.agents/dashboard/apps/frontend/src/ProjectStackEditor.tsx +33 -6
  116. package/templates/.agents/dashboard/apps/frontend/src/app-loading-boundary.css +98 -0
  117. package/templates/.agents/dashboard/apps/frontend/src/cost-period-refresh.test.ts +5 -5
  118. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api-reconciliation.ts +33 -0
  119. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.test.ts +75 -0
  120. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.ts +9 -8
  121. package/templates/.agents/dashboard/apps/frontend/src/project-settings-model.ts +15 -3
  122. package/templates/.agents/dashboard/apps/frontend/src/project-settings-page.css +0 -104
  123. package/templates/.agents/dashboard/apps/frontend/src/project-settings.test.ts +61 -0
  124. package/templates/.agents/dashboard/apps/frontend/src/project-stack-versions.css +117 -0
  125. package/templates/.agents/dashboard/apps/frontend/src/snapshot-coherence.test.ts +78 -2
  126. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.test.ts +28 -0
  127. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.ts +8 -5
  128. package/templates/.agents/dashboard/apps/frontend/src/use-app-effects.ts +180 -1
  129. package/templates/.agents/dashboard/modules/cost-monitor/src/contracts.ts +7 -4
  130. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorContent.tsx +13 -7
  131. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.tsx +14 -0
  132. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTableDisplay.ts +211 -0
  133. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.test.ts +152 -157
  134. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.tsx +155 -142
  135. package/templates/.agents/dashboard/modules/cost-monitor/src/react/InvocationManifestQuery.test.ts +145 -0
  136. package/templates/.agents/dashboard/modules/cost-monitor/src/react/period-cost-groups.ts +7 -5
  137. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-compact-cost-page.ts +4 -1
  138. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-group-page.ts +4 -1
  139. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-run-page.ts +4 -1
  140. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-projection.ts +4 -4
  141. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.test.ts +79 -1
  142. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.tsx +105 -11
  143. package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +477 -327
  144. package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +430 -285
  145. package/templates/.agents/dashboard/packages/local-runtime/scripts/build-sqlite-runtime.mjs +14 -2
  146. package/templates/.agents/dashboard/packages/local-runtime/scripts/rebuild-sqlite-read-model.mjs +6 -26
  147. package/templates/.agents/dashboard/packages/local-runtime/scripts/validate-sqlite-read-model.mjs +1 -1
  148. package/templates/.agents/dashboard/packages/local-runtime/scripts/verify-persistent-launch.mjs +268 -0
  149. package/templates/.agents/dashboard/packages/local-runtime/server.mjs +482 -332
  150. package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.test.ts +243 -1
  151. package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.ts +252 -41
  152. package/templates/.agents/dashboard/packages/local-runtime/src/server-agent-assets.ts +39 -0
  153. package/templates/.agents/dashboard/packages/local-runtime/src/server-browser-lease.ts +33 -0
  154. package/templates/.agents/dashboard/packages/local-runtime/src/server-reconciliation-refresh.test.ts +148 -11
  155. package/templates/.agents/dashboard/packages/local-runtime/src/server-reconciliation-result.test.ts +68 -0
  156. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-refresh.test.ts +19 -0
  157. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-refresh.ts +39 -43
  158. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-revision.test.ts +42 -0
  159. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-revision.ts +26 -0
  160. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-synchronization.ts +34 -0
  161. package/templates/.agents/dashboard/packages/local-runtime/src/server-startup.ts +30 -0
  162. package/templates/.agents/dashboard/packages/local-runtime/src/server-work-reconciliation.test.ts +89 -0
  163. package/templates/.agents/dashboard/packages/local-runtime/src/server-work-reconciliation.ts +88 -0
  164. package/templates/.agents/dashboard/packages/local-runtime/src/server-workspace.ts +68 -7
  165. package/templates/.agents/dashboard/packages/local-runtime/src/server.test.ts +11 -5
  166. package/templates/.agents/dashboard/packages/local-runtime/src/server.ts +129 -99
  167. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-agent-productivity.test.ts +111 -0
  168. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-agent-productivity.ts +140 -0
  169. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-agent-page.test.ts +127 -4
  170. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-agent-page.ts +145 -5
  171. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-aggregates.ts +156 -23
  172. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-group-projection.ts +18 -9
  173. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-projection.ts +52 -0
  174. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-first-fold-persistence.ts +35 -13
  175. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-force-canonical.test.ts +56 -0
  176. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-incremental-cache.test.ts +29 -0
  177. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-incremental-cache.ts +136 -0
  178. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-incremental-refresh.test.ts +251 -0
  179. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-operations-projection.ts +22 -7
  180. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-cache.test-support.ts +1 -1
  181. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-canonical-parsing.ts +42 -0
  182. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-canonical.ts +793 -0
  183. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider-duration.test.ts +270 -0
  184. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider-fixture.ts +3 -2
  185. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider.test.ts +18 -4
  186. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-source-fingerprint.test.ts +87 -0
  187. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-source-fingerprint.ts +105 -16
  188. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.test.ts +131 -59
  189. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.ts +1000 -1115
  190. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-cache.ts +13 -92
  191. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-claims.test.mjs +79 -0
  192. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.mjs +27 -0
  193. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-lifecycle.mjs +6 -6
  194. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-prerequisites.mjs +1 -1
  195. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-refresh.mjs +2 -10
  196. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-server.mjs +142 -0
  197. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.mjs +231 -239
  198. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.test.mjs +18 -29
  199. package/templates/.agents/dashboard/packages/windows-launcher/Program.cs +6 -0
  200. package/templates/.agents/dashboard/stack-dependencies.json +14 -0
  201. package/templates/.agents/dashboard/zaip-dashboard-windows.exe +0 -0
  202. package/templates/.agents/requirements/autonomous-assurance.md +1 -1
  203. package/templates/.agents/requirements/cloud-hostinger-autonomy.md +47 -0
  204. package/templates/.agents/requirements/comparative-audit.md +1 -1
  205. package/templates/.agents/requirements/context-engine.md +1 -1
  206. package/templates/.agents/requirements/cost-observability.md +1 -1
  207. package/templates/.agents/requirements/dashboard-launch.md +2 -2
  208. package/templates/.agents/requirements/data-export.md +1 -1
  209. package/templates/.agents/requirements/design-system.md +1 -1
  210. package/templates/.agents/requirements/execution-engine.md +1 -1
  211. package/templates/.agents/requirements/kanban-dashboard.md +1 -1
  212. package/templates/.agents/requirements/model-orchestration.md +1 -1
  213. package/templates/.agents/requirements/public-distribution.md +1 -1
  214. package/templates/.agents/requirements/software-architecture.md +1 -1
  215. package/templates/.agents/requirements/strategic-improvements.md +1 -1
  216. package/templates/.agents/requirements/work-lifecycle.md +1 -1
  217. package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
  218. package/templates/.agents/rules/autonomy-decision.md +1 -1
  219. package/templates/.agents/rules/cloud-hostinger-autonomy.md +21 -0
  220. package/templates/.agents/rules/core.md +5 -4
  221. package/templates/.agents/rules/local-sqlite-storage.md +18 -0
  222. package/templates/.agents/rules/meta-mcp.md +21 -0
  223. package/templates/.agents/rules/workflow.md +1 -0
  224. package/templates/.agents/runtime/bin/zaip.mjs +305 -137
  225. package/templates/.agents/runtime/checksums.sha256 +2 -2
  226. package/templates/.agents/runtime/provenance.json +2 -2
  227. package/templates/.agents/skills/zaip-skill-api-to-mcp/SKILL.md +4 -0
  228. package/templates/.agents/skills/zaip-skill-brand-governance/SKILL.md +25 -9
  229. package/templates/.agents/skills/zaip-skill-brand-governance/references/brandbook-framework.md +2 -2
  230. package/templates/.agents/skills/zaip-skill-brand-governance/references/brandbook-standards.md +2 -2
  231. package/templates/.agents/skills/zaip-skill-change/references/observable-evidence.md +15 -0
  232. package/templates/.agents/skills/zaip-skill-dashboard-sqlite/SKILL.md +13 -5
  233. package/templates/.agents/skills/zaip-skill-data-analyst/SKILL.md +10 -5
  234. package/templates/.agents/skills/zaip-skill-execution/SKILL.md +5 -4
  235. package/templates/.agents/skills/zaip-skill-frontend-launcher/SKILL.md +4 -3
  236. package/templates/.agents/skills/zaip-skill-frontend-launcher/references/launcher-contract.md +5 -3
  237. package/templates/.agents/skills/zaip-skill-github-deploy/SKILL.md +5 -5
  238. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/preflight.mjs +78 -13
  239. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/preflight.test.mjs +59 -0
  240. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/yaml-parser.mjs +157 -0
  241. package/templates/.agents/skills/zaip-skill-growth-strategy-revops/SKILL.md +11 -6
  242. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/SKILL.md +17 -0
  243. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/agents/openai.yaml +7 -0
  244. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/references/autonomous-cloud-inputs.example.json +41 -0
  245. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/references/autonomous-runner.md +84 -0
  246. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/references/operational-contract.md +19 -0
  247. package/templates/.agents/skills/zaip-skill-local-credentials/SKILL.md +43 -0
  248. package/templates/.agents/skills/zaip-skill-local-credentials/agents/openai.yaml +6 -0
  249. package/templates/.agents/skills/zaip-skill-local-credentials/references/dotenv-resolution.md +47 -0
  250. package/templates/.agents/skills/zaip-skill-marketing/SKILL.md +8 -3
  251. package/templates/.agents/skills/zaip-skill-meta-ads/SKILL.md +16 -0
  252. package/templates/.agents/skills/zaip-skill-meta-ads/agents/openai.yaml +4 -0
  253. package/templates/.agents/skills/zaip-skill-meta-ads/references/setup.md +3 -0
  254. package/templates/.agents/skills/zaip-skill-meta-ads-setup/SKILL.md +24 -0
  255. package/templates/.agents/skills/zaip-skill-meta-ads-setup/agents/openai.yaml +4 -0
  256. package/templates/.agents/skills/zaip-skill-meta-ads-setup/references/diagnostics.md +13 -0
  257. package/templates/.agents/skills/zaip-skill-meta-ads-setup/references/official-sources.md +10 -0
  258. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/SKILL.md +12 -0
  259. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/agents/openai.yaml +4 -0
  260. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/references/LICENSE +21 -0
  261. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/references/upstream.md +77 -0
  262. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/SKILL.md +12 -0
  263. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/agents/openai.yaml +4 -0
  264. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/references/LICENSE +21 -0
  265. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/references/upstream.md +126 -0
  266. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/SKILL.md +12 -0
  267. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/agents/openai.yaml +4 -0
  268. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/references/LICENSE +21 -0
  269. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/references/upstream.md +67 -0
  270. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/SKILL.md +12 -0
  271. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/agents/openai.yaml +4 -0
  272. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/references/LICENSE +21 -0
  273. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/references/upstream.md +69 -0
  274. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/SKILL.md +12 -0
  275. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/agents/openai.yaml +4 -0
  276. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/references/LICENSE +21 -0
  277. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/references/upstream.md +79 -0
  278. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/SKILL.md +14 -0
  279. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/agents/openai.yaml +4 -0
  280. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/references/LICENSE +21 -0
  281. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/references/upstream.md +144 -0
  282. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/SKILL.md +12 -0
  283. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/agents/openai.yaml +4 -0
  284. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/references/LICENSE +21 -0
  285. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/references/upstream.md +83 -0
  286. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/SKILL.md +10 -0
  287. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/agents/openai.yaml +4 -0
  288. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/references/LICENSE +21 -0
  289. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/references/setup.md +11 -0
  290. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/references/upstream.md +55 -0
  291. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/SKILL.md +12 -0
  292. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/agents/openai.yaml +4 -0
  293. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/references/LICENSE +21 -0
  294. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/references/upstream.md +56 -0
  295. package/templates/.agents/skills/zaip-skill-sales-methodology/SKILL.md +12 -7
  296. package/templates/.agents/skills/zaip-skill-sqlite-read-model/SKILL.md +11 -4
  297. package/templates/.agents/skills/zaip-skill-tool-setup/SKILL.md +6 -4
  298. package/templates/.agents/skills/zaip-skill-tool-setup/references/codex-config.md +4 -2
  299. package/templates/.agents/skills/zaip-skill-tool-setup/references/secrets.md +1 -0
  300. package/templates/.agents/tools/catalog.yaml +6 -0
  301. package/templates/.agents/tools/cloud-runner-dns.mjs +28 -0
  302. package/templates/.agents/tools/cloud-runner-terraform.mjs +212 -0
  303. package/templates/.agents/tools/cloud-runner.integration.test.mjs +239 -0
  304. package/templates/.agents/tools/cloud-runner.mjs +786 -0
  305. package/templates/.agents/tools/cloud-runner.test.mjs +534 -0
  306. package/templates/.agents/tools/cloud-terraform-plan-policy.mjs +262 -0
  307. package/templates/.agents/tools/mcp/development/meta-devtools/README.md +14 -0
  308. package/templates/.agents/tools/mcp/development/meta-devtools/manifest.yaml +28 -0
  309. package/templates/.agents/tools/mcp/infra/cloudflare/manifest.yaml +2 -3
  310. package/templates/.agents/tools/mcp/infra/hostinger/manifest.yaml +1 -3
  311. package/templates/.agents/tools/mcp/marketing/meta-ads/README.md +15 -13
  312. package/templates/.agents/tools/mcp/marketing/meta-ads/manifest.yaml +15 -10
  313. package/templates/.agents/tools/profiles/development.yaml +3 -0
  314. package/templates/infra/terraform/.terraform.lock.hcl +42 -0
  315. package/templates/infra/terraform/README.md +31 -0
  316. package/templates/infra/terraform/backend.tf.json +13 -0
  317. package/templates/infra/terraform/deployment-inputs.example.yaml +22 -0
  318. package/templates/infra/terraform/fixtures/approved-targets.json +32 -0
  319. package/templates/infra/terraform/fixtures/plan-approved.json +67 -0
  320. package/templates/infra/terraform/main.tf +77 -0
  321. package/templates/infra/terraform/outputs.tf +20 -0
  322. package/templates/infra/terraform/terraform.tfvars.example +26 -0
  323. package/templates/infra/terraform/variables.tf +91 -0
  324. package/templates/infra/terraform/versions.tf +18 -0
  325. package/templates/scripts/validate-cloud-hostinger-contract.mjs +89 -0
  326. package/templates/scripts/validate-cloud-terraform-plan.mjs +1 -0
  327. package/templates/template-manifest.json +613 -173
  328. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-B_Oj6Dlf.js +0 -3
  329. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-C09KmlSw.js +0 -68
  330. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-CblPpexd.js +0 -2
  331. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-hPPfVGQv.css +0 -1
  332. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-stale-seed.ts +0 -50
@@ -0,0 +1,111 @@
1
+ import { DatabaseSync } from "node:sqlite";
2
+ import { describe, expect, it } from "vitest";
3
+ import { agentProductivity, attachAgentProductivity } from "./sqlite-agent-productivity.js";
4
+
5
+ function fixture() {
6
+ const db = new DatabaseSync(":memory:");
7
+ db.exec(`CREATE TABLE zaip_aios_activities (payload_type TEXT,payload_json TEXT,run_id TEXT,zaip_agent TEXT,occurred_day_sp TEXT,phase TEXT,status TEXT,task_id TEXT,agent_invocation_id TEXT,zaip_agent_persona TEXT,zaip_agent_job TEXT);
8
+ INSERT INTO zaip_aios_activities(run_id,zaip_agent,occurred_day_sp) VALUES ('CREATE','dave','2026-09-01'),('CREATE','dave','2026-09-01'),('LATER','leo','2026-09-02'); UPDATE zaip_aios_activities SET task_id='T1' WHERE payload_type IS NULL;`);
9
+ const put = (type: string, value: object) =>
10
+ db
11
+ .prepare("INSERT INTO zaip_aios_activities(payload_type,payload_json) VALUES (?,?)")
12
+ .run(type, JSON.stringify(value));
13
+ put("work_item", {
14
+ entity_kind: "task",
15
+ id: "T1",
16
+ creation_run_id: "CREATE",
17
+ occurred_day_sp: "2026-09-01",
18
+ });
19
+ put("work_item", {
20
+ entity_kind: "prd",
21
+ id: "P1",
22
+ creation_run_id: "CREATE",
23
+ occurred_day_sp: "2026-09-01",
24
+ });
25
+ put("work_item", {
26
+ entity_kind: "spec",
27
+ id: "S1",
28
+ creation_run_id: "CREATE",
29
+ occurred_day_sp: "2026-09-01",
30
+ });
31
+ for (const path of ["a/artifacts/tests/one.md", "b/artifacts/tests/duplicate.md"])
32
+ put("file", {
33
+ path,
34
+ artifact_kind: "validation",
35
+ artifact_id: "TEST1",
36
+ artifact_run_id: "LATER",
37
+ artifact_occurred_day_sp: "2026-09-02",
38
+ });
39
+ put("file", {
40
+ path: "a/artifacts/validations/review.md",
41
+ artifact_kind: "validation",
42
+ artifact_id: "REVIEW",
43
+ artifact_run_id: "LATER",
44
+ artifact_occurred_day_sp: "2026-09-02",
45
+ });
46
+ put("file", {
47
+ path: "a/artifacts/evidences/e.md",
48
+ artifact_kind: "evidence",
49
+ artifact_id: "E1",
50
+ artifact_run_id: "LATER",
51
+ artifact_occurred_day_sp: "2026-09-02",
52
+ });
53
+ return db;
54
+ }
55
+
56
+ describe("agent productivity", () => {
57
+ it("counts a shared Task for each agent without duplicating repeated Runs by that agent", () => {
58
+ const db = fixture();
59
+ try {
60
+ const result = agentProductivity(db, {});
61
+ expect(result.get("dave")).toEqual({
62
+ prd_count: 1,
63
+ spec_count: 1,
64
+ task_count: 1,
65
+ test_count: 0,
66
+ evidence_count: 0,
67
+ });
68
+ expect(result.get("leo")).toEqual({
69
+ prd_count: 0,
70
+ spec_count: 0,
71
+ task_count: 1,
72
+ test_count: 1,
73
+ evidence_count: 1,
74
+ });
75
+ expect(agentProductivity(db, { from: "2026-09-02", to: "2026-09-02" }).has("dave")).toBe(
76
+ false,
77
+ );
78
+ expect(agentProductivity(db, { agent: "leo" }).get("leo")?.task_count).toBe(1);
79
+ } finally {
80
+ db.close();
81
+ }
82
+ });
83
+ it("counts participation without creation metadata and sums memberships in the full-population footer", () => {
84
+ const db = fixture();
85
+ try {
86
+ db.exec(
87
+ `UPDATE zaip_aios_activities SET payload_json=json_remove(payload_json,'$.creation_run_id') WHERE payload_type='work_item' AND json_extract(payload_json,'$.entity_kind')='task'`,
88
+ );
89
+ const page = {
90
+ rows: [{ key: "dave", task_count: 50, total_tokens: 12 }],
91
+ totals: { task_count: 50, total_tokens: 100 },
92
+ };
93
+ attachAgentProductivity(db, {}, page);
94
+ expect(page.rows[0]).toMatchObject({
95
+ task_count: 1,
96
+ tasks_active: 50,
97
+ prd_count: 1,
98
+ total_tokens: 12,
99
+ });
100
+ expect(page.totals).toMatchObject({
101
+ task_count: 2,
102
+ prd_count: 1,
103
+ test_count: 1,
104
+ evidence_count: 1,
105
+ total_tokens: 100,
106
+ });
107
+ } finally {
108
+ db.close();
109
+ }
110
+ });
111
+ });
@@ -0,0 +1,140 @@
1
+ import type { DatabaseSync } from "node:sqlite";
2
+ import type { DateOnlyCostPageInput } from "./sqlite-cost-agent-page.js";
3
+
4
+ const fields = ["prd_count", "spec_count", "task_count", "test_count", "evidence_count"] as const;
5
+ type Counts = Record<(typeof fields)[number], number | null>;
6
+ const empty = (): Counts => ({
7
+ prd_count: 0,
8
+ spec_count: 0,
9
+ task_count: 0,
10
+ test_count: 0,
11
+ evidence_count: 0,
12
+ });
13
+
14
+ /** Tasks count participation per agent; macro creation and artifacts keep their Run attribution. */
15
+ export function agentProductivity(database: DatabaseSync, input: DateOnlyCostPageInput) {
16
+ const clauses = ["a.payload_type IS NULL", "a.run_id IS NOT NULL", "a.zaip_agent IS NOT NULL"];
17
+ const values: string[] = [];
18
+ for (const bound of ["from", "to"] as const) {
19
+ if (!input[bound]) continue;
20
+ clauses.push(`a.occurred_day_sp ${bound === "from" ? ">=" : "<="} ?`);
21
+ values.push(input[bound].slice(0, 10));
22
+ }
23
+ for (const field of ["agent", "phase", "status"] as const) {
24
+ if (!input[field]) continue;
25
+ clauses.push(`a.${field === "agent" ? "zaip_agent" : field}=?`);
26
+ values.push(input[field]);
27
+ }
28
+ if (input.risk) {
29
+ clauses.push(
30
+ "EXISTS (SELECT 1 FROM zaip_aios_activities t WHERE t.payload_type='work_item' AND json_extract(t.payload_json,'$.entity_kind')='task' AND json_extract(t.payload_json,'$.id')=a.task_id AND json_extract(t.payload_json,'$.risk')=?)",
31
+ );
32
+ values.push(input.risk);
33
+ }
34
+ if (input.priority || input.workType) clauses.push("0");
35
+ if (input.origin)
36
+ clauses.push(
37
+ input.origin === "prd"
38
+ ? "a.prd_id IS NOT NULL"
39
+ : input.origin === "spec"
40
+ ? "a.prd_id IS NULL AND a.spec_id IS NOT NULL"
41
+ : input.origin === "task"
42
+ ? "a.prd_id IS NULL AND a.spec_id IS NULL AND a.task_id IS NOT NULL"
43
+ : "a.prd_id IS NULL AND a.spec_id IS NULL AND a.task_id IS NULL",
44
+ );
45
+ const usage: string[] = [];
46
+ for (const field of ["provider", "model"] as const) {
47
+ if (!input[field]) continue;
48
+ usage.push(`COALESCE(u.${field},'unavailable')=?`);
49
+ values.push(input[field]);
50
+ }
51
+ if (usage.length)
52
+ clauses.push(
53
+ `EXISTS (SELECT 1 FROM invocation_usage u WHERE u.agent_invocation_id=a.agent_invocation_id AND ${usage.join(" AND ")})`,
54
+ );
55
+ if (input.query) {
56
+ clauses.push("(a.zaip_agent LIKE ? OR a.zaip_agent_persona LIKE ? OR a.zaip_agent_job LIKE ?)");
57
+ values.push(...Array<string>(3).fill(`%${input.query}%`));
58
+ }
59
+ const owners = database
60
+ .prepare(
61
+ `SELECT a.run_id, MIN(a.zaip_agent) AS agent FROM zaip_aios_activities a WHERE ${clauses.join(" AND ")} GROUP BY a.run_id HAVING COUNT(DISTINCT a.zaip_agent)=1`,
62
+ )
63
+ .all(...values) as Array<{ run_id: string; agent: string }>;
64
+ const agentByRun = new Map(owners.map((row) => [row.run_id, row.agent]));
65
+ const counts = new Map<string, Counts>();
66
+ const seen = new Set<string>();
67
+ const from = input.from?.slice(0, 10) ?? "0000-01-01";
68
+ const to = input.to?.slice(0, 10) ?? "9999-12-31";
69
+ const facts = database
70
+ .prepare(`WITH files AS (
71
+ SELECT json_extract(payload_json,'$.path') path,
72
+ json_extract(payload_json,'$.artifact_kind') artifact_kind,
73
+ json_extract(payload_json,'$.artifact_id') artifact_id,
74
+ json_extract(payload_json,'$.artifact_run_id') artifact_run_id,
75
+ json_extract(payload_json,'$.artifact_occurred_day_sp') artifact_occurred_day_sp
76
+ FROM zaip_aios_activities WHERE payload_type='file'
77
+ )
78
+ SELECT json_extract(payload_json,'$.entity_kind') AS kind,
79
+ json_extract(payload_json,'$.id') id, json_extract(payload_json,'$.creation_run_id') AS run_id,
80
+ json_extract(payload_json,'$.occurred_day_sp') AS day
81
+ FROM zaip_aios_activities WHERE payload_type='work_item'
82
+ AND json_extract(payload_json,'$.entity_kind') IN ('prd','spec')
83
+ AND json_extract(payload_json,'$.creation_run_id') IS NOT NULL
84
+ UNION ALL
85
+ SELECT CASE WHEN artifact_kind='validation' THEN 'test' ELSE 'evidence' END,
86
+ COALESCE(artifact_id,path), artifact_run_id, artifact_occurred_day_sp
87
+ FROM files WHERE artifact_run_id IS NOT NULL AND
88
+ (artifact_kind='evidence' OR (artifact_kind='validation' AND path LIKE '%/artifacts/tests/%'))
89
+ `)
90
+ .all() as Array<{ kind: string; id: string; run_id: string; day: string }>;
91
+ for (const fact of facts) {
92
+ if (!fact.day || fact.day < from || fact.day > to) continue;
93
+ const owner = agentByRun.get(fact.run_id);
94
+ if (!owner) continue;
95
+ const key = `${fact.kind}:${fact.id}`;
96
+ if (seen.has(key)) continue;
97
+ seen.add(key);
98
+ const row = counts.get(owner) ?? empty();
99
+ const field = `${fact.kind}_count` as keyof Counts;
100
+ row[field] = (row[field] ?? 0) + 1;
101
+ counts.set(owner, row);
102
+ }
103
+ // Repeated Runs by one agent do not create extra Tasks. The same Task is
104
+ // counted once for EACH participating agent, so the footer sums memberships.
105
+ const taskCounts = database
106
+ .prepare(`SELECT a.zaip_agent AS agent,
107
+ COUNT(DISTINCT NULLIF(trim(a.task_id),'')) AS task_count
108
+ FROM zaip_aios_activities a WHERE ${clauses.join(" AND ")}
109
+ GROUP BY a.zaip_agent`)
110
+ .all(...values) as Array<{ agent: string; task_count: number }>;
111
+ for (const participation of taskCounts) {
112
+ const row = counts.get(participation.agent) ?? empty();
113
+ row.task_count = participation.task_count;
114
+ counts.set(participation.agent, row);
115
+ }
116
+ return counts;
117
+ }
118
+
119
+ export function attachAgentProductivity(
120
+ database: DatabaseSync,
121
+ input: DateOnlyCostPageInput,
122
+ page: { rows: Array<Record<string, unknown>>; totals?: Record<string, unknown> },
123
+ ) {
124
+ const counts = agentProductivity(database, input);
125
+ for (const row of page.rows) {
126
+ row.tasks_active = row.task_count;
127
+ Object.assign(row, counts.get(String(row.key)) ?? empty());
128
+ row.validation_count = row.test_count;
129
+ }
130
+ if (page.totals) {
131
+ const totals = empty();
132
+ for (const row of counts.values())
133
+ for (const field of fields)
134
+ totals[field] =
135
+ totals[field] === null || row[field] === null ? null : totals[field] + row[field];
136
+ Object.assign(page.totals, totals, {
137
+ validation_count: totals.test_count,
138
+ });
139
+ }
140
+ }
@@ -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
  }