@zaip-ai/cli 1.3.8 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (315) hide show
  1. package/README.md +58 -3
  2. package/dist/commands/init.d.ts +1 -0
  3. package/dist/commands/init.d.ts.map +1 -1
  4. package/dist/commands/init.js +17 -6
  5. package/dist/commands/init.js.map +1 -1
  6. package/dist/index.d.ts +1 -0
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +2 -0
  9. package/dist/index.js.map +1 -1
  10. package/dist/services/adapters.js +4 -4
  11. package/dist/services/adapters.js.map +1 -1
  12. package/dist/services/codex-config-template.d.ts +2 -2
  13. package/dist/services/codex-config-template.d.ts.map +1 -1
  14. package/dist/services/codex-config-template.js +3 -3
  15. package/dist/services/codex-config-template.js.map +1 -1
  16. package/dist/services/codex-rules.d.ts +1 -1
  17. package/dist/services/codex-rules.d.ts.map +1 -1
  18. package/dist/services/codex-rules.js +9 -24
  19. package/dist/services/codex-rules.js.map +1 -1
  20. package/dist/services/delivery-git-preflight.d.ts +23 -0
  21. package/dist/services/delivery-git-preflight.d.ts.map +1 -0
  22. package/dist/services/delivery-git-preflight.js +69 -0
  23. package/dist/services/delivery-git-preflight.js.map +1 -0
  24. package/dist/services/doctor.d.ts.map +1 -1
  25. package/dist/services/doctor.js +13 -1
  26. package/dist/services/doctor.js.map +1 -1
  27. package/dist/services/install-agents-template.d.ts.map +1 -1
  28. package/dist/services/install-agents-template.js +22 -28
  29. package/dist/services/install-agents-template.js.map +1 -1
  30. package/dist/services/install-profile.d.ts +8 -0
  31. package/dist/services/install-profile.d.ts.map +1 -0
  32. package/dist/services/install-profile.js +56 -0
  33. package/dist/services/install-profile.js.map +1 -0
  34. package/dist/services/logger.d.ts.map +1 -1
  35. package/dist/services/logger.js +6 -5
  36. package/dist/services/logger.js.map +1 -1
  37. package/dist/services/stack-git.d.ts +9 -0
  38. package/dist/services/stack-git.d.ts.map +1 -0
  39. package/dist/services/stack-git.js +188 -0
  40. package/dist/services/stack-git.js.map +1 -0
  41. package/dist/services/stack-plan.d.ts +3 -1
  42. package/dist/services/stack-plan.d.ts.map +1 -1
  43. package/dist/services/stack-plan.js +15 -1
  44. package/dist/services/stack-plan.js.map +1 -1
  45. package/dist/services/stack-terraform.d.ts +13 -0
  46. package/dist/services/stack-terraform.d.ts.map +1 -0
  47. package/dist/services/stack-terraform.js +274 -0
  48. package/dist/services/stack-terraform.js.map +1 -0
  49. package/dist/services/template-manifest.d.ts.map +1 -1
  50. package/dist/services/template-manifest.js +14 -1
  51. package/dist/services/template-manifest.js.map +1 -1
  52. package/dist/types.d.ts +1 -0
  53. package/dist/types.d.ts.map +1 -1
  54. package/package.json +1 -1
  55. package/templates/.agents/.env.example +34 -0
  56. package/templates/{README.md → .agents/README.md} +124 -60
  57. package/templates/.agents/config/agents/catalog.yaml +23 -23
  58. package/templates/.agents/config/clients/codex/agents/zaip-agent-analyst.toml +1 -0
  59. package/templates/.agents/config/clients/codex/agents/zaip-agent-auditor.toml +1 -0
  60. package/templates/.agents/config/clients/codex/agents/zaip-agent-backend.toml +5 -4
  61. package/templates/.agents/config/clients/codex/agents/zaip-agent-brand-strategist.toml +1 -0
  62. package/templates/.agents/config/clients/codex/agents/zaip-agent-builder.toml +1 -0
  63. package/templates/.agents/config/clients/codex/agents/zaip-agent-cloud-engineer.toml +10 -7
  64. package/templates/.agents/config/clients/codex/agents/zaip-agent-data-analyst.toml +1 -0
  65. package/templates/.agents/config/clients/codex/agents/zaip-agent-designer.toml +1 -0
  66. package/templates/.agents/config/clients/codex/agents/zaip-agent-finalizer.toml +1 -0
  67. package/templates/.agents/config/clients/codex/agents/zaip-agent-frontend.toml +5 -4
  68. package/templates/.agents/config/clients/codex/agents/zaip-agent-general-operations.toml +1 -0
  69. package/templates/.agents/config/clients/codex/agents/zaip-agent-governance-compliance.toml +1 -0
  70. package/templates/.agents/config/clients/codex/agents/zaip-agent-growth-strategist.toml +1 -0
  71. package/templates/.agents/config/clients/codex/agents/zaip-agent-integrator.toml +1 -0
  72. package/templates/.agents/config/clients/codex/agents/zaip-agent-marketing-analyst.toml +5 -4
  73. package/templates/.agents/config/clients/codex/agents/zaip-agent-orchestrator.toml +1 -0
  74. package/templates/.agents/config/clients/codex/agents/zaip-agent-refactoring-specialist.toml +1 -0
  75. package/templates/.agents/config/clients/codex/agents/zaip-agent-reviewer.toml +1 -0
  76. package/templates/.agents/config/clients/codex/agents/zaip-agent-sales-specialist.toml +1 -0
  77. package/templates/.agents/config/clients/codex/agents/zaip-agent-security-auditor.toml +1 -0
  78. package/templates/.agents/config/clients/codex/agents/zaip-agent-task-executor.toml +1 -0
  79. package/templates/.agents/config/clients/codex/agents/zaip-agent-website-developer.toml +5 -4
  80. package/templates/.agents/config/clients/codex/agents/zaip-agent-writer.toml +1 -0
  81. package/templates/.agents/config/pricing/models.json +25 -1
  82. package/templates/.agents/config/project.yaml +2 -0
  83. package/templates/.agents/dashboard/.zaip-managed.json +200 -88
  84. package/templates/.agents/dashboard/DATA-DICTIONARY.md +1 -1
  85. package/templates/.agents/dashboard/README.md +58 -19
  86. package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-DwVZOmMV.js → CostCalculationGuidePage-snUCQYTC.js} +1 -1
  87. package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog-VcLx9iOO.js → DashboardHelpDialog-oZVlf-l5.js} +72 -8
  88. package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-CyjMXkm8.js → PricingSettingsDialog-BPtVUGgs.js} +1 -1
  89. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-3Awyaovx.js +3 -0
  90. package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-DFZwIXSC.js → TraceabilityWorkspace-BYjVOHI_.js} +1 -1
  91. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-DbaLbU2q.js +68 -0
  92. package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-DXBOtWNI.js → icons-vendor-TPUT2IUt.js} +1 -1
  93. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-Cmlx8OYH.css +1 -0
  94. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-ZJFXmK7m.js +2 -0
  95. package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-DaNXQPyk.js → react-vendor-Ct48XHGL.js} +1 -1
  96. package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-I4AKqVDX.js → traceability-BDe4_Ubl.js} +1 -1
  97. package/templates/.agents/dashboard/apps/frontend/dist/index.html +6 -6
  98. package/templates/.agents/dashboard/apps/frontend/src/App.tsx +73 -67
  99. package/templates/.agents/dashboard/apps/frontend/src/AppLoadingBoundary.tsx +35 -7
  100. package/templates/.agents/dashboard/apps/frontend/src/AppWorkBoard.ts +55 -0
  101. package/templates/.agents/dashboard/apps/frontend/src/ProjectStackEditor.tsx +11 -0
  102. package/templates/.agents/dashboard/apps/frontend/src/app-loading-boundary.css +98 -0
  103. package/templates/.agents/dashboard/apps/frontend/src/cost-period-refresh.test.ts +5 -5
  104. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api-reconciliation.ts +33 -0
  105. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.test.ts +75 -0
  106. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.ts +7 -8
  107. package/templates/.agents/dashboard/apps/frontend/src/project-settings-model.ts +14 -2
  108. package/templates/.agents/dashboard/apps/frontend/src/project-settings.test.ts +35 -0
  109. package/templates/.agents/dashboard/apps/frontend/src/snapshot-coherence.test.ts +78 -2
  110. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.test.ts +28 -0
  111. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.ts +8 -5
  112. package/templates/.agents/dashboard/apps/frontend/src/use-app-effects.ts +180 -1
  113. package/templates/.agents/dashboard/modules/cost-monitor/src/contracts.ts +7 -4
  114. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorContent.tsx +13 -7
  115. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.tsx +14 -0
  116. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTableDisplay.ts +211 -0
  117. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.test.ts +152 -157
  118. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.tsx +155 -142
  119. package/templates/.agents/dashboard/modules/cost-monitor/src/react/InvocationManifestQuery.test.ts +145 -0
  120. package/templates/.agents/dashboard/modules/cost-monitor/src/react/period-cost-groups.ts +7 -5
  121. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-compact-cost-page.ts +4 -1
  122. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-group-page.ts +4 -1
  123. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-run-page.ts +4 -1
  124. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-projection.ts +4 -4
  125. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.test.ts +79 -1
  126. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.tsx +105 -11
  127. package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +482 -332
  128. package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +430 -285
  129. package/templates/.agents/dashboard/packages/local-runtime/scripts/build-sqlite-runtime.mjs +14 -2
  130. package/templates/.agents/dashboard/packages/local-runtime/scripts/rebuild-sqlite-read-model.mjs +6 -26
  131. package/templates/.agents/dashboard/packages/local-runtime/scripts/validate-sqlite-read-model.mjs +1 -1
  132. package/templates/.agents/dashboard/packages/local-runtime/scripts/verify-persistent-launch.mjs +268 -0
  133. package/templates/.agents/dashboard/packages/local-runtime/server.mjs +482 -332
  134. package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.test.ts +90 -1
  135. package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.ts +112 -22
  136. package/templates/.agents/dashboard/packages/local-runtime/src/server-agent-assets.ts +39 -0
  137. package/templates/.agents/dashboard/packages/local-runtime/src/server-browser-lease.ts +33 -0
  138. package/templates/.agents/dashboard/packages/local-runtime/src/server-reconciliation-refresh.test.ts +148 -11
  139. package/templates/.agents/dashboard/packages/local-runtime/src/server-reconciliation-result.test.ts +68 -0
  140. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-refresh.test.ts +19 -0
  141. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-refresh.ts +39 -43
  142. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-revision.test.ts +42 -0
  143. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-revision.ts +26 -0
  144. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-synchronization.ts +34 -0
  145. package/templates/.agents/dashboard/packages/local-runtime/src/server-startup.ts +30 -0
  146. package/templates/.agents/dashboard/packages/local-runtime/src/server-work-reconciliation.test.ts +89 -0
  147. package/templates/.agents/dashboard/packages/local-runtime/src/server-work-reconciliation.ts +88 -0
  148. package/templates/.agents/dashboard/packages/local-runtime/src/server-workspace.ts +68 -7
  149. package/templates/.agents/dashboard/packages/local-runtime/src/server.test.ts +11 -5
  150. package/templates/.agents/dashboard/packages/local-runtime/src/server.ts +129 -99
  151. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-agent-productivity.test.ts +111 -0
  152. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-agent-productivity.ts +140 -0
  153. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-agent-page.test.ts +127 -4
  154. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-agent-page.ts +145 -5
  155. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-aggregates.ts +156 -23
  156. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-group-projection.ts +18 -9
  157. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-projection.ts +52 -0
  158. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-first-fold-persistence.ts +35 -13
  159. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-force-canonical.test.ts +56 -0
  160. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-incremental-cache.test.ts +29 -0
  161. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-incremental-cache.ts +136 -0
  162. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-incremental-refresh.test.ts +251 -0
  163. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-operations-projection.ts +22 -7
  164. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-cache.test-support.ts +1 -1
  165. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-canonical-parsing.ts +42 -0
  166. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-canonical.ts +793 -0
  167. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider-duration.test.ts +270 -0
  168. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider-fixture.ts +3 -2
  169. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider.test.ts +18 -4
  170. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-source-fingerprint.test.ts +87 -0
  171. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-source-fingerprint.ts +105 -16
  172. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.test.ts +131 -59
  173. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.ts +1000 -1115
  174. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-cache.ts +13 -92
  175. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-claims.test.mjs +79 -0
  176. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.mjs +27 -0
  177. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-lifecycle.mjs +6 -6
  178. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-prerequisites.mjs +1 -1
  179. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-refresh.mjs +2 -10
  180. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-server.mjs +142 -0
  181. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.mjs +231 -239
  182. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.test.mjs +18 -29
  183. package/templates/.agents/dashboard/packages/windows-launcher/Program.cs +6 -0
  184. package/templates/.agents/dashboard/zaip-dashboard-windows.exe +0 -0
  185. package/templates/.agents/requirements/autonomous-assurance.md +1 -1
  186. package/templates/.agents/requirements/cloud-hostinger-autonomy.md +47 -0
  187. package/templates/.agents/requirements/comparative-audit.md +1 -1
  188. package/templates/.agents/requirements/context-engine.md +1 -1
  189. package/templates/.agents/requirements/cost-observability.md +1 -1
  190. package/templates/.agents/requirements/dashboard-launch.md +2 -2
  191. package/templates/.agents/requirements/data-export.md +1 -1
  192. package/templates/.agents/requirements/design-system.md +1 -1
  193. package/templates/.agents/requirements/execution-engine.md +1 -1
  194. package/templates/.agents/requirements/kanban-dashboard.md +1 -1
  195. package/templates/.agents/requirements/model-orchestration.md +1 -1
  196. package/templates/.agents/requirements/public-distribution.md +1 -1
  197. package/templates/.agents/requirements/software-architecture.md +1 -1
  198. package/templates/.agents/requirements/strategic-improvements.md +1 -1
  199. package/templates/.agents/requirements/work-lifecycle.md +1 -1
  200. package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
  201. package/templates/.agents/rules/autonomy-decision.md +1 -1
  202. package/templates/.agents/rules/cloud-hostinger-autonomy.md +21 -0
  203. package/templates/.agents/rules/core.md +5 -4
  204. package/templates/.agents/rules/local-sqlite-storage.md +18 -0
  205. package/templates/.agents/rules/meta-mcp.md +21 -0
  206. package/templates/.agents/rules/workflow.md +1 -0
  207. package/templates/.agents/runtime/bin/zaip.mjs +305 -137
  208. package/templates/.agents/runtime/checksums.sha256 +2 -2
  209. package/templates/.agents/runtime/provenance.json +2 -2
  210. package/templates/.agents/skills/zaip-skill-api-to-mcp/SKILL.md +4 -0
  211. package/templates/.agents/skills/zaip-skill-brand-governance/SKILL.md +25 -9
  212. package/templates/.agents/skills/zaip-skill-brand-governance/references/brandbook-framework.md +2 -2
  213. package/templates/.agents/skills/zaip-skill-brand-governance/references/brandbook-standards.md +2 -2
  214. package/templates/.agents/skills/zaip-skill-change/references/observable-evidence.md +15 -0
  215. package/templates/.agents/skills/zaip-skill-dashboard-sqlite/SKILL.md +13 -5
  216. package/templates/.agents/skills/zaip-skill-data-analyst/SKILL.md +10 -5
  217. package/templates/.agents/skills/zaip-skill-execution/SKILL.md +5 -4
  218. package/templates/.agents/skills/zaip-skill-frontend-launcher/SKILL.md +4 -3
  219. package/templates/.agents/skills/zaip-skill-frontend-launcher/references/launcher-contract.md +5 -3
  220. package/templates/.agents/skills/zaip-skill-github-deploy/SKILL.md +5 -5
  221. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/preflight.mjs +78 -13
  222. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/preflight.test.mjs +59 -0
  223. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/yaml-parser.mjs +157 -0
  224. package/templates/.agents/skills/zaip-skill-growth-strategy-revops/SKILL.md +11 -6
  225. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/SKILL.md +17 -0
  226. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/agents/openai.yaml +7 -0
  227. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/references/autonomous-cloud-inputs.example.json +41 -0
  228. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/references/autonomous-runner.md +84 -0
  229. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/references/operational-contract.md +19 -0
  230. package/templates/.agents/skills/zaip-skill-local-credentials/SKILL.md +43 -0
  231. package/templates/.agents/skills/zaip-skill-local-credentials/agents/openai.yaml +6 -0
  232. package/templates/.agents/skills/zaip-skill-local-credentials/references/dotenv-resolution.md +47 -0
  233. package/templates/.agents/skills/zaip-skill-marketing/SKILL.md +8 -3
  234. package/templates/.agents/skills/zaip-skill-meta-ads/SKILL.md +16 -0
  235. package/templates/.agents/skills/zaip-skill-meta-ads/agents/openai.yaml +4 -0
  236. package/templates/.agents/skills/zaip-skill-meta-ads/references/setup.md +3 -0
  237. package/templates/.agents/skills/zaip-skill-meta-ads-setup/SKILL.md +24 -0
  238. package/templates/.agents/skills/zaip-skill-meta-ads-setup/agents/openai.yaml +4 -0
  239. package/templates/.agents/skills/zaip-skill-meta-ads-setup/references/diagnostics.md +13 -0
  240. package/templates/.agents/skills/zaip-skill-meta-ads-setup/references/official-sources.md +10 -0
  241. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/SKILL.md +12 -0
  242. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/agents/openai.yaml +4 -0
  243. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/references/LICENSE +21 -0
  244. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/references/upstream.md +77 -0
  245. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/SKILL.md +12 -0
  246. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/agents/openai.yaml +4 -0
  247. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/references/LICENSE +21 -0
  248. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/references/upstream.md +126 -0
  249. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/SKILL.md +12 -0
  250. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/agents/openai.yaml +4 -0
  251. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/references/LICENSE +21 -0
  252. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/references/upstream.md +67 -0
  253. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/SKILL.md +12 -0
  254. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/agents/openai.yaml +4 -0
  255. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/references/LICENSE +21 -0
  256. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/references/upstream.md +69 -0
  257. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/SKILL.md +12 -0
  258. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/agents/openai.yaml +4 -0
  259. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/references/LICENSE +21 -0
  260. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/references/upstream.md +79 -0
  261. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/SKILL.md +14 -0
  262. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/agents/openai.yaml +4 -0
  263. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/references/LICENSE +21 -0
  264. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/references/upstream.md +144 -0
  265. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/SKILL.md +12 -0
  266. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/agents/openai.yaml +4 -0
  267. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/references/LICENSE +21 -0
  268. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/references/upstream.md +83 -0
  269. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/SKILL.md +10 -0
  270. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/agents/openai.yaml +4 -0
  271. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/references/LICENSE +21 -0
  272. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/references/setup.md +11 -0
  273. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/references/upstream.md +55 -0
  274. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/SKILL.md +12 -0
  275. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/agents/openai.yaml +4 -0
  276. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/references/LICENSE +21 -0
  277. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/references/upstream.md +56 -0
  278. package/templates/.agents/skills/zaip-skill-sales-methodology/SKILL.md +12 -7
  279. package/templates/.agents/skills/zaip-skill-sqlite-read-model/SKILL.md +11 -4
  280. package/templates/.agents/skills/zaip-skill-tool-setup/SKILL.md +6 -4
  281. package/templates/.agents/skills/zaip-skill-tool-setup/references/codex-config.md +4 -2
  282. package/templates/.agents/skills/zaip-skill-tool-setup/references/secrets.md +1 -0
  283. package/templates/.agents/tools/catalog.yaml +6 -0
  284. package/templates/.agents/tools/cloud-runner-dns.mjs +28 -0
  285. package/templates/.agents/tools/cloud-runner-terraform.mjs +212 -0
  286. package/templates/.agents/tools/cloud-runner.integration.test.mjs +239 -0
  287. package/templates/.agents/tools/cloud-runner.mjs +786 -0
  288. package/templates/.agents/tools/cloud-runner.test.mjs +534 -0
  289. package/templates/.agents/tools/cloud-terraform-plan-policy.mjs +262 -0
  290. package/templates/.agents/tools/mcp/development/meta-devtools/README.md +14 -0
  291. package/templates/.agents/tools/mcp/development/meta-devtools/manifest.yaml +28 -0
  292. package/templates/.agents/tools/mcp/infra/cloudflare/manifest.yaml +2 -3
  293. package/templates/.agents/tools/mcp/infra/hostinger/manifest.yaml +1 -3
  294. package/templates/.agents/tools/mcp/marketing/meta-ads/README.md +15 -13
  295. package/templates/.agents/tools/mcp/marketing/meta-ads/manifest.yaml +15 -10
  296. package/templates/.agents/tools/profiles/development.yaml +3 -0
  297. package/templates/infra/terraform/.terraform.lock.hcl +42 -0
  298. package/templates/infra/terraform/README.md +31 -0
  299. package/templates/infra/terraform/backend.tf.json +13 -0
  300. package/templates/infra/terraform/deployment-inputs.example.yaml +22 -0
  301. package/templates/infra/terraform/fixtures/approved-targets.json +32 -0
  302. package/templates/infra/terraform/fixtures/plan-approved.json +67 -0
  303. package/templates/infra/terraform/main.tf +77 -0
  304. package/templates/infra/terraform/outputs.tf +20 -0
  305. package/templates/infra/terraform/terraform.tfvars.example +26 -0
  306. package/templates/infra/terraform/variables.tf +91 -0
  307. package/templates/infra/terraform/versions.tf +18 -0
  308. package/templates/scripts/validate-cloud-hostinger-contract.mjs +89 -0
  309. package/templates/scripts/validate-cloud-terraform-plan.mjs +1 -0
  310. package/templates/template-manifest.json +599 -167
  311. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-BwnJpl02.js +0 -3
  312. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-C09KmlSw.js +0 -68
  313. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-Dyo_wpL4.js +0 -2
  314. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-hPPfVGQv.css +0 -1
  315. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-stale-seed.ts +0 -50
