@zaip-ai/cli 1.3.8 → 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (317) 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 +208 -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-runtime-selection.mjs +36 -0
  181. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-runtime-selection.test.mjs +63 -0
  182. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-server.mjs +142 -0
  183. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.mjs +262 -267
  184. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.test.mjs +18 -29
  185. package/templates/.agents/dashboard/packages/windows-launcher/Program.cs +6 -0
  186. package/templates/.agents/dashboard/zaip-dashboard-windows.exe +0 -0
  187. package/templates/.agents/requirements/autonomous-assurance.md +1 -1
  188. package/templates/.agents/requirements/cloud-hostinger-autonomy.md +47 -0
  189. package/templates/.agents/requirements/comparative-audit.md +1 -1
  190. package/templates/.agents/requirements/context-engine.md +1 -1
  191. package/templates/.agents/requirements/cost-observability.md +1 -1
  192. package/templates/.agents/requirements/dashboard-launch.md +2 -2
  193. package/templates/.agents/requirements/data-export.md +1 -1
  194. package/templates/.agents/requirements/design-system.md +1 -1
  195. package/templates/.agents/requirements/execution-engine.md +1 -1
  196. package/templates/.agents/requirements/kanban-dashboard.md +1 -1
  197. package/templates/.agents/requirements/model-orchestration.md +1 -1
  198. package/templates/.agents/requirements/public-distribution.md +1 -1
  199. package/templates/.agents/requirements/software-architecture.md +1 -1
  200. package/templates/.agents/requirements/strategic-improvements.md +1 -1
  201. package/templates/.agents/requirements/work-lifecycle.md +1 -1
  202. package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
  203. package/templates/.agents/rules/autonomy-decision.md +1 -1
  204. package/templates/.agents/rules/cloud-hostinger-autonomy.md +21 -0
  205. package/templates/.agents/rules/core.md +5 -4
  206. package/templates/.agents/rules/local-sqlite-storage.md +18 -0
  207. package/templates/.agents/rules/meta-mcp.md +21 -0
  208. package/templates/.agents/rules/workflow.md +1 -0
  209. package/templates/.agents/runtime/bin/zaip.mjs +305 -137
  210. package/templates/.agents/runtime/checksums.sha256 +2 -2
  211. package/templates/.agents/runtime/provenance.json +2 -2
  212. package/templates/.agents/skills/zaip-skill-api-to-mcp/SKILL.md +4 -0
  213. package/templates/.agents/skills/zaip-skill-brand-governance/SKILL.md +25 -9
  214. package/templates/.agents/skills/zaip-skill-brand-governance/references/brandbook-framework.md +2 -2
  215. package/templates/.agents/skills/zaip-skill-brand-governance/references/brandbook-standards.md +2 -2
  216. package/templates/.agents/skills/zaip-skill-change/references/observable-evidence.md +15 -0
  217. package/templates/.agents/skills/zaip-skill-dashboard-sqlite/SKILL.md +13 -5
  218. package/templates/.agents/skills/zaip-skill-data-analyst/SKILL.md +10 -5
  219. package/templates/.agents/skills/zaip-skill-execution/SKILL.md +5 -4
  220. package/templates/.agents/skills/zaip-skill-frontend-launcher/SKILL.md +4 -3
  221. package/templates/.agents/skills/zaip-skill-frontend-launcher/references/launcher-contract.md +5 -3
  222. package/templates/.agents/skills/zaip-skill-github-deploy/SKILL.md +5 -5
  223. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/preflight.mjs +78 -13
  224. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/preflight.test.mjs +59 -0
  225. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/yaml-parser.mjs +157 -0
  226. package/templates/.agents/skills/zaip-skill-growth-strategy-revops/SKILL.md +11 -6
  227. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/SKILL.md +17 -0
  228. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/agents/openai.yaml +7 -0
  229. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/references/autonomous-cloud-inputs.example.json +41 -0
  230. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/references/autonomous-runner.md +84 -0
  231. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/references/operational-contract.md +19 -0
  232. package/templates/.agents/skills/zaip-skill-local-credentials/SKILL.md +43 -0
  233. package/templates/.agents/skills/zaip-skill-local-credentials/agents/openai.yaml +6 -0
  234. package/templates/.agents/skills/zaip-skill-local-credentials/references/dotenv-resolution.md +47 -0
  235. package/templates/.agents/skills/zaip-skill-marketing/SKILL.md +8 -3
  236. package/templates/.agents/skills/zaip-skill-meta-ads/SKILL.md +16 -0
  237. package/templates/.agents/skills/zaip-skill-meta-ads/agents/openai.yaml +4 -0
  238. package/templates/.agents/skills/zaip-skill-meta-ads/references/setup.md +3 -0
  239. package/templates/.agents/skills/zaip-skill-meta-ads-setup/SKILL.md +24 -0
  240. package/templates/.agents/skills/zaip-skill-meta-ads-setup/agents/openai.yaml +4 -0
  241. package/templates/.agents/skills/zaip-skill-meta-ads-setup/references/diagnostics.md +13 -0
  242. package/templates/.agents/skills/zaip-skill-meta-ads-setup/references/official-sources.md +10 -0
  243. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/SKILL.md +12 -0
  244. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/agents/openai.yaml +4 -0
  245. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/references/LICENSE +21 -0
  246. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/references/upstream.md +77 -0
  247. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/SKILL.md +12 -0
  248. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/agents/openai.yaml +4 -0
  249. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/references/LICENSE +21 -0
  250. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/references/upstream.md +126 -0
  251. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/SKILL.md +12 -0
  252. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/agents/openai.yaml +4 -0
  253. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/references/LICENSE +21 -0
  254. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/references/upstream.md +67 -0
  255. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/SKILL.md +12 -0
  256. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/agents/openai.yaml +4 -0
  257. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/references/LICENSE +21 -0
  258. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/references/upstream.md +69 -0
  259. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/SKILL.md +12 -0
  260. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/agents/openai.yaml +4 -0
  261. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/references/LICENSE +21 -0
  262. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/references/upstream.md +79 -0
  263. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/SKILL.md +14 -0
  264. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/agents/openai.yaml +4 -0
  265. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/references/LICENSE +21 -0
  266. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/references/upstream.md +144 -0
  267. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/SKILL.md +12 -0
  268. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/agents/openai.yaml +4 -0
  269. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/references/LICENSE +21 -0
  270. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/references/upstream.md +83 -0
  271. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/SKILL.md +10 -0
  272. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/agents/openai.yaml +4 -0
  273. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/references/LICENSE +21 -0
  274. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/references/setup.md +11 -0
  275. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/references/upstream.md +55 -0
  276. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/SKILL.md +12 -0
  277. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/agents/openai.yaml +4 -0
  278. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/references/LICENSE +21 -0
  279. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/references/upstream.md +56 -0
  280. package/templates/.agents/skills/zaip-skill-sales-methodology/SKILL.md +12 -7
  281. package/templates/.agents/skills/zaip-skill-sqlite-read-model/SKILL.md +11 -4
  282. package/templates/.agents/skills/zaip-skill-tool-setup/SKILL.md +6 -4
  283. package/templates/.agents/skills/zaip-skill-tool-setup/references/codex-config.md +4 -2
  284. package/templates/.agents/skills/zaip-skill-tool-setup/references/secrets.md +1 -0
  285. package/templates/.agents/tools/catalog.yaml +6 -0
  286. package/templates/.agents/tools/cloud-runner-dns.mjs +28 -0
  287. package/templates/.agents/tools/cloud-runner-terraform.mjs +212 -0
  288. package/templates/.agents/tools/cloud-runner.integration.test.mjs +239 -0
  289. package/templates/.agents/tools/cloud-runner.mjs +786 -0
  290. package/templates/.agents/tools/cloud-runner.test.mjs +534 -0
  291. package/templates/.agents/tools/cloud-terraform-plan-policy.mjs +262 -0
  292. package/templates/.agents/tools/mcp/development/meta-devtools/README.md +14 -0
  293. package/templates/.agents/tools/mcp/development/meta-devtools/manifest.yaml +28 -0
  294. package/templates/.agents/tools/mcp/infra/cloudflare/manifest.yaml +2 -3
  295. package/templates/.agents/tools/mcp/infra/hostinger/manifest.yaml +1 -3
  296. package/templates/.agents/tools/mcp/marketing/meta-ads/README.md +15 -13
  297. package/templates/.agents/tools/mcp/marketing/meta-ads/manifest.yaml +15 -10
  298. package/templates/.agents/tools/profiles/development.yaml +3 -0
  299. package/templates/infra/terraform/.terraform.lock.hcl +42 -0
  300. package/templates/infra/terraform/README.md +31 -0
  301. package/templates/infra/terraform/backend.tf.json +13 -0
  302. package/templates/infra/terraform/deployment-inputs.example.yaml +22 -0
  303. package/templates/infra/terraform/fixtures/approved-targets.json +32 -0
  304. package/templates/infra/terraform/fixtures/plan-approved.json +67 -0
  305. package/templates/infra/terraform/main.tf +77 -0
  306. package/templates/infra/terraform/outputs.tf +20 -0
  307. package/templates/infra/terraform/terraform.tfvars.example +26 -0
  308. package/templates/infra/terraform/variables.tf +91 -0
  309. package/templates/infra/terraform/versions.tf +18 -0
  310. package/templates/scripts/validate-cloud-hostinger-contract.mjs +89 -0
  311. package/templates/scripts/validate-cloud-terraform-plan.mjs +1 -0
  312. package/templates/template-manifest.json +607 -167
  313. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-BwnJpl02.js +0 -3
  314. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-C09KmlSw.js +0 -68
  315. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-Dyo_wpL4.js +0 -2
  316. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-hPPfVGQv.css +0 -1
  317. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-stale-seed.ts +0 -50