@@ -9,7 +9,8 @@ export const stackComponentDefinitions = [
9
9
  { id: "auth_storage", defaultName: "Supabase Auth + Storage", scope: "backend" },
10
10
  { id: "cache_messaging", defaultName: "Redis", scope: "backend" },
11
11
  { id: "async_queue", defaultName: "BullMQ", scope: "backend" },
12
- { id: "backend_infrastructure", defaultName: "Hostinger VPS", scope: "backend" },
12
+ { id: "backend_infrastructure", defaultName: "Terraform", scope: "backend" },
13
+ { id: "backend_hosting", defaultName: "Hostinger VPS", scope: "backend" },
13
14
  { id: "web_framework", defaultName: "React", scope: "frontend" },
14
15
  { id: "web_language", defaultName: "TypeScript", scope: "frontend" },
15
16
  { id: "web_build", defaultName: "Vite", scope: "frontend" },
@@ -19,6 +20,14 @@ export const stackComponentDefinitions = [
19
20
  { id: "site_islands", defaultName: "React Islands", scope: "site" },
20
21
  { id: "site_build", defaultName: "Vite", scope: "site" },
21
22
  { id: "site_hosting", defaultName: "Cloudflare Pages", scope: "site" },
23
+ { id: "prometheus", defaultName: "Prometheus", scope: "observability" },
24
+ { id: "grafana", defaultName: "Grafana", scope: "observability" },
25
+ { id: "uptime_kuma", defaultName: "Uptime Kuma", scope: "observability" },
26
+ { id: "cadvisor", defaultName: "cAdvisor", scope: "observability" },
27
+ { id: "node_exporter", defaultName: "node-exporter", scope: "observability" },
28
+ { id: "redis_exporter", defaultName: "Redis Exporter", scope: "observability" },
29
+ { id: "k6", defaultName: "k6", scope: "testing" },
30
+ { id: "autocannon", defaultName: "autocannon", scope: "testing" },
22
31
  ] as const;
23
32
 
24
33
  export type StackComponentId = (typeof stackComponentDefinitions)[number]["id"];
@@ -107,13 +116,16 @@ export function buildProjectSettingsPayload(
107
116
  },
108
117
  ]),