@@ -0,0 +1,793 @@
1
+ import {
2
+ consolidatedValue,
3
+ frontMatterRecord,
4
+ recordValue,
5
+ stringValue,
6
+ stringValues,
7
+ } from "./sqlite-read-model-canonical-parsing.js";
8
+
9
+ export {
10
+ consolidatedValue,
11
+ frontMatterRecord,
12
+ recordValue,
13
+ stringValue,
14
+ stringValues,
15
+ } from "./sqlite-read-model-canonical-parsing.js";
16
+
17
+ import {
18
+ estimateUsageCost,
19
+ type FxRate,
20
+ fxRateSchema,
21
+ inferProvider,
22
+ type PersistedTokenUsage,
23
+ type PricingCatalog,
24
+ } from "@zaip-ai/costs";
25
+ import type { RunV2Fold, UsageRecordedV2 } from "../../../../../packages/decisions/src/index.js";
26
+
27
+ const MAX_RECONCILED_RUN_INTERVAL_MS = 2 * 3_600_000;
28
+
29
+ export function saoPauloCurrentMonth(): { from: string; to: string } {
30
+ const parts = new Intl.DateTimeFormat("en-CA", {
31
+ timeZone: "America/Sao_Paulo",
32
+ year: "numeric",
33
+ month: "2-digit",
34
+ day: "2-digit",
35
+ }).formatToParts(new Date());
36
+ const value = Object.fromEntries(parts.map((part) => [part.type, part.value]));
37
+ const day = `${value.year}-${value.month}-${value.day}`;
38
+ return { from: `${value.year}-${value.month}-01`, to: day };
39
+ }
40
+
41
+ /** Only canonical inputs consumed by the fact or its direct dimensions. */
42
+
43
+ export type AgentProfile = {
44
+ catalogId: string;
45
+ persona: string | null;
46
+ job: string | null;
47
+ role: string | null;
48
+ };
49
+ export type CanonicalEntity = {
50
+ kind: string;
51
+ id: string;
52
+ title: string | null;
53
+ status: string | null;
54
+ stage: string | null;
55
+ risk: string | null;
56
+ specId: string | null;
57
+ changeId: string | null;
58
+ occurredAt: string | null;
59
+ creationRunId: string | null;
60
+ completedRunId: string | null;
61
+ };
62
+ export type CanonicalArtifact = {
63
+ kind: "evidence" | "validation";
64
+ id: string;
65
+ occurredAt: string;
66
+ occurredDaySp: string;
67
+ taskId: string | null;
68
+ changeId: string | null;
69
+ runId: string | null;
70
+ };
71
+ export type CanonicalExecution = {
72
+ id: string;
73
+ status: string | null;
74
+ kind: string | null;
75
+ originType: string | null;
76
+ originId: string | null;
77
+ createdAt: string;
78
+ finishedAt: string | null;
79
+ sourcePath: string;
80
+ };
81
+ export type CanonicalChange = {
82
+ id: string;
83
+ title: string;
84
+ status: string;
85
+ risk: string;
86
+ sourceType: string;
87
+ sourceId: string;
88
+ sourceSpecId: string | null;
89
+ sourcePrdId: string | null;
90
+ requirementIds: string[];
91
+ acceptanceIds: string[];
92
+ taskIds: string[];
93
+ result: string | null;
94
+ createdAt: string;
95
+ sourcePath: string;
96
+ };
97
+ export type CanonicalDecision = {
98
+ id: string;
99
+ title: string;
100
+ origin: "human" | "agent";
101
+ scopeType: "change" | "execution";
102
+ scopeId: string;
103
+ createdAt: string;
104
+ sourcePath: string;
105
+ };
106
+ export function readCanonicalLifecycleRecords(rows: Array<{ path: string; content: string }>): {
107
+ executions: CanonicalExecution[];
108
+ changes: CanonicalChange[];
109
+ decisions: CanonicalDecision[];
110
+ } {
111
+ const executions: CanonicalExecution[] = [];
112
+ const changes: CanonicalChange[] = [];
113
+ const decisions: CanonicalDecision[] = [];
114
+ for (const row of rows) {
115
+ const metadata = frontMatterRecord(row.content);
116
+ if (metadata === null) continue;
117
+ const id = stringValue(metadata.id);
118
+ const createdAt = stringValue(metadata.created_at);
119
+ if (id === null || createdAt === null || Number.isNaN(Date.parse(createdAt))) continue;
120
+ if (/^\.agents\/records\/executions\/.*\/execution\.md$/.test(row.path)) {
121
+ const origin = recordValue(metadata.origin);
122
+ const originType = stringValue(origin?.type);
123
+ const originId =
124
+ originType === "interaction"
125
+ ? stringValue(origin?.conversation_id)
126
+ : originType === "task"
127
+ ? stringValue(origin?.task_id)
128
+ : originType === "spec"
129
+ ? stringValue(origin?.spec_id)
130
+ : originType === "prd"
131
+ ? stringValue(origin?.prd_id)
132
+ : null;
133
+ executions.push({
134
+ id,
135
+ status: stringValue(metadata.status),
136
+ kind: stringValue(metadata.kind),
137
+ originType,
138
+ originId,
139
+ createdAt,
140
+ finishedAt: stringValue(metadata.finished_at),
141
+ sourcePath: row.path,
142
+ });
143
+ continue;
144
+ }
145
+ if (/^\.agents\/records\/changes\/.*\/change\.md$/.test(row.path)) {
146
+ const source = recordValue(metadata.source);
147
+ const sourceType = stringValue(source?.type);
148
+ const sourceId =
149
+ sourceType === "task"
150
+ ? stringValue(source?.task_id)
151
+ : sourceType === "spec"
152
+ ? stringValue(source?.spec_id)
153
+ : sourceType === "prd"
154
+ ? stringValue(source?.prd_id)
155
+ : null;
156
+ if (sourceType === null || sourceId === null) continue;
157
+ changes.push({
158
+ id,
159
+ title: stringValue(metadata.title) ?? id,
160
+ status: stringValue(metadata.status) ?? "unclassified",
161
+ risk: stringValue(metadata.risk) ?? "unclassified",
162
+ sourceType,
163
+ sourceId,
164
+ // A Change sourced from a PRD can still name its selected SPEC. Keep
165
+ // both canonical lineage links: choosing one by source.type loses the
166
+ // only evidence that a planned Task belongs to that SPEC.
167
+ sourceSpecId: stringValue(source?.spec_id),
168
+ sourcePrdId: stringValue(source?.prd_id),
169
+ requirementIds: stringValues(metadata.requirement_ids),
170
+ acceptanceIds: stringValues(metadata.acceptance_ids),
171
+ taskIds: stringValues(metadata.task_ids),
172
+ result: stringValue(metadata.result),
173
+ createdAt,
174
+ sourcePath: row.path,
175
+ });
176
+ continue;
177
+ }
178
+ if (/^\.agents\/records\/(changes|executions)\/.*\/decisions\/.*\.md$/.test(row.path)) {
179
+ const origin = metadata.origin;
180
+ const scopeType = metadata.scope_type;
181
+ const scopeId = stringValue(metadata.scope_id);
182
+ if (
183
+ (origin !== "human" && origin !== "agent") ||
184
+ (scopeType !== "change" && scopeType !== "execution") ||
185
+ scopeId === null
186
+ )
187
+ continue;
188
+ decisions.push({
189
+ id,
190
+ title: stringValue(metadata.title) ?? id,
191
+ origin,
192
+ scopeType,
193
+ scopeId,
194
+ createdAt,
195
+ sourcePath: row.path,
196
+ });
197
+ }
198
+ }
199
+ return { executions, changes, decisions };
200
+ }
201
+
202
+ /** Reads only scalar front matter from canonical records to enrich linked activities. */
203
+ export function readCanonicalEntities(
204
+ rows: Array<{ path: string; content: string }>,
205
+ ): Map<string, CanonicalEntity> {
206
+ const entities = new Map<string, CanonicalEntity>();
207
+ for (const row of rows) {
208
+ const match = /^\.agents\/records\/(tasks|specs|prds|changes|executions|conversations)\//.exec(
209
+ row.path,
210
+ );
211
+ if (match === null) continue;
212
+ const frontMatter = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(row.content)?.[1];
213
+ if (frontMatter === undefined) continue;
214
+ const scalar = (key: string): string | null => {
215
+ const value = new RegExp(`^${key}:\\s*(.+?)\\s*$`, "m").exec(frontMatter)?.[1]?.trim();
216
+ if (value === undefined || value === "null") return null;
217
+ return value.replace(/^['"]|['"]$/g, "");
218
+ };
219
+ const id = scalar("id");
220
+ if (id === null) continue;
221
+ const kind = match[1]?.slice(0, -1);
222
+ if (kind === undefined) continue;
223
+ entities.set(`${kind}:${id}`, {
224
+ kind,
225
+ id,
226
+ title: scalar("title"),
227
+ status: scalar("status"),
228
+ stage: scalar("stage"),
229
+ risk: scalar("risk"),
230
+ specId: scalar("spec_id"),
231
+ changeId: scalar("change_id"),
232
+ occurredAt: scalar("created_at") ?? scalar("started_at") ?? scalar("recorded_at"),
233
+ creationRunId: scalar("creation_run_id"),
234
+ completedRunId: scalar("completed_run_id"),
235
+ });
236
+ }
237
+ return entities;
238
+ }
239
+
240
+ /** Catalog data is snapshotted into the fact so historical labels never change after a catalog edit. */
241
+ export function readAgentProfiles(
242
+ catalog: string | undefined,
243
+ clients: string | undefined,
244
+ ): Map<string, AgentProfile> {
245
+ const profiles = new Map<string, AgentProfile>();
246
+ for (const block of (catalog ?? "").split(/^ {2}- id: /m).slice(1)) {
247
+ const catalogId = block.slice(0, block.indexOf("\n")).trim();
248
+ const agent = /\bcodex_name:\s*([^\s#]+)/.exec(block)?.[1] ?? null;
249
+ if (agent === null) continue;
250
+ const persona = /persona:\s*\{\s*nome:\s*([^,}\s]+),\s*cargo:\s*([^,}\s]+)\s*\}/.exec(block);
251
+ const role = /\brole:\s*([^\s#]+)/.exec(block)?.[1] ?? null;
252
+ const profile = {
253
+ catalogId,
254
+ persona: persona?.[1] ?? null,
255
+ job: persona?.[2] ?? null,
256
+ role,
257
+ };
258
+ // Runs before canonicalization used catalog IDs directly, while older
259
+ // host records used `zaip-` forms of deprecated IDs. All of these are
260
+ // explicit identities from the catalog; no UI fallback is involved.
261
+ const identities = new Set([agent, catalogId]);
262
+ for (const alias of block.matchAll(
263
+ /^ {8}- kind:\s*(?:id|codex_name|codex_task_name)\s*\r?\n {10}value:\s*([^\s#]+)/gm,
264
+ )) {
265
+ const value = alias[1];
266
+ if (value !== undefined) identities.add(value);
267
+ }
268
+ for (const identity of [...identities]) {
269
+ if (identity.startsWith("agent-")) identities.add(`zaip-${identity}`);
270
+ }
271
+ for (const identity of identities) profiles.set(identity, profile);
272
+ }
273
+ // `codex` is the host client identity, not a delegable Agent Catalog
274
+ // member. Its canonical client profile makes the host classification
275
+ // explicit, while keeping it separate from ZAIP subagent routing.
276
+ if (/^ {2}- id:\s*codex\s*$/m.test(clients ?? "")) {
277
+ profiles.set("codex", {
278
+ catalogId: "client:codex",
279
+ persona: "codex",
280
+ job: "client_host",
281
+ role: "host",
282
+ });
283
+ }
284
+ return profiles;
285
+ }
286
+
287
+ export function activityKind(value: {
288
+ execution_id: string | null;
289
+ task_id: string | null;
290
+ spec_id: string | null;
291
+ prd_id: string | null;
292
+ phase: string | null;
293
+ purpose: string | null;
294
+ }): "conversation" | "planning" | "development" {
295
+ if (value.prd_id !== null && value.execution_id === null) return "planning";
296
+ if (value.spec_id !== null && value.execution_id === null) return "planning";
297
+ if (value.phase === "planning" || value.purpose === "planning") return "planning";
298
+ if (
299
+ value.execution_id !== null ||
300
+ value.task_id !== null ||
301
+ value.phase === "execution" ||
302
+ value.purpose === "implementation"
303
+ )
304
+ return "development";
305
+ return "conversation";
306
+ }
307
+
308
+ export type ActivityMetric = number | null;
309
+
310
+ export function knownSum(
311
+ records: readonly UsageRecordedV2[],
312
+ key: keyof PersistedTokenUsage,
313
+ ): ActivityMetric {
314
+ const values = records
315
+ .map((record) => record.tokens[key])
316
+ .filter((value): value is number => typeof value === "number");
317
+ return values.length === 0 ? null : values.reduce((sum, value) => sum + value, 0);
318
+ }
319
+
320
+ export function canonicalProvider(
321
+ value: string | null | undefined,
322
+ model: string | null,
323
+ client: string,
324
+ ): "openai" | "anthropic" | null {
325
+ if (value === "openai" || value === "anthropic") return value;
326
+ // The canonical sentinel denotes an absent source attribute. It must not be
327
+ // re-inferred from the dashboard client as a known provider.
328
+ if (value === "unavailable" || model === "unavailable") return null;
329
+ return model === null ? null : inferProvider(model, client, "");
330
+ }
331
+
332
+ export function factualDuration(record: UsageRecordedV2): boolean {
333
+ return (
334
+ typeof record.duration_ms === "number" &&
335
+ (record.duration_provenance === "observed" ||
336
+ record.duration_provenance === "reconciled" ||
337
+ record.duration_provenance === "legacy_inferred")
338
+ );
339
+ }
340
+
341
+ export type ResolvedSourceInterval = { start: string; end: string; duration: number };
342
+
343
+ /**
344
+ * Reconciled usage is the closest canonical observation of when model work
345
+ * happened. It wins over Run finalization elapsed time, which may include
346
+ * orchestration/capture lag or be recorded much later.
347
+ */
348
+ export function resolvedSourceInterval(
349
+ records: readonly UsageRecordedV2[],
350
+ ): ResolvedSourceInterval | null {
351
+ const intervals = records.flatMap((record) => {
352
+ const start = record.source_started_at;
353
+ const end = record.source_finished_at;
354
+ const startMs = typeof start === "string" ? Date.parse(start) : Number.NaN;
355
+ const endMs = typeof end === "string" ? Date.parse(end) : Number.NaN;
356
+ return typeof start === "string" &&
357
+ typeof end === "string" &&
358
+ Number.isFinite(startMs) &&
359
+ Number.isFinite(endMs) &&
360
+ endMs > startMs &&
361
+ endMs - startMs <= MAX_RECONCILED_RUN_INTERVAL_MS
362
+ ? [{ start, end, startMs, endMs }]
363
+ : [];
364
+ });
365
+ if (intervals.length === 0) return null;
366
+ const start = intervals.reduce((earliest, interval) =>
367
+ interval.startMs < earliest.startMs ? interval : earliest,
368
+ );
369
+ const end = intervals.reduce((latest, interval) =>
370
+ interval.endMs > latest.endMs ? interval : latest,
371
+ );
372
+ return end.endMs - start.startMs <= MAX_RECONCILED_RUN_INTERVAL_MS
373
+ ? { start: start.start, end: end.end, duration: end.endMs - start.startMs }
374
+ : null;
375
+ }
376
+
377
+ export function resolveDurations(
378
+ records: readonly UsageRecordedV2[],
379
+ fold: RunV2Fold,
380
+ ): {
381
+ source: number | null;
382
+ effective: number | null;
383
+ provenance: string | null;
384
+ sourceStartedAt: string | null;
385
+ sourceFinishedAt: string | null;
386
+ } {
387
+ const sourceInterval = resolvedSourceInterval(records);
388
+ if (sourceInterval !== null) {
389
+ return {
390
+ source: sourceInterval.duration,
391
+ effective: sourceInterval.duration,
392
+ provenance: "reconciled",
393
+ sourceStartedAt: sourceInterval.start,
394
+ sourceFinishedAt: sourceInterval.end,
395
+ };
396
+ }
397
+ if (
398
+ typeof fold.finished?.duration_ms === "number" &&
399
+ fold.finished.duration_provenance !== "estimated" &&
400
+ fold.finished.duration_provenance !== "unavailable"
401
+ ) {
402
+ return {
403
+ source: fold.finished.duration_ms,
404
+ effective: fold.finished.duration_ms,
405
+ provenance: fold.finished.duration_provenance ?? "observed",
406
+ sourceStartedAt: null,
407
+ sourceFinishedAt: null,
408
+ };
409
+ }
410
+ const factual = records.filter(factualDuration);
411
+ if (factual.length > 0) {
412
+ const source = factual.reduce((sum, record) => sum + (record.duration_ms ?? 0), 0);
413
+ const provenance =
414
+ consolidatedValue(factual.map((record) => record.duration_provenance)) ?? "observed";
415
+ return { source, effective: source, provenance, sourceStartedAt: null, sourceFinishedAt: null };
416
+ }
417
+ // A canonical Run pair can supply elapsed time when terminal records predate
418
+ // the explicit duration field. Historical finalizers can however close a
419
+ // Run days later, so only a bounded interval is trusted as reconciliation.
420
+ const startedAt = Date.parse(fold.run.occurred_at);
421
+ const finishedAt =
422
+ typeof fold.finished?.occurred_at === "string"
423
+ ? Date.parse(fold.finished.occurred_at)
424
+ : Number.NaN;
425
+ if (
426
+ Number.isFinite(startedAt) &&
427
+ Number.isFinite(finishedAt) &&
428
+ finishedAt > startedAt &&
429
+ finishedAt - startedAt <= MAX_RECONCILED_RUN_INTERVAL_MS
430
+ ) {
431
+ return {
432
+ source: null,
433
+ effective: finishedAt - startedAt,
434
+ provenance: "reconciled",
435
+ sourceStartedAt: null,
436
+ sourceFinishedAt: null,
437
+ };
438
+ }
439
+ // Tokens and lifecycle timestamps are not a time source. Absence stays
440
+ // absent: consumers must display unavailable rather than manufacture time.
441
+ return {
442
+ source: null,
443
+ effective: null,
444
+ provenance: null,
445
+ sourceStartedAt: null,
446
+ sourceFinishedAt: null,
447
+ };
448
+ }
449
+
450
+ export function latestFxRate(rows: Array<{ path: string; content: string }>): FxRate | null {
451
+ return (
452
+ rows
453
+ .flatMap((row) => {
454
+ try {
455
+ return [fxRateSchema.parse(JSON.parse(row.content))];
456
+ } catch {
457
+ return [];
458
+ }
459
+ })
460
+ .sort((left, right) => right.as_of.localeCompare(left.as_of))[0] ?? null
461
+ );
462
+ }
463
+
464
+ export function aggregateCost(
465
+ records: readonly UsageRecordedV2[],
466
+ fold: RunV2Fold,
467
+ catalog: PricingCatalog,
468
+ fxRate: FxRate | null,
469
+ ) {
470
+ let usd = 0;
471
+ let brl = 0;
472
+ let knownUsd = false;
473
+ let knownBrl = false;
474
+ let estimated = false;
475
+ const pricingModels: string[] = [];
476
+ const pricingSources: string[] = [];
477
+ for (const record of records) {
478
+ if (typeof record.cost.usd === "number") {
479
+ usd += record.cost.usd;
480
+ knownUsd = true;
481
+ }
482
+ if (typeof record.cost.brl === "number") {
483
+ brl += record.cost.brl;
484
+ knownBrl = true;
485
+ }
486
+ if (typeof record.cost.usd === "number" && typeof record.cost.brl === "number") continue;
487
+ if (record.tokens.total === null) continue;
488
+ if (record.tokens.total === 0) {
489
+ knownUsd = true;
490
+ knownBrl = true;
491
+ continue;
492
+ }
493
+ const model = record.model ?? fold.run.model;
494
+ if (model === null || model === "unavailable") continue;
495
+ const priced = estimateUsageCost({
496
+ provider: canonicalProvider(record.provider, model, fold.run.client),
497
+ model,
498
+ usage: record.tokens,
499
+ catalog,
500
+ fxRate,
501
+ serviceTier: record.runtime?.service_tier ?? null,
502
+ });
503
+ if (typeof record.cost.usd !== "number" && priced.estimated_usd !== null) {
504
+ usd += priced.estimated_usd;
505
+ knownUsd = true;
506
+ estimated = true;
507
+ }
508
+ if (typeof record.cost.brl !== "number" && priced.estimated_brl !== null) {
509
+ brl += priced.estimated_brl;
510
+ knownBrl = true;
511
+ estimated = true;
512
+ }
513
+ if (priced.pricing_model !== null) pricingModels.push(priced.pricing_model);
514
+ if (priced.pricing_source !== null) pricingSources.push(priced.pricing_source);
515
+ }
516
+ const totalTokens = knownSum(records, "total");
517
+ return {
518
+ usd: knownUsd ? Math.round(usd * 1_000_000) / 1_000_000 : null,
519
+ brl: knownBrl ? Math.round(brl * 1_000_000) / 1_000_000 : null,
520
+ method:
521
+ totalTokens === 0
522
+ ? "zero_no_llm_usage"
523
+ : estimated
524
+ ? "token_pricing_estimate"
525
+ : knownUsd || knownBrl
526
+ ? "canonical_recorded"
527
+ : null,
528
+ pricingModel: consolidatedValue(pricingModels),
529
+ pricingSource: consolidatedValue(pricingSources),
530
+ fxRate: knownBrl ? (fxRate?.rate ?? null) : null,
531
+ };
532
+ }
533
+
534
+ export function pricingModelForUsage(catalog: PricingCatalog, model: string | null) {
535
+ if (model === null) return null;
536
+ const normalizedModel = model.toLowerCase();
537
+ const matches = (identity: string) => {
538
+ const normalizedIdentity = identity.toLowerCase();
539
+ if (normalizedModel === normalizedIdentity) return true;
540
+ const suffix = normalizedModel.slice(normalizedIdentity.length);
541
+ return (
542
+ suffix.startsWith("@") ||
543
+ suffix.startsWith(":") ||
544
+ suffix.startsWith("/") ||
545
+ /^-\d{4}-\d{2}-\d{2}(?:$|[.:@/])/.test(suffix)
546
+ );
547
+ };
548
+ return (
549
+ catalog.models.find(
550
+ (candidate) => matches(candidate.id) || candidate.aliases.some((alias) => matches(alias)),
551
+ ) ?? null
552
+ );
553
+ }
554
+
555
+ /** Pricing columns needed to reproduce the Guide calculation at invocation grain. */
556
+ export function usagePricingAudit(
557
+ record: UsageRecordedV2,
558
+ fold: RunV2Fold,
559
+ catalog: PricingCatalog,
560
+ fxRate: FxRate | null,
561
+ ) {
562
+ const model = record.model ?? fold.run.model;
563
+ const provider = canonicalProvider(record.provider, model, fold.run.client);
564
+ const serviceTier = record.runtime?.service_tier ?? null;
565
+ const pricingModel = pricingModelForUsage(catalog, model);
566
+ const actual =
567
+ model === null
568
+ ? null
569
+ : estimateUsageCost({
570
+ provider,
571
+ model,
572
+ usage: record.tokens,
573
+ catalog,
574
+ fxRate,
575
+ serviceTier,
576
+ });
577
+ const standard =
578
+ model === null
579
+ ? null
580
+ : estimateUsageCost({
581
+ provider,
582
+ model,
583
+ usage: record.tokens,
584
+ catalog,
585
+ fxRate,
586
+ serviceTier: "default",
587
+ });
588
+ const longContext =
589
+ pricingModel?.long_context_threshold_tokens !== null &&
590
+ pricingModel?.long_context_threshold_tokens !== undefined &&
591
+ (record.tokens.input ?? 0) > pricingModel.long_context_threshold_tokens;
592
+ const priority = serviceTier === "priority" || serviceTier === "fast";
593
+ const priorityAvailable = priority && !longContext;
594
+ const inputRate = pricingModel
595
+ ? priorityAvailable
596
+ ? (pricingModel.priority_input_per_million_usd ?? null)
597
+ : pricingModel.input_per_million_usd
598
+ : null;
599
+ const cachedRate = pricingModel
600
+ ? priorityAvailable
601
+ ? (pricingModel.priority_cached_input_per_million_usd ?? null)
602
+ : pricingModel.cached_input_per_million_usd
603
+ : null;
604
+ const outputRate = pricingModel
605
+ ? priorityAvailable
606
+ ? (pricingModel.priority_output_per_million_usd ?? null)
607
+ : pricingModel.output_per_million_usd
608
+ : null;
609
+ const reasoningRate = pricingModel
610
+ ? priorityAvailable
611
+ ? (pricingModel.priority_reasoning_per_million_usd ??
612
+ pricingModel.priority_output_per_million_usd ??
613
+ null)
614
+ : (pricingModel.reasoning_per_million_usd ?? pricingModel.output_per_million_usd)
615
+ : null;
616
+ // Priority + long-context has no published combined tariff. The Guide uses
617
+ // the published Standard long-context scenario in that case, matching the
618
+ // canonical historical estimate without inventing a price table.
619
+ const priced = actual?.estimated_usd !== null && actual !== null ? actual : standard;
620
+ const calculatedUsd = priced?.estimated_usd ?? null;
621
+ const standardUsd = standard?.estimated_usd ?? null;
622
+ const recordedUsd = record.cost.usd;
623
+ const effectiveUsd = recordedUsd ?? calculatedUsd;
624
+ const effectiveBrl =
625
+ record.cost.brl ??
626
+ actual?.estimated_brl ??
627
+ (effectiveUsd !== null && fxRate !== null
628
+ ? Math.round(effectiveUsd * fxRate.rate * 1_000_000) / 1_000_000
629
+ : null);
630
+ return {
631
+ provider,
632
+ model,
633
+ serviceTier,
634
+ uncachedInputTokens:
635
+ record.tokens.input === null
636
+ ? null
637
+ : Math.max(record.tokens.input - (record.tokens.cached_input ?? 0), 0),
638
+ visibleOutputTokens:
639
+ record.tokens.output === null
640
+ ? null
641
+ : Math.max(record.tokens.output - (record.tokens.reasoning ?? 0), 0),
642
+ inputRate,
643
+ cachedRate,
644
+ outputRate,
645
+ reasoningRate,
646
+ contextInputMultiplier: pricingModel
647
+ ? longContext
648
+ ? pricingModel.long_context_input_multiplier
649
+ : 1
650
+ : null,
651
+ contextOutputMultiplier: pricingModel
652
+ ? longContext
653
+ ? pricingModel.long_context_output_multiplier
654
+ : 1
655
+ : null,
656
+ speedCostMultiplier:
657
+ calculatedUsd !== null && standardUsd !== null && standardUsd > 0
658
+ ? Math.round((calculatedUsd / standardUsd) * 1000) / 1000
659
+ : null,
660
+ pricingScenario:
661
+ priority && longContext
662
+ ? "standard_long_context_fallback"
663
+ : priority
664
+ ? "priority"
665
+ : "standard",
666
+ standardUsd,
667
+ speedUpliftUsd:
668
+ calculatedUsd !== null && standardUsd !== null
669
+ ? Math.round((calculatedUsd - standardUsd) * 1_000_000) / 1_000_000
670
+ : null,
671
+ inputUsd: priced?.input_usd ?? null,
672
+ cachedInputUsd: priced?.cached_input_usd ?? null,
673
+ outputUsd: priced?.output_usd ?? null,
674
+ reasoningUsd: priced?.reasoning_usd ?? null,
675
+ calculatedUsd,
676
+ recordedUsd,
677
+ effectiveUsd,
678
+ effectiveBrl,
679
+ varianceUsd:
680
+ recordedUsd !== null && calculatedUsd !== null
681
+ ? Math.round((recordedUsd - calculatedUsd) * 1_000_000) / 1_000_000
682
+ : null,
683
+ pricingModel: priced?.pricing_model ?? null,
684
+ pricingSource: priced?.pricing_source ?? null,
685
+ fxRate: record.cost.fx_rate ?? fxRate?.rate ?? null,
686
+ };
687
+ }
688
+
689
+ export function saoPauloDay(timestamp: string): string {
690
+ const values = Object.fromEntries(
691
+ new Intl.DateTimeFormat("en", {
692
+ timeZone: "America/Sao_Paulo",
693
+ year: "numeric",
694
+ month: "2-digit",
695
+ day: "2-digit",
696
+ })
697
+ .formatToParts(new Date(timestamp))
698
+ .filter((part) => part.type !== "literal")
699
+ .map((part) => [part.type, part.value]),
700
+ ) as { year: string; month: string; day: string };
701
+ return `${values.year}-${values.month}-${values.day}`;
702
+ }
703
+
704
+ export type TemporalInterval = { activityRowId: string; startMs: number; endMs: number };
705
+
706
+ /**
707
+ * Splits every observed interval at every activity boundary and São Paulo day
708
+ * boundary. Each elementary segment is divided equally among the token-active
709
+ * activities covering it, making the materialized fact additive by activity,
710
+ * day, and every parent dimension.
711
+ */
712
+ export function allocateAttributedTokenActiveMs(intervals: readonly TemporalInterval[]) {
713
+ const boundaries = new Set<number>();
714
+ for (const interval of intervals) {
715
+ boundaries.add(interval.startMs);
716
+ boundaries.add(interval.endMs);
717
+ for (let cursor = interval.startMs; cursor < interval.endMs; ) {
718
+ const nextDay =
719
+ Date.parse(`${saoPauloDay(new Date(cursor).toISOString())}T03:00:00.000Z`) + 86_400_000;
720
+ if (nextDay >= interval.endMs) break;
721
+ boundaries.add(nextDay);
722
+ cursor = nextDay;
723
+ }
724
+ }
725
+ const points = [...boundaries].sort((left, right) => left - right);
726
+ const allocated = new Map<string, { activityRowId: string; day: string; value: number }>();
727
+ for (let index = 0; index < points.length - 1; index += 1) {
728
+ const startMs = points[index]!;
729
+ const endMs = points[index + 1]!;
730
+ const active = intervals.filter(
731
+ (interval) => interval.startMs < endMs && interval.endMs > startMs,
732
+ );
733
+ if (active.length === 0 || endMs <= startMs) continue;
734
+ const share = (endMs - startMs) / active.length;
735
+ const day = saoPauloDay(new Date(startMs).toISOString());
736
+ for (const interval of active) {
737
+ const key = `${interval.activityRowId}:${day}`;
738
+ const previous = allocated.get(key);
739
+ allocated.set(key, {
740
+ activityRowId: interval.activityRowId,
741
+ day,
742
+ value: (previous?.value ?? 0) + share,
743
+ });
744
+ }
745
+ }
746
+ return [...allocated.values()];
747
+ }
748
+
749
+ /** A dashboard API boundary can be a date or an ISO instant. Normalize both
750
+ * to the same Sao Paulo calendar key used by Cost Monitor SQL buckets. */
751
+ export function costMonitorFilterDay(value: string | null | undefined): string | null {
752
+ if (value === null || value === undefined) return null;
753
+ return /^\d{4}-\d{2}-\d{2}$/.test(value) ? value : saoPauloDay(value);
754
+ }
755
+
756
+ export function sumKnownDeveloperMetric(
757
+ rows: readonly Record<string, unknown>[],
758
+ field: string,
759
+ ): number | null {
760
+ const values = rows
761
+ .map((row) => row[field])
762
+ .filter((value): value is number => typeof value === "number" && Number.isFinite(value));
763
+ return values.length === 0 ? null : values.reduce((sum, value) => sum + value, 0);
764
+ }
765
+
766
+ /** Extracts the small artifact projection while the canonical file is already being read. */
767
+ export function readCanonicalArtifact(source: string, content: string): CanonicalArtifact | null {
768
+ const kind = source.includes("/artifacts/evidence/")
769
+ ? "evidence"
770
+ : source.includes("/artifacts/validations/") || source.includes("/artifacts/tests/")
771
+ ? "validation"
772
+ : null;
773
+ if (kind === null) return null;
774
+ const frontMatter = /^---\r?\n([\s\S]*?)\r?\n---/.exec(content)?.[1] ?? "";
775
+ const scalar = (key: string): string | null => {
776
+ const value = new RegExp(`^${key}:\\s*['"]?(.+?)['"]?\\s*$`, "m")
777
+ .exec(frontMatter)?.[1]
778
+ ?.trim();
779
+ return value === undefined || value === "null" ? null : value;
780
+ };
781
+ const occurredAt =
782
+ scalar("observed_at") ?? scalar("executed_at") ?? scalar("created_at") ?? scalar("recorded_at");
783
+ if (occurredAt === null || Number.isNaN(Date.parse(occurredAt))) return null;
784
+ return {
785
+ kind,
786
+ id: scalar("id") ?? source.slice(source.lastIndexOf("/") + 1, -3),
787
+ occurredAt,
788
+ occurredDaySp: saoPauloDay(occurredAt),
789
+ taskId: scalar("task_id"),
790
+ changeId: scalar("change_id"),
791
+ runId: scalar("run_id"),
792
+ };
793
+ }