109
118
  ) as StackComponentsDraft;
119
+ const existingComponents = isRecord(config.stack.overrides.components)
120
+ ? config.stack.overrides.components
121
+ : {};
110
122
  return {
111
123
  ...config,
112
124
  stack: {
113
125
  ...config.stack,
114
126
  overrides: {
115
127
  ...otherOverrides,
116
- components: normalized,
128
+ components: { ...existingComponents, ...normalized },
117
129
  languages: {
118
130
  frontend: componentNames(normalized, "frontend"),
119
131
  backend: componentNames(normalized, "backend"),
@@ -70,6 +70,7 @@ describe("project settings form mapping", () => {
70
70
  "Supabase Auth + Storage",
71
71
  "Redis",
72
72
  "BullMQ",
73
+ "Terraform",
73
74
  "Hostinger VPS",
74
75
  ],
75
76
  site: ["Astro", "TypeScript", "React Islands", "Vite", "Cloudflare Pages"],
@@ -98,6 +99,40 @@ describe("project settings form mapping", () => {
98
99
  ).toThrow("deve ser exata");
99
100
  });
100
101
 
102
+ it("keeps observability and on-demand testing tools editable with pinned-or-empty versions", () => {
103
+ const configured = {
104
+ ...config,
105
+ stack: {
106
+ ...config.stack,
107
+ overrides: {
108
+ ...config.stack.overrides,
109
+ components: { future_tool: { enabled: true } },
110
+ },
111
+ },
112
+ } satisfies ProjectConfigSettings;
113
+ const draft = projectSettingsDraft(configured);
114
+ const components = {
115
+ ...draft.components,
116
+ prometheus: { name: "Prometheus", version: "3.6.0", reference: "docker:prom/prometheus" },
117
+ grafana: { name: "Grafana", version: "", reference: "docker:grafana/grafana" },
118
+ k6: { name: "k6", version: "1.1.0", reference: "docker:grafana/k6" },
119
+ autocannon: { name: "autocannon", version: "", reference: "npm:autocannon" },
120
+ };
121
+
122
+ const payload = buildProjectSettingsPayload(configured, components, draft.otherOverrides);
123
+
124
+ expect(payload.stack.overrides).toMatchObject({
125
+ components: {
126
+ prometheus: components.prometheus,
127
+ grafana: components.grafana,
128
+ uptime_kuma: { name: "Uptime Kuma", version: "", reference: "" },
129
+ k6: components.k6,
130
+ autocannon: components.autocannon,
131
+ future_tool: { enabled: true },
132
+ },
133
+ });
134
+ });
135
+
101
136
  it("keeps execution and assurance settings in the save payload", () => {
102
137
  const configured: ProjectConfigSettings = {
103
138
  ...config,
@@ -2,7 +2,20 @@ import { readFileSync } from "node:fs";
2
2
  import { describe, expect, it } from "vitest";
3
3
  import { isCurrentDashboardRequest, mergeBoardPages, needsAtomicCostSnapshot } from "./App.js";
4
4
  import { loadAtomicCostSnapshot } from "./snapshot-coherence.js";
5
- import { projectionRefreshDebounceMs, shouldRefreshVisibleProjection } from "./use-app-effects.js";
5
+ import {
6
+ isCommittedSnapshotRevision,
7
+ projectionRefreshDebounceMs,
8
+ settleSnapshotRevisionRefresh,
9
+ shouldPollSnapshotReady,
10
+ shouldRefreshForSnapshotRevision,
11
+ shouldRefreshVisibleProjection,
12
+ snapshotReadyPollAttempts,
13
+ snapshotReadyPollExhaustedMessage,
14
+ snapshotRevisionHiddenFailurePollMs,
15
+ snapshotRevisionHiddenPollMs,
16
+ snapshotRevisionPollDelay,
17
+ snapshotRevisionVisiblePollMs,
18
+ } from "./use-app-effects.js";
6
19
 
7
20
  describe("atomic Cost snapshot loading", () => {
8
21
  it("keeps Operations, Cost, and cards on one source generation while the source moves", async () => {
@@ -82,7 +95,7 @@ describe("atomic Cost snapshot loading", () => {
82
95
  expect(retry).not.toContain("loadCosts");
83
96
  });
84
97
 
85
- it("does not prefetch or poll a newer Operations snapshot in the background", () => {
98
+ it("does not prefetch a newer Operations snapshot in the background", () => {
86
99
  const app = readFileSync(new URL("./App.tsx", import.meta.url), "utf8");
87
100
  const effects = readFileSync(new URL("./use-app-effects.ts", import.meta.url), "utf8");
88
101
  const costLoaders = readFileSync(new URL("./use-app-cost-loaders.ts", import.meta.url), "utf8");
@@ -135,6 +148,69 @@ describe("atomic Cost snapshot loading", () => {
135
148
  expect(effects).toContain("void refresh(false, false, true)");
136
149
  });
137
150
 
151
+ it("polls ready only while a startup snapshot reports updating", () => {
152
+ expect(shouldPollSnapshotReady(true, 0)).toBe(true);
153
+ expect(shouldPollSnapshotReady(true, snapshotReadyPollAttempts - 1)).toBe(true);
154
+ expect(shouldPollSnapshotReady(true, snapshotReadyPollAttempts)).toBe(false);
155
+ expect(shouldPollSnapshotReady(false, 0)).toBe(false);
156
+ expect(snapshotReadyPollExhaustedMessage).toContain("desatualizados");
157
+ const effects = readFileSync(new URL("./use-app-effects.ts", import.meta.url), "utf8");
158
+ expect(effects).toContain("const ready = await api.ready()");
159
+ expect(effects).toContain("await refresh(false, false, false, true)");
160
+ expect(effects).not.toContain("refresh(false, true");
161
+ expect(effects).toContain("setSnapshotUpdating(false);");
162
+ expect(effects).toContain("setError(snapshotReadyPollExhaustedMessage)");
163
+ });
164
+
165
+ it("observes committed snapshot revisions from an open tab with GET-only refreshes", () => {
166
+ expect(snapshotRevisionPollDelay("visible", 0)).toBe(snapshotRevisionVisiblePollMs);
167
+ expect(snapshotRevisionPollDelay("hidden", 0)).toBe(snapshotRevisionHiddenPollMs);
168
+ expect(snapshotRevisionPollDelay("hidden", 1)).toBe(snapshotRevisionHiddenFailurePollMs);
169
+ const committedHash = "40fb07a77e3f";
170
+ const nextCommittedHash = "1c83d5ea7142";
171
+ expect(shouldRefreshForSnapshotRevision(null, committedHash)).toBe(false);
172
+ expect(shouldRefreshForSnapshotRevision(committedHash, undefined)).toBe(false);
173
+ expect(shouldRefreshForSnapshotRevision(committedHash, committedHash)).toBe(false);
174
+ // `updating` only means a later generation is being prepared. The hash
175
+ // still identifies the already committed SQLite generation and can make
176
+ // an open tab reload when it differs from the last applied one.
177
+ expect(isCommittedSnapshotRevision("updating", nextCommittedHash)).toBe(true);
178
+ expect(shouldRefreshForSnapshotRevision(committedHash, nextCommittedHash)).toBe(true);
179
+ expect(isCommittedSnapshotRevision("current", undefined)).toBe(false);
180
+ expect(isCommittedSnapshotRevision("current", nextCommittedHash)).toBe(true);
181
+
182
+ const afterFailedReload = settleSnapshotRevisionRefresh({
183
+ observedRevision: "revision-1",
184
+ pendingRevision: "revision-2",
185
+ requestedRevision: "revision-2",
186
+ succeeded: false,
187
+ });
188
+ expect(afterFailedReload).toEqual({
189
+ observedRevision: "revision-1",
190
+ pendingRevision: "revision-2",
191
+ });
192
+ expect(
193
+ settleSnapshotRevisionRefresh({
194
+ ...afterFailedReload,
195
+ requestedRevision: "revision-2",
196
+ succeeded: true,
197
+ }),
198
+ ).toEqual({ observedRevision: "revision-2", pendingRevision: null });
199
+
200
+ const effects = readFileSync(new URL("./use-app-effects.ts", import.meta.url), "utf8");
201
+ expect(effects).toContain("const observedSnapshotRevision = useRef<string | null>(null)");
202
+ expect(effects).toContain("pendingSnapshotRevisionRefresh");
203
+ expect(effects).toContain(
204
+ "isCommittedSnapshotRevision(ready.snapshot_state, ready.snapshot_revision)",
205
+ );
206
+ expect(effects).toContain("settleSnapshotRevisionRefresh({");
207
+ expect(effects).toContain(
208
+ "snapshotRevisionPollDelay(document.visibilityState, consecutiveFailures)",
209
+ );
210
+ expect(effects).toContain("void refresh(false, false, false, true)");
211
+ expect(effects).not.toContain("refresh(false, false, true, true)");
212
+ });
213
+
138
214
  it("does not reconcile open work automatically", () => {
139
215
  const app = readFileSync(new URL("./App.tsx", import.meta.url), "utf8");
140
216
  expect(app).not.toContain("startupReconciliation");
@@ -119,6 +119,34 @@ describe("daily Cost Monitor continuous pagination", () => {
119
119
  });
120
120
  });
121
121
 
122
+ describe("SQLite Cost group duration adapter", () => {
123
+ it("prefers total_duration over a synthesized duration_known zero and uses zero only when absent", async () => {
124
+ const api = {
125
+ sqliteViewPage: async () => ({
126
+ offset: 0,
127
+ limit: 10,
128
+ total_count: 2,
129
+ has_more: false,
130
+ rows: [
131
+ { key: "gpt-5.6-sol", total_duration: 285_480_000, duration_known: 0 },
132
+ { key: "unavailable", total_duration: null, duration_known: undefined },
133
+ ],
134
+ }),
135
+ };
136
+ const page = await loadSqliteCostGroupPage(api as never, {
137
+ view: "models",
138
+ offset: 0,
139
+ limit: 10,
140
+ filters: {} as never,
141
+ });
142
+
143
+ expect(page.rows).toMatchObject([
144
+ { key: "gpt-5.6-sol", duration_ms: 285_480_000, duration: { known_sum: 285_480_000 } },
145
+ { key: "unavailable", duration_ms: null, duration: { known_sum: 0 } },
146
+ ]);
147
+ });
148
+ });
149
+
122
150
  describe("adaptSqliteCostRun", () => {
123
151
  it("renders a sparse API invocation row without throwing and marks missing multipliers unavailable", () => {
124
152
  const run = adaptSqliteCostRun({
@@ -40,7 +40,9 @@ function costGroupQueryOptions(request: CostGroupLoadRequest, offset: number, li
40
40
  function adaptSqliteCostGroup(row: Record<string, unknown>) {
41
41
  const duration = costGroupDuration(row);
42
42
  const durationUnknown = Number(row.duration_unknown ?? (duration === null ? 1 : 0));
43
- const durationKnown = nullableSqlNumber(row.duration_known) ?? duration ?? 0;
43
+ // Model rows may expose only `total_duration`. Prefer it over a legacy
44
+ // synthesized zero in `duration_known` so the table and export agree.
45
+ const durationKnown = duration ?? nullableSqlNumber(row.duration_known) ?? 0;
44
46
  return {
45
47
  key: String(row.key ?? row.period_key ?? "total"),
46
48
  label: String(row.label ?? row.key ?? row.period_key ?? ""),
@@ -49,10 +51,11 @@ function adaptSqliteCostGroup(row: Record<string, unknown>) {
49
51
  job: typeof row.job === "string" ? row.job : null,
50
52
  run_count: Number(row.total_run_count ?? row.invocation_count ?? 0),
51
53
  task_count: nullableSqlNumber(row.task_count),
52
- prd_count: Number(row.prd_count ?? 0),
53
- spec_count: Number(row.spec_count ?? 0),
54
- evidence_count: Number(row.evidence_count ?? 0),
55
- test_count: Number(row.validation_count ?? 0),
54
+ conversation_count: Number(row.conversation_count ?? 0),
55
+ prd_count: nullableSqlNumber(row.prd_count),
56
+ spec_count: nullableSqlNumber(row.spec_count),
57
+ evidence_count: nullableSqlNumber(row.evidence_count),
58
+ test_count: nullableSqlNumber(row.validation_count),
56
59
  event_count: Number(row.source_event_count ?? 0),
57
60
  total_tokens: nullableSqlNumber(row.total_tokens),
58
61
  estimated_usd: nullableSqlNumber(row.total_cost_usd),
@@ -17,6 +17,68 @@ import {
17
17
  } from "./operations-detail-hydration.js";
18
18
 
19
19
  export const projectionRefreshDebounceMs = 5_000;
20
+ export const snapshotReadyPollMs = 2_000;
21
+ export const snapshotReadyPollAttempts = 90;
22
+ export const snapshotRevisionVisiblePollMs = 5_000;
23
+ export const snapshotRevisionHiddenPollMs = 10_000;
24
+ export const snapshotRevisionHiddenFailurePollMs = 30_000;
25
+ export const snapshotReadyPollExhaustedMessage =
26
+ "A atualização do snapshot não foi concluída. Os dados exibidos podem estar desatualizados.";
27
+
28
+ export function shouldPollSnapshotReady(snapshotUpdating: boolean, attempts: number): boolean {
29
+ return snapshotUpdating && attempts < snapshotReadyPollAttempts;
30
+ }
31
+
32
+ export function snapshotRevisionPollDelay(
33
+ visibilityState: DocumentVisibilityState,
34
+ consecutiveFailures: number,
35
+ ): number {
36
+ if (visibilityState === "visible") return snapshotRevisionVisiblePollMs;
37
+ return consecutiveFailures > 0
38
+ ? snapshotRevisionHiddenFailurePollMs
39
+ : snapshotRevisionHiddenPollMs;
40
+ }
41
+
42
+ export function shouldRefreshForSnapshotRevision(
43
+ previousRevision: string | null,
44
+ nextRevision: string | undefined,
45
+ ): boolean {
46
+ return (
47
+ previousRevision !== null &&
48
+ typeof nextRevision === "string" &&
49
+ nextRevision.length > 0 &&
50
+ previousRevision !== nextRevision
51
+ );
52
+ }
53
+
54
+ export function isCommittedSnapshotRevision(
55
+ snapshotState: "current" | "updating" | undefined,
56
+ snapshotRevision: string | undefined,
57
+ ): snapshotRevision is string {
58
+ return (
59
+ (snapshotState === "current" || snapshotState === "updating") &&
60
+ typeof snapshotRevision === "string" &&
61
+ snapshotRevision.length > 0
62
+ );
63
+ }
64
+
65
+ export function settleSnapshotRevisionRefresh({
66
+ observedRevision,
67
+ pendingRevision,
68
+ requestedRevision,
69
+ succeeded,
70
+ }: {
71
+ observedRevision: string | null;
72
+ pendingRevision: string | null;
73
+ requestedRevision: string;
74
+ succeeded: boolean;
75
+ }): { observedRevision: string | null; pendingRevision: string | null } {
76
+ if (!succeeded) return { observedRevision, pendingRevision };
77
+ return {
78
+ observedRevision: requestedRevision,
79
+ pendingRevision: pendingRevision === requestedRevision ? null : pendingRevision,
80
+ };
81
+ }
20
82
 
21
83
  export function shouldRefreshVisibleProjection({
22
84
  inFlight,
@@ -48,6 +110,8 @@ export function useAppEffects({
48
110
  pricing,
49
111
  refresh,
50
112
  refreshVersion,
113
+ snapshotUpdating,
114
+ setSnapshotUpdating,
51
115
  setError,
52
116
  setFxRate,
53
117
  setPathname,
@@ -68,8 +132,11 @@ export function useAppEffects({
68
132
  visible?: boolean,
69
133
  reconcileOpenWork?: boolean,
70
134
  refreshProjection?: boolean,
71
- ) => Promise<void>;
135
+ reloadCostSummary?: boolean,
136
+ ) => Promise<boolean>;
72
137
  refreshVersion: MutableRefObject<number>;
138
+ snapshotUpdating: boolean;
139
+ setSnapshotUpdating: Dispatch<SetStateAction<boolean>>;
73
140
  setError: Dispatch<SetStateAction<string | null>>;
74
141
  setFxRate: Dispatch<SetStateAction<Awaited<ReturnType<DashboardApiClient["fxRate"]>> | null>>;
75
142
  setPathname: Dispatch<SetStateAction<string>>;
@@ -77,6 +144,9 @@ export function useAppEffects({
77
144
  }) {
78
145
  const projectionRefreshInFlight = useRef(false);
79
146
  const lastProjectionRefreshAt = useRef(0);
147
+ const observedSnapshotRevision = useRef<string | null>(null);
148
+ const snapshotRevisionRefreshInFlight = useRef(false);
149
+ const pendingSnapshotRevisionRefresh = useRef<string | null>(null);
80
150
  useEffect(() => {
81
151
  void refresh();
82
152
  return () => {
@@ -124,6 +194,115 @@ export function useAppEffects({
124
194
  };
125
195
  }, [refresh]);
126
196
 
197
+ useEffect(() => {
198
+ if (!snapshotUpdating) return;
199
+ let cancelled = false;
200
+ let attempts = 0;
201
+ let timeout: number | null = null;
202
+ const pollReady = async () => {
203
+ if (cancelled || !shouldPollSnapshotReady(true, attempts)) return;
204
+ attempts += 1;
205
+ try {
206
+ const ready = await api.ready();
207
+ if (cancelled) return;
208
+ if (ready.snapshot_state === "current") {
209
+ setSnapshotUpdating(false);
210
+ // Promotion produced a new immutable SQLite generation. Reload its
211
+ // Operations and Cost Monitor summaries by GET; never rebuild or
212
+ // reconcile as part of the readiness poll.
213
+ await refresh(false, false, false, true);
214
+ return;
215
+ }
216
+ } catch {
217
+ // Keep the validated stale seed visible within the bounded poll window.
218
+ }
219
+ if (cancelled) return;
220
+ if (shouldPollSnapshotReady(true, attempts)) {
221
+ timeout = window.setTimeout(() => void pollReady(), snapshotReadyPollMs);
222
+ return;
223
+ }
224
+ // Stop the progress indicator after the bounded window, while retaining
225
+ // an explicit visible warning that the stale snapshot remains on screen.
226
+ setSnapshotUpdating(false);
227
+ setError(snapshotReadyPollExhaustedMessage);
228
+ };
229
+ void pollReady();
230
+ return () => {
231
+ cancelled = true;
232
+ if (timeout !== null) window.clearTimeout(timeout);
233
+ };
234
+ }, [api, refresh, setError, setSnapshotUpdating, snapshotUpdating]);
235
+
236
+ useEffect(() => {
237
+ let cancelled = false;
238
+ let consecutiveFailures = 0;
239
+ let timeout: number | null = null;
240
+
241
+ const refreshChangedRevision = () => {
242
+ if (cancelled || snapshotRevisionRefreshInFlight.current) return;
243
+ const requestedRevision = pendingSnapshotRevisionRefresh.current;
244
+ if (requestedRevision === null) return;
245
+ snapshotRevisionRefreshInFlight.current = true;
246
+ // A committed revision is discovered by GET /ready. Its data reload
247
+ // remains GET-only: it must never start a projection rebuild or
248
+ // lifecycle reconciliation from a tab that happened to stay open.
249
+ void refresh(false, false, false, true)
250
+ .then((succeeded) => {
251
+ const settled = settleSnapshotRevisionRefresh({
252
+ observedRevision: observedSnapshotRevision.current,
253
+ pendingRevision: pendingSnapshotRevisionRefresh.current,
254
+ requestedRevision,
255
+ succeeded,
256
+ });
257
+ // A failed GET leaves the exact revision pending. The following
258
+ // passive readiness poll retries it without changing the UI state.
259
+ observedSnapshotRevision.current = settled.observedRevision;
260
+ pendingSnapshotRevisionRefresh.current = settled.pendingRevision;
261
+ })
262
+ .finally(() => {
263
+ snapshotRevisionRefreshInFlight.current = false;
264
+ // A newer committed revision that arrived while this GET was in
265
+ // flight can run immediately; retrying the same rejected revision
266
+ // waits for the normal passive polling cadence.
267
+ if (pendingSnapshotRevisionRefresh.current !== requestedRevision) {
268
+ refreshChangedRevision();
269
+ }
270
+ });
271
+ };
272
+
273
+ const pollRevision = async () => {
274
+ try {
275
+ const ready = await api.ready();
276
+ if (cancelled) return;
277
+ consecutiveFailures = 0;
278
+ const previousRevision = observedSnapshotRevision.current;
279
+ if (isCommittedSnapshotRevision(ready.snapshot_state, ready.snapshot_revision)) {
280
+ if (previousRevision === null) {
281
+ observedSnapshotRevision.current = ready.snapshot_revision;
282
+ }
283
+ if (shouldRefreshForSnapshotRevision(previousRevision, ready.snapshot_revision)) {
284
+ pendingSnapshotRevisionRefresh.current = ready.snapshot_revision;
285
+ refreshChangedRevision();
286
+ }
287
+ }
288
+ } catch {
289
+ consecutiveFailures += 1;
290
+ }
291
+ if (!cancelled) {
292
+ timeout = window.setTimeout(
293
+ () => void pollRevision(),
294
+ snapshotRevisionPollDelay(document.visibilityState, consecutiveFailures),
295
+ );
296
+ }
297
+ };
298
+
299
+ void pollRevision();
300
+ return () => {
301
+ cancelled = true;
302
+ if (timeout !== null) window.clearTimeout(timeout);
303
+ };
304
+ }, [api, refresh]);
305
+
127
306
  useEffect(() => {
128
307
  if (
129
308
  !shouldHydrateOperationsDetails(
@@ -135,6 +135,8 @@ export interface CostGroupRow {
135
135
  run_count: number;
136
136
  /** Null when a paged source does not provide a factual task attribution. */
137
137
  task_count: number | null;
138
+ /** Distinct factual conversation memberships for the displayed model. */
139
+ conversation_count?: number | null;
138
140
  /** Canonical export fields; task_count remains the active-task alias. */
139
141
  tasksCreated?: number;
140
142
  tasksActive?: number | null;
@@ -143,10 +145,11 @@ export interface CostGroupRow {
143
145
  /** Stable ZAIP identity details for the Agent aggregation only. */
144
146
  persona?: string | null;
145
147
  job?: string | null;
146
- prd_count: number;
147
- spec_count: number;
148
- evidence_count: number;
149
- test_count: number;
148
+ /** Creation-run attribution is unavailable when canonical linkage is absent. */
149
+ prd_count: number | null;
150
+ spec_count: number | null;
151
+ evidence_count: number | null;
152
+ test_count: number | null;
150
153
  event_count: number;
151
154
  total_tokens: number | null;
152
155
  estimated_usd: number | null;
@@ -290,16 +290,22 @@ export function CostMonitorContent({
290
290
  locale={locale}
291
291
  loading={groupPageLoading}
292
292
  rows={groups[key]}
293
- {...(key === "models" || key === "providers"
294
- ? { totals: summaryTotals }
295
- : groupPage?.totals === undefined
293
+ {...(key === "models"
294
+ ? groupPage?.totals === undefined
296
295
  ? {}
297
- : { totals: groupPage.totals })}
296
+ : { totals: groupPage.totals }
297
+ : key === "providers"
298
+ ? { totals: summaryTotals }
299
+ : groupPage?.totals === undefined
300
+ ? {}
301
+ : { totals: groupPage.totals })}
298
302
  {...(key === "agents"
299
303
  ? { showDateTime: false, showAgentIdentity: true }
300
- : key === "models" || key === "providers"
301
- ? { showDateTime: false, showInvocationTasks: true }
302
- : {})}
304
+ : key === "models"
305
+ ? { showDateTime: false, showModelLifecycleDetails: true }
306
+ : key === "providers"
307
+ ? { showDateTime: false, showInvocationTasks: true }
308
+ : {})}
303
309
  />
304
310
  {groupPager}
305
311
  </>
@@ -204,6 +204,17 @@ export function CostMonitorPage({
204
204
  const [detailLoading, setDetailLoading] = useState(false);
205
205
  const [detailError, setDetailError] = useState<string | null>(null);
206
206
  const [detailAttempt, setDetailAttempt] = useState(0);
207
+ // The summary object is replaced only when the host receives a newer
208
+ // snapshot. Keep this revision separate from tabs and filters so the active
209
+ // remote table is reloaded without remounting the Cost Monitor surface.
210
+ const dataRevisionRef = useRef({ data, revision: 0 });
211
+ if (dataRevisionRef.current.data !== data) {
212
+ dataRevisionRef.current = {
213
+ data,
214
+ revision: dataRevisionRef.current.revision + 1,
215
+ };
216
+ }
217
+ const dataRevision = dataRevisionRef.current.revision;
207
218
  const loadDetailRef = useRef(loadDetail);
208
219
  loadDetailRef.current = loadDetail;
209
220
  const text = costCopy[locale];
@@ -267,6 +278,7 @@ export function CostMonitorPage({
267
278
  globalQuery: globalFilters?.query ?? "",
268
279
  filters,
269
280
  limit: groupPageLimit,
281
+ revision: dataRevision,
270
282
  ...(groupView === null ? {} : { load: loadGroupPage }),
271
283
  });
272
284
  const displayedGroupPage = groupPageState.page ?? groupPageState.stalePage;
@@ -285,6 +297,7 @@ export function CostMonitorPage({
285
297
  range: compactPageRange,
286
298
  query: globalFilters?.query,
287
299
  filters,
300
+ revision: dataRevision,
288
301
  });
289
302
  // Server pagination is safe only when every active Run filter is part of the
290
303
  // endpoint contract. Unsupported local text/metadata filters retain the
@@ -309,6 +322,7 @@ export function CostMonitorPage({
309
322
  // must not silently add a date range and bypass the fast all-time page.
310
323
  range: groupPageRange,
311
324
  refresh: lastUpdatedAt,
325
+ revision: dataRevision,
312
326
  load: loadRunPage,
313
327
  });
314
328
  useEffect(