@zaip-ai/cli 3.7.0 → 4.0.0

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 (288) hide show
  1. package/README.md +5 -5
  2. package/dist/commands/init.d.ts.map +1 -1
  3. package/dist/commands/init.js +6 -0
  4. package/dist/commands/init.js.map +1 -1
  5. package/dist/services/adapters.d.ts +1 -1
  6. package/dist/services/adapters.d.ts.map +1 -1
  7. package/dist/services/adapters.js +22 -4
  8. package/dist/services/adapters.js.map +1 -1
  9. package/dist/services/adapters.test.js +24 -0
  10. package/dist/services/adapters.test.js.map +1 -1
  11. package/dist/services/agent-adapters.js +1 -1
  12. package/dist/services/agent-adapters.js.map +1 -1
  13. package/dist/services/backup-gitignore.d.ts +9 -0
  14. package/dist/services/backup-gitignore.d.ts.map +1 -0
  15. package/dist/services/backup-gitignore.js +66 -0
  16. package/dist/services/backup-gitignore.js.map +1 -0
  17. package/dist/services/backup-gitignore.test.d.ts +2 -0
  18. package/dist/services/backup-gitignore.test.d.ts.map +1 -0
  19. package/dist/services/backup-gitignore.test.js +27 -0
  20. package/dist/services/backup-gitignore.test.js.map +1 -0
  21. package/dist/services/codex-conversation-lifecycle-hook.d.ts.map +1 -1
  22. package/dist/services/codex-conversation-lifecycle-hook.js +15 -7
  23. package/dist/services/codex-conversation-lifecycle-hook.js.map +1 -1
  24. package/dist/services/install-agents-template.d.ts.map +1 -1
  25. package/dist/services/install-agents-template.js +43 -127
  26. package/dist/services/install-agents-template.js.map +1 -1
  27. package/dist/services/install-template-policy.d.ts.map +1 -1
  28. package/dist/services/install-template-policy.js +2 -0
  29. package/dist/services/install-template-policy.js.map +1 -1
  30. package/dist/services/legacy-records-migration.d.ts +15 -0
  31. package/dist/services/legacy-records-migration.d.ts.map +1 -0
  32. package/dist/services/legacy-records-migration.js +109 -0
  33. package/dist/services/legacy-records-migration.js.map +1 -0
  34. package/dist/services/local-state-migration.d.ts +8 -1
  35. package/dist/services/local-state-migration.d.ts.map +1 -1
  36. package/dist/services/local-state-migration.js +67 -32
  37. package/dist/services/local-state-migration.js.map +1 -1
  38. package/dist/services/logger.d.ts.map +1 -1
  39. package/dist/services/logger.js +2 -0
  40. package/dist/services/logger.js.map +1 -1
  41. package/dist/services/project-model-migration.d.ts +3 -0
  42. package/dist/services/project-model-migration.d.ts.map +1 -0
  43. package/dist/services/project-model-migration.js +163 -0
  44. package/dist/services/project-model-migration.js.map +1 -0
  45. package/dist/services/project-model-migration.test.d.ts +2 -0
  46. package/dist/services/project-model-migration.test.d.ts.map +1 -0
  47. package/dist/services/project-model-migration.test.js +62 -0
  48. package/dist/services/project-model-migration.test.js.map +1 -0
  49. package/dist/services/sqlite-bootstrap.d.ts.map +1 -1
  50. package/dist/services/sqlite-bootstrap.js +9 -1
  51. package/dist/services/sqlite-bootstrap.js.map +1 -1
  52. package/dist/services/template-manifest.d.ts.map +1 -1
  53. package/dist/services/template-manifest.js +2 -0
  54. package/dist/services/template-manifest.js.map +1 -1
  55. package/dist/services/transactional-files.d.ts +1 -0
  56. package/dist/services/transactional-files.d.ts.map +1 -1
  57. package/dist/services/transactional-files.js +3 -0
  58. package/dist/services/transactional-files.js.map +1 -1
  59. package/dist/types.d.ts +1 -0
  60. package/dist/types.d.ts.map +1 -1
  61. package/package.json +1 -1
  62. package/templates/.agents/README-ENG.md +90 -34
  63. package/templates/.agents/README.md +59 -32
  64. package/templates/.agents/config/agents/catalog.yaml +23 -23
  65. package/templates/.agents/config/agents/discovery.json +2 -2
  66. package/templates/.agents/config/clients/codex/agents/zaip-agent-analyst.toml +2 -2
  67. package/templates/.agents/config/clients/codex/agents/zaip-agent-auditor.toml +2 -2
  68. package/templates/.agents/config/clients/codex/agents/zaip-agent-backend.toml +2 -2
  69. package/templates/.agents/config/clients/codex/agents/zaip-agent-brand-strategist.toml +2 -2
  70. package/templates/.agents/config/clients/codex/agents/zaip-agent-builder.toml +2 -2
  71. package/templates/.agents/config/clients/codex/agents/zaip-agent-cloud-engineer.toml +2 -2
  72. package/templates/.agents/config/clients/codex/agents/zaip-agent-data-analyst.toml +2 -2
  73. package/templates/.agents/config/clients/codex/agents/zaip-agent-designer.toml +2 -2
  74. package/templates/.agents/config/clients/codex/agents/zaip-agent-finalizer.toml +2 -2
  75. package/templates/.agents/config/clients/codex/agents/zaip-agent-frontend.toml +2 -2
  76. package/templates/.agents/config/clients/codex/agents/zaip-agent-general-operations.toml +2 -2
  77. package/templates/.agents/config/clients/codex/agents/zaip-agent-governance-compliance.toml +2 -2
  78. package/templates/.agents/config/clients/codex/agents/zaip-agent-growth-strategist.toml +2 -2
  79. package/templates/.agents/config/clients/codex/agents/zaip-agent-integrator.toml +2 -2
  80. package/templates/.agents/config/clients/codex/agents/zaip-agent-marketing-analyst.toml +2 -2
  81. package/templates/.agents/config/clients/codex/agents/zaip-agent-orchestrator.toml +3 -3
  82. package/templates/.agents/config/clients/codex/agents/zaip-agent-refactoring-specialist.toml +2 -2
  83. package/templates/.agents/config/clients/codex/agents/zaip-agent-reviewer.toml +2 -2
  84. package/templates/.agents/config/clients/codex/agents/zaip-agent-sales-specialist.toml +2 -2
  85. package/templates/.agents/config/clients/codex/agents/zaip-agent-security-auditor.toml +2 -2
  86. package/templates/.agents/config/clients/codex/agents/zaip-agent-task-executor.toml +2 -2
  87. package/templates/.agents/config/clients/codex/agents/zaip-agent-website-developer.toml +2 -2
  88. package/templates/.agents/config/clients/codex/agents/zaip-agent-writer.toml +2 -2
  89. package/templates/.agents/config/clients.yaml +1 -1
  90. package/templates/.agents/config/pricing/models.json +57 -1
  91. package/templates/.agents/config/project.yaml +21 -7
  92. package/templates/.agents/dashboard/.zaip-managed.json +195 -115
  93. package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-C5NgEcVh.js → CostCalculationGuidePage-f4ZGJRg3.js} +1 -1
  94. package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog-BDQR0U4a.js → DashboardHelpDialog-DlN5EMmD.js} +249 -136
  95. package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-CGYcJ5EB.js → PricingSettingsDialog-v9zGRTlk.js} +1 -1
  96. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-BRdoeTWk.js +3 -0
  97. package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-Ckxk_izu.js → TraceabilityWorkspace-B_5u4Xnp.js} +1 -1
  98. package/templates/.agents/dashboard/apps/frontend/dist/assets/{cost-monitor-BsMfGfQi.css → cost-monitor-C2AFYBu_.css} +1 -1
  99. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-D7PrDZy9.js +68 -0
  100. package/templates/.agents/dashboard/apps/frontend/dist/assets/{dashboard-localization-jvdDfNKs.js → dashboard-localization-CF7B1Hjn.js} +90 -34
  101. package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-x8xxL9yW.js → icons-vendor-Cv3WBFIF.js} +1 -1
  102. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-C4rLXrz_.js +2 -0
  103. package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-CNOXIJHN.js → react-vendor-mHgsmxb6.js} +1 -1
  104. package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-CcpbpT1s.js → traceability-B0CPyEVq.js} +1 -1
  105. package/templates/.agents/dashboard/apps/frontend/dist/index.html +6 -6
  106. package/templates/.agents/dashboard/apps/frontend/package.json +1 -1
  107. package/templates/.agents/dashboard/apps/frontend/src/AppDashboardContent.tsx +11 -1
  108. package/templates/.agents/dashboard/apps/frontend/src/AppLoadingBoundary.tsx +3 -2
  109. package/templates/.agents/dashboard/apps/frontend/src/AppLoadingPanels.tsx +1 -1
  110. package/templates/.agents/dashboard/apps/frontend/src/AppOverlays.tsx +1 -1
  111. package/templates/.agents/dashboard/apps/frontend/src/ProjectSettingsPage.tsx +9 -13
  112. package/templates/.agents/dashboard/apps/frontend/src/cost-monitor-population.test.tsx +4 -2
  113. package/templates/.agents/dashboard/apps/frontend/src/cost-period-refresh.test.ts +9 -5
  114. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api-settings.ts +3 -2
  115. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.test.ts +2 -2
  116. package/templates/.agents/dashboard/apps/frontend/src/dashboard-identity.test.ts +3 -3
  117. package/templates/.agents/dashboard/apps/frontend/src/dashboard-identity.ts +1 -1
  118. package/templates/.agents/dashboard/apps/frontend/src/dashboard-localization.test.ts +2 -1
  119. package/templates/.agents/dashboard/apps/frontend/src/operations-period-render.test.ts +40 -3
  120. package/templates/.agents/dashboard/apps/frontend/src/project-settings-model.ts +12 -1
  121. package/templates/.agents/dashboard/apps/frontend/src/project-settings.test.ts +19 -0
  122. package/templates/.agents/dashboard/apps/frontend/src/snapshot-coherence.test.ts +1 -1
  123. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.test.ts +3 -1
  124. package/templates/.agents/dashboard/apps/frontend/src/use-dashboard-app-model.tsx +14 -23
  125. package/templates/.agents/dashboard/modules/cost-monitor/package.json +1 -1
  126. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CompactLifecycleCostTable.test.ts +3 -3
  127. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CompactLifecycleCostTable.tsx +2 -3
  128. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostDeveloperAttributionTable.tsx +2 -6
  129. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostGroupsTable.period-values.test.tsx +66 -0
  130. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostGroupsTable.tsx +29 -34
  131. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorContent.tsx +2 -2
  132. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.embedded.test.ts +11 -0
  133. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.tsx +1 -13
  134. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostPeriodAverageCards.test.tsx +23 -0
  135. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostPeriodAverageCards.tsx +2 -1
  136. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTableDisplay.ts +6 -11
  137. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables-keeps-legacy-codex-identity-fields-blank.test.ts +6 -6
  138. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.test.ts +10 -12
  139. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-group-page-totals.ts +23 -0
  140. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-group-period-values.tsx +34 -0
  141. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-embedded.css +0 -7
  142. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-table-formatters.ts +4 -9
  143. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-table-presentation.ts +2 -3
  144. package/templates/.agents/dashboard/modules/operations/package.json +1 -1
  145. package/templates/.agents/dashboard/modules/operations/src/react/DeveloperAttributionPanel.tsx +2 -12
  146. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.helpers.tsx +2 -2
  147. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.test.ts +1 -6
  148. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.tsx +34 -36
  149. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverviewCharts.tsx +12 -3
  150. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverviewTopPanel.tsx +12 -14
  151. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPage.tsx +4 -4
  152. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPageOverview.tsx +23 -4
  153. package/templates/.agents/dashboard/modules/operations/src/react/OverviewCostKpi.tsx +30 -2
  154. package/templates/.agents/dashboard/modules/operations/src/react/TaskDetailsDialog.tsx +2 -13
  155. package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkBoard.priority.test.ts +8 -1
  156. package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkBoard.tsx +8 -16
  157. package/templates/.agents/dashboard/modules/operations/src/react/WorkColumnState.tsx +34 -0
  158. package/templates/.agents/dashboard/modules/operations/src/react/operation-duration.test.ts +1 -1
  159. package/templates/.agents/dashboard/modules/operations/src/react/operation-duration.ts +1 -1
  160. package/templates/.agents/dashboard/modules/operations/src/react/operations-average-unmeasured.test.ts +10 -0
  161. package/templates/.agents/dashboard/modules/operations/src/react/operations-content-visibility.ts +3 -0
  162. package/templates/.agents/dashboard/modules/operations/src/react/operations-coverage-toast.test.ts +4 -3
  163. package/templates/.agents/dashboard/modules/operations/src/react/operations-overview-period-filter.test.ts +12 -1
  164. package/templates/.agents/dashboard/modules/operations/src/react/operations-page-contract.ts +1 -0
  165. package/templates/.agents/dashboard/modules/operations/src/react/operations-skeleton.css +80 -0
  166. package/templates/.agents/dashboard/modules/operations/src/react/operations.css +2 -11
  167. package/templates/.agents/dashboard/modules/operations/src/react/overview-cost-card.test.tsx +36 -0
  168. package/templates/.agents/dashboard/modules/operations/src/react/overview-metric-format.test.ts +11 -0
  169. package/templates/.agents/dashboard/modules/operations/src/react/overview-metric-format.ts +28 -3
  170. package/templates/.agents/dashboard/modules/operations/src/react/overview-metric-stats.ts +44 -0
  171. package/templates/.agents/dashboard/modules/operations/src/react/work-card-label.ts +5 -0
  172. package/templates/.agents/dashboard/modules/operations/src/react/work-column-state.ts +11 -0
  173. package/templates/.agents/dashboard/modules/operations/src/react/work-item-filter.ts +13 -0
  174. package/templates/.agents/dashboard/modules/traceability/package.json +1 -1
  175. package/templates/.agents/dashboard/packages/branding/package.json +1 -1
  176. package/templates/.agents/dashboard/packages/contracts/package.json +1 -1
  177. package/templates/.agents/dashboard/packages/data-export/package.json +1 -1
  178. package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +173 -173
  179. package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +120 -120
  180. package/templates/.agents/dashboard/packages/local-runtime/package.json +1 -1
  181. package/templates/.agents/dashboard/packages/local-runtime/server.mjs +173 -173
  182. package/templates/.agents/dashboard/packages/local-runtime/src/active-codex-telemetry.test.ts +31 -0
  183. package/templates/.agents/dashboard/packages/local-runtime/src/active-codex-telemetry.ts +74 -1
  184. package/templates/.agents/dashboard/packages/local-runtime/src/agent-catalog-settings.test.ts +15 -15
  185. package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.test.ts +2 -2
  186. package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.ts +1 -1
  187. package/templates/.agents/dashboard/packages/local-runtime/src/server-identity.test.ts +1 -1
  188. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-synchronization.test.ts +25 -3
  189. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-synchronization.ts +51 -16
  190. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot.test.ts +1 -1
  191. package/templates/.agents/dashboard/packages/local-runtime/src/server-ui-security.ts +37 -0
  192. package/templates/.agents/dashboard/packages/local-runtime/src/server-workspace.ts +1 -1
  193. package/templates/.agents/dashboard/packages/local-runtime/src/server.ts +11 -33
  194. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-rebuild.ts +12 -13
  195. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-source-fingerprint.test.ts +71 -1
  196. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-source-fingerprint.ts +141 -25
  197. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-worktree-sources.test.ts +60 -0
  198. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-rebuild-fingerprint-options.ts +13 -0
  199. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.mjs +26 -2
  200. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.test.mjs +5 -0
  201. package/templates/.agents/dashboard/packages/ui/package.json +1 -1
  202. package/templates/.agents/dashboard/packages/ui/src/DashboardHeader.test.mjs +5 -0
  203. package/templates/.agents/dashboard/packages/ui/src/DashboardHeader.tsx +4 -4
  204. package/templates/.agents/dashboard/tests/e2e/package.json +1 -1
  205. package/templates/.agents/dashboard/tests/e2e/playwright.config.ts +4 -0
  206. package/templates/.agents/dashboard/tests/e2e/specs/dashboard-performance-matrix.spec.ts +15 -23
  207. package/templates/.agents/dashboard/tests/e2e/specs/dashboard.spec.ts +1 -1
  208. package/templates/.agents/dashboard/tests/e2e/specs/performance-matrix-types.ts +15 -0
  209. package/templates/.agents/dashboard/tests/e2e/specs/performance-observability.ts +30 -0
  210. package/templates/.agents/dashboard/tests/e2e/specs/performance-work-row-identity.ts +6 -0
  211. package/templates/.agents/design/.zaip-managed.json +13 -9
  212. package/templates/.agents/design/apps/catalog/dist/assets/index-DkpQYoyC.js +72 -0
  213. package/templates/.agents/design/apps/catalog/dist/index.html +1 -1
  214. package/templates/.agents/design/apps/catalog/package.json +1 -1
  215. package/templates/.agents/design/apps/catalog/src/components/CostPopulationSpecimen.test.tsx +8 -4
  216. package/templates/.agents/design/apps/catalog/src/components/CostPopulationSpecimen.tsx +10 -3
  217. package/templates/.agents/design/apps/catalog/src/components/DashboardFiltersSpecimen.tsx +0 -15
  218. package/templates/.agents/design/apps/catalog/src/pages/ComponentsPage.tsx +3 -3
  219. package/templates/.agents/design/apps/catalog/src/stories/DashboardFilters.stories.tsx +0 -1
  220. package/templates/.agents/design/packages/local-runtime/package.json +1 -1
  221. package/templates/.agents/requirements/autonomous-assurance.md +1 -1
  222. package/templates/.agents/requirements/cloud-hostinger-autonomy.md +1 -1
  223. package/templates/.agents/requirements/comparative-audit.md +1 -1
  224. package/templates/.agents/requirements/context-engine.md +1 -1
  225. package/templates/.agents/requirements/cost-observability.md +1 -1
  226. package/templates/.agents/requirements/dashboard-launch.md +1 -1
  227. package/templates/.agents/requirements/data-export.md +1 -1
  228. package/templates/.agents/requirements/design-system.md +1 -1
  229. package/templates/.agents/requirements/execution-engine.md +1 -1
  230. package/templates/.agents/requirements/kanban-dashboard.md +1 -1
  231. package/templates/.agents/requirements/model-orchestration.md +6 -6
  232. package/templates/.agents/requirements/public-distribution.md +1 -1
  233. package/templates/.agents/requirements/software-architecture.md +1 -1
  234. package/templates/.agents/requirements/strategic-improvements.md +1 -1
  235. package/templates/.agents/requirements/work-lifecycle.md +1 -1
  236. package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
  237. package/templates/.agents/rules/autonomy-decision.md +2 -2
  238. package/templates/.agents/rules/workflow.md +1 -1
  239. package/templates/.agents/runtime/VERSION +1 -1
  240. package/templates/.agents/runtime/bin/zaip.mjs +73 -64
  241. package/templates/.agents/runtime/checksums.sha256 +5 -5
  242. package/templates/.agents/runtime/manifest.json +1 -1
  243. package/templates/.agents/runtime/provenance.json +4 -4
  244. package/templates/.agents/runtime/sbom.cdx.json +1 -1
  245. package/templates/.agents/skills/zaip-skill-adapters/SKILL.md +1 -1
  246. package/templates/.agents/skills/zaip-skill-executar-fila-pronta/SKILL.md +7 -7
  247. package/templates/.agents/skills/zaip-skill-executar-fila-pronta/scripts/ready-queue-fence.mjs +32 -7
  248. package/templates/.agents/skills/zaip-skill-executar-fila-pronta/scripts/ready-queue-fence.test.mjs +134 -1
  249. package/templates/.agents/skills/zaip-skill-executar-fila-pronta/scripts/ready-queue-lease.mjs +57 -0
  250. package/templates/.agents/skills/zaip-skill-executar-fila-pronta/scripts/scheduled-ready-queue-contract.test.mjs +9 -6
  251. package/templates/.agents/skills/zaip-skill-execution/SKILL.md +1 -1
  252. package/templates/.agents/skills/zaip-skill-execution/references/runtime-governance.md +2 -2
  253. package/templates/.agents/skills/zaip-skill-github-deploy/SKILL.md +5 -5
  254. package/templates/.agents/skills/zaip-skill-github-deploy/agents/openai.yaml +2 -2
  255. package/templates/.agents/skills/zaip-skill-github-deploy/references/deployment-workflow.md +5 -3
  256. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/github-deploy-contract.test.mjs +40 -5
  257. package/templates/.agents/skills/zaip-skill-instalar-agendamento/SKILL.md +2 -2
  258. package/templates/.agents/skills/zaip-skill-release-qualification/SKILL.md +6 -0
  259. package/templates/.agents/skills/zaip-skill-release-qualification/references/local-preparation.md +54 -6
  260. package/templates/.agents/zaip-aios/README.md +4 -4
  261. package/templates/.agents/zaip-aios/VERSION +1 -1
  262. package/templates/.agents/zaip-aios/guides/01-primeiros-passos.md +4 -3
  263. package/templates/.agents/zaip-aios/guides/03-tasks.md +5 -4
  264. package/templates/.agents/zaip-aios/guides/04-specs.md +4 -1
  265. package/templates/.agents/zaip-aios/guides/05-prds.md +7 -3
  266. package/templates/.agents/zaip-aios/guides/06-execucao.md +9 -4
  267. package/templates/.agents/zaip-aios/guides/09-agentes-e-squads.md +9 -5
  268. package/templates/.agents/zaip-aios/guides/11-observabilidade-e-custos.md +10 -2
  269. package/templates/.agents/zaip-aios/guides/12-instalacao-e-upgrade.md +10 -5
  270. package/templates/.agents/zaip-aios/guides/13-boas-praticas.md +12 -1
  271. package/templates/.agents/zaip-aios/guides/14-troubleshooting.md +3 -1
  272. package/templates/.agents/zaip-aios/guides/16-lifecycle-e-reconciliacao.md +12 -7
  273. package/templates/.agents/zaip-aios/guides/17-como-o-custo-e-calculado.md +1 -1
  274. package/templates/.agents/zaip-aios/guides/18-catalogo-de-agentes.md +5 -1
  275. package/templates/.agents/zaip-aios/guides/19-assurance-e-autonomia.md +19 -15
  276. package/templates/.agents/zaip-aios/guides/20-instalacao-detalhada.md +13 -9
  277. package/templates/.agents/zaip-aios/guides/21-telemetria-monitor-custos.md +4 -3
  278. package/templates/.agents/zaip-aios/guides/22-fluxos-e-roteamento.md +9 -7
  279. package/templates/.agents/zaip-aios/guides/23-orquestracao-de-modelos.md +40 -25
  280. package/templates/.agents/zaip-aios/guides/24-calculo-de-custos-openai.md +8 -8
  281. package/templates/.agents/zaip-aios/guides/26-qualidade-e-release.md +14 -5
  282. package/templates/.agents/zaip-aios/guides/27-telemetria-git-native.md +6 -0
  283. package/templates/.agents/zaip-aios/guides/28-lifecycle-completo.md +129 -11
  284. package/templates/template-manifest.json +310 -218
  285. package/templates/tsconfig.base.json +22 -0
  286. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-OulA4CoX.js +0 -3
  287. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-j3aUYH5G.js +0 -68
  288. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-CqfSgtk9.js +0 -2
@@ -1,7 +1,6 @@
1
1
  import type { CostMonitorProjection } from "@zaip-ai/dashboard-cost-monitor/contracts";
2
2
  import type { CostCurrency, CostLocale } from "@zaip-ai/dashboard-cost-monitor/react";
3
3
  import type { OperationsProjection } from "@zaip-ai/dashboard-operations/contracts";
4
- import * as React from "react";
5
4
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
6
5
  import type { LoadingArea } from "./AppLoadingPanels.js";
7
6
  import {
@@ -136,9 +135,9 @@ export function useDashboardAppModel() {
136
135
  const [fxRate, setFxRate] = useState<FxRate | null>(null);
137
136
  const [pathname, setPathname] = useState(window.location.pathname);
138
137
  const [operationsLoading, setOperationsLoading] = useState(true);
139
- const [, setOperationsSurfaceLoading] = useState(false);
138
+ const [operationsSurfaceLoading, setOperationsSurfaceLoading] = useState(false);
140
139
  const [, setCostSurfaceLoading] = useState(false);
141
- const [periodTransitionPending, setPeriodTransitionPending] = useState(false);
140
+ const [, setPeriodTransitionPending] = useState(false);
142
141
  const [loadingArea, setLoadingArea] = useState<LoadingArea | null>("operations");
143
142
  const [failedArea, setFailedArea] = useState<LoadingArea | null>(null);
144
143
  const [refreshDialog, setRefreshDialog] = useState<
@@ -307,8 +306,8 @@ export function useDashboardAppModel() {
307
306
  setLastUpdatedAt(projection.summary.latest_token_collected_at ?? null);
308
307
  }
309
308
  } else if (refreshProjection) {
310
- let snapshot: Awaited<ReturnType<typeof api.refreshProjection>>;
311
- snapshot = await api.refreshProjection(currency, period);
309
+ const snapshot: Awaited<ReturnType<typeof api.refreshProjection>> =
310
+ await api.refreshProjection(currency, period);
312
311
  if (!isCurrentRequest()) return false;
313
312
  setOperations(snapshot.operations);
314
313
  quickOverviewMetricsRef.current = snapshot.operations.summary.overview_metrics;
@@ -582,16 +581,11 @@ export function useDashboardAppModel() {
582
581
  void loadCostSummary();
583
582
  }, [costs?.currency, currency, loadCostSummary]);
584
583
 
585
- // A global period change invalidates the Cost snapshot before React commits
586
- // the replacement view. All Time also needs its factual Cost range while
587
- // Operations is active, so its disabled date inputs do not fall back to
588
- // today's Operations range. The request itself remains deliberately
589
- // unbounded. The in-flight guard prevents duplicate requests.
584
+ // Hydrate factual cost independently after the compact Operations fold. The
585
+ // cost cards retain skeletons while this narrower request is in flight;
586
+ // unrelated metrics and the board can render without waiting for it.
590
587
  useEffect(() => {
591
- if (
592
- (dashboardView !== "cost-monitor" && operationsPeriod.preset !== "all") ||
593
- costSummaryAbortRef.current !== null
594
- )
588
+ if (operations === null || failedArea === "costs" || costSummaryAbortRef.current !== null)
595
589
  return;
596
590
  if (
597
591
  shouldLoadCostSummary({
@@ -601,7 +595,7 @@ export function useDashboardAppModel() {
601
595
  })
602
596
  )
603
597
  void loadCostSummary();
604
- }, [costs?.currency, currency, dashboardView, loadCostSummary, operationsPeriod.preset]);
598
+ }, [costs?.currency, currency, failedArea, loadCostSummary, operations]);
605
599
 
606
600
  const selectDashboardView = createDashboardViewSelector({
607
601
  setDashboardView,
@@ -648,12 +642,9 @@ export function useDashboardAppModel() {
648
642
  setFxRate,
649
643
  setPricing,
650
644
  );
651
- // A request is not an empty dashboard state. One accessible global gate
652
- // prevents every tab from briefly presenting zero/empty placeholders while
653
- // its coherent replacement snapshot or page is still in flight.
654
- // The stale snapshot is useful only while the initial screen still waits
655
- // for data. Once Operations has arrived, the normal dashboard is the
656
- // clearest confirmation that it opened; do not leave a duplicate toast.
645
+ // Operations uses independent skeleton cards and a loading toast while a
646
+ // period changes. Only the cross-currency first fold still needs the global
647
+ // gate to avoid formatting BRL values as USD before Cost Monitor responds.
657
648
  return {
658
649
  loadMoreBoard,
659
650
  overviewStatusPageKey,
@@ -671,8 +662,7 @@ export function useDashboardAppModel() {
671
662
  setOperationsLoading,
672
663
  operations,
673
664
  error,
674
- globalLoadingPending:
675
- periodTransitionPending || (currency === "USD" && costs?.currency !== currency),
665
+ globalLoadingPending: currency === "USD" && costs?.currency !== currency,
676
666
  costLocale,
677
667
  refresh: async () => {
678
668
  await refresh();
@@ -727,6 +717,7 @@ export function useDashboardAppModel() {
727
717
  factualCostFact,
728
718
  factualCostPeriods,
729
719
  setOperationsSurfaceLoading,
720
+ operationsSurfaceLoading,
730
721
  currentCostByRun,
731
722
  loadOperationsEntityPage,
732
723
  loadOperationsLifecyclePage,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zaip-ai/dashboard-cost-monitor",
3
- "version": "3.7.0",
3
+ "version": "4.0.0",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "exports": {
@@ -63,8 +63,8 @@ describe("CompactLifecycleCostTable filtered subtotal", () => {
63
63
  };
64
64
  const render = (key: string) =>
65
65
  table!.props.columns.find((column) => column.key === key)?.render?.(unknownRow);
66
- expect(render("duration")).toBe("Indisponível");
67
- expect(render("cost")).toBe("Indisponível");
66
+ expect(render("duration")).toBe("0");
67
+ expect(render("cost")).toBe("0");
68
68
  expect(render("tokens")).toBe("15");
69
69
  const partialRow = {
70
70
  metrics: {
@@ -113,7 +113,7 @@ describe("CompactLifecycleCostTable filtered subtotal", () => {
113
113
  table!.props.columns.find((column) => column.key === key)?.footer;
114
114
  expect(footer("runs")).toBe("0");
115
115
  expect(footer("tokens")).toBe("0");
116
- expect(footer("duration")).toBe("Indisponível");
116
+ expect(footer("duration")).toBe("0");
117
117
  });
118
118
 
119
119
  it("does not render a subtotal row when all lifecycle metrics are unavailable", () => {
@@ -82,8 +82,7 @@ export function CompactLifecycleCostTable({
82
82
  onOpenTrace?: (id: string) => void;
83
83
  }) {
84
84
  const metric = (value: KnownMetric, format: (known: number) => string) => {
85
- if (value.known_sum === 0 && !value.complete && value.coverage_ratio === 0)
86
- return locale === "pt-BR" ? "Indisponível" : "Unavailable";
85
+ if (value.known_sum === 0 && !value.complete && value.coverage_ratio === 0) return "0";
87
86
  const formatted = format(value.known_sum ?? 0);
88
87
  return value.complete
89
88
  ? formatted
@@ -91,7 +90,7 @@ export function CompactLifecycleCostTable({
91
90
  };
92
91
  const durationMetric = (row: CompactLifecycleCostRow) => {
93
92
  const effective = compactLifecycleDurationMs(row);
94
- if (effective === null) return locale === "pt-BR" ? "Indisponível" : "Unavailable";
93
+ if (effective === null) return "0";
95
94
  return metric(row.metrics.duration, (value) =>
96
95
  value === 0 ? "0" : formatDurationMs(value, locale),
97
96
  );
@@ -40,7 +40,7 @@ function formatCount(value: number | null, locale: CostLocale): string {
40
40
  }
41
41
 
42
42
  function formatDuration(value: number | null, locale: CostLocale): string {
43
- if (value === null) return locale === "pt-BR" ? "Indisponível" : "Unavailable";
43
+ if (value === null) return "0";
44
44
  return value === 0 ? "0" : formatDurationMs(value, locale);
45
45
  }
46
46
 
@@ -49,11 +49,7 @@ function formatBrlCurrency(value: number | null, locale: CostLocale): string {
49
49
  }
50
50
 
51
51
  function formatTokens(value: number | null, locale: CostLocale): string {
52
- return value === null
53
- ? locale === "pt-BR"
54
- ? "Indisponível"
55
- : "Unavailable"
56
- : formatCompactNumber(value, locale);
52
+ return formatCompactNumber(value, locale);
57
53
  }
58
54
 
59
55
  function partialMetric(
@@ -0,0 +1,66 @@
1
+ import type { ReactElement } from "react";
2
+ import { describe, expect, it } from "vitest";
3
+ import { CostGroupsTable, formatAuditableCurrency } from "./CostTables.js";
4
+
5
+ const unavailableGroup = {
6
+ key: "unknown",
7
+ label: "Sem telemetria",
8
+ latest_started_at: "2026-07-24T20:00:00.000Z",
9
+ run_count: 2,
10
+ task_count: 1,
11
+ prd_count: 0,
12
+ spec_count: 0,
13
+ evidence_count: 0,
14
+ test_count: 0,
15
+ event_count: 2,
16
+ total_tokens: null,
17
+ estimated_usd: null,
18
+ estimated_brl: null,
19
+ duration_ms: null,
20
+ duration: { known_sum: 0, unknown: 2, complete: false, coverage_ratio: 0 },
21
+ coverage: { events: 2, tokens: 0, usd: 0, brl: 0 },
22
+ } as const;
23
+
24
+ describe("period group values", () => {
25
+ it("discloses presentation zero without changing null sorting", () => {
26
+ const table = CostGroupsTable({
27
+ rows: [unavailableGroup],
28
+ currency: "BRL",
29
+ locale: "pt-BR",
30
+ period: "daily",
31
+ });
32
+ const columns = table.props.columns as Array<{
33
+ key: string;
34
+ render: (row: typeof unavailableGroup) => string | ReactElement;
35
+ sortValue?: (row: typeof unavailableGroup) => unknown;
36
+ footer?: ReactElement<{ "aria-label"?: string }>;
37
+ }>;
38
+ const column = (key: string) => columns.find((candidate) => candidate.key === key)!;
39
+ const duration = column("duration").render(unavailableGroup) as ReactElement<{
40
+ title: string;
41
+ children: string;
42
+ }>;
43
+ const tokens = column("tokens").render(unavailableGroup) as ReactElement<{
44
+ title: string;
45
+ children: string;
46
+ }>;
47
+ const cost = column("cost").render(unavailableGroup) as ReactElement<{
48
+ title: string;
49
+ children: string;
50
+ }>;
51
+ expect(duration.props.title).toBe(
52
+ "Zero exibido por falta de medição; não é um valor observado.",
53
+ );
54
+ expect([duration.props.children, tokens.props.children, cost.props.children]).toEqual([
55
+ "0",
56
+ "0",
57
+ formatAuditableCurrency(0, "BRL", "pt-BR"),
58
+ ]);
59
+ expect(
60
+ [column("duration"), column("tokens"), column("cost")].map((entry) =>
61
+ entry.sortValue?.(unavailableGroup),
62
+ ),
63
+ ).toEqual([null, null, null]);
64
+ expect(column("duration").footer?.props["aria-label"]).not.toContain("Indisponível");
65
+ });
66
+ });
@@ -1,9 +1,11 @@
1
1
  import { DataTable } from "@zaip-ai/dashboard-ui";
2
- import type { CostGroupRow, DurationCoverage } from "../contracts.js";
2
+ import type { CostGroupRow } from "../contracts.js";
3
3
  import { MoreVerticalIcon } from "./CostTableIcons.js";
4
4
  import { costPeriodHeading } from "./CostTables.js";
5
+ import { costGroupConversationCount, costGroupPageTotals } from "./cost-group-page-totals.js";
6
+ import { createCostGroupPeriodValues } from "./cost-group-period-values.js";
5
7
  import type { CostCurrency, CostLocale } from "./cost-monitor-copy.js";
6
- import { localizedDescription, sumNumbers } from "./cost-table-metrics.js";
8
+ import { localizedDescription } from "./cost-table-metrics.js";
7
9
  import {
8
10
  costGroupsScrollLabel,
9
11
  costPeriodTotalAriaLabel,
@@ -47,8 +49,6 @@ export {
47
49
  import {
48
50
  displayCostGroupLabel,
49
51
  formatAuditableCompactNumber,
50
- formatAuditableCurrency,
51
- formatDurationCoverage,
52
52
  formatFactualCount,
53
53
  hasKnownDuration,
54
54
  sortCostValue,
@@ -102,25 +102,10 @@ export function CostGroupsTable({
102
102
  showAgentIdentity || showModelLifecycleDetails ? (value ?? 0) : value,
103
103
  locale,
104
104
  );
105
- const displayDuration = (value: DurationCoverage) => formatDurationCoverage(value, locale);
106
- const displayCurrency = (value: number | null) =>
107
- formatAuditableCurrency(value, currency, locale);
108
- const conversationCount = (row: unknown): number | null =>
109
- (row as { conversation_count?: number | null }).conversation_count ?? null;
110
- const pageTotals = {
111
- runs: rows.reduce((sum, row) => sum + row.run_count, 0),
112
- tasks: sumNumbers(rows.map((row) => row.task_count)),
113
- prds: sumNumbers(rows.map((row) => row.prd_count)),
114
- specs: sumNumbers(rows.map((row) => row.spec_count)),
115
- evidences: sumNumbers(rows.map((row) => row.evidence_count)),
116
- tests: sumNumbers(rows.map((row) => row.test_count)),
117
- conversations: sumNumbers(rows.map(conversationCount)),
118
- durationKnownMs: rows.reduce((sum, row) => sum + row.duration.known_sum, 0),
119
- durationPartial: rows.some((row) => !row.duration.complete),
120
- tokens: sumNumbers(rows.map((row) => row.total_tokens)),
121
- usd: sumNumbers(rows.map((row) => row.estimated_usd)),
122
- brl: sumNumbers(rows.map((row) => row.estimated_brl)),
123
- };
105
+ const periodValues = createCostGroupPeriodValues(currency, locale, period);
106
+ const periodDescription =
107
+ periodValues.description === undefined ? {} : { description: periodValues.description };
108
+ const pageTotals = costGroupPageTotals(rows);
124
109
  const totals =
125
110
  suppliedTotals === undefined
126
111
  ? pageTotals
@@ -131,14 +116,14 @@ export function CostGroupsTable({
131
116
  specs: suppliedTotals.spec_count,
132
117
  evidences: suppliedTotals.evidence_count,
133
118
  tests: suppliedTotals.test_count,
134
- conversations: conversationCount(suppliedTotals) ?? pageTotals.conversations,
119
+ conversations: costGroupConversationCount(suppliedTotals) ?? pageTotals.conversations,
135
120
  durationKnownMs: suppliedTotals.duration.known_sum,
136
121
  durationPartial: !suppliedTotals.duration.complete,
137
122
  tokens: suppliedTotals.total_tokens,
138
123
  usd: suppliedTotals.estimated_usd,
139
124
  brl: suppliedTotals.estimated_brl,
140
125
  };
141
- const totalDurationDisplay = displayDuration(
126
+ const totalDurationDisplay = periodValues.duration(
142
127
  suppliedTotals?.duration ?? {
143
128
  known_sum: totals.durationKnownMs,
144
129
  unknown: totals.durationPartial ? 1 : 0,
@@ -152,7 +137,7 @@ export function CostGroupsTable({
152
137
  showModelLifecycleDetails ? (totals.tokens ?? 0) : totals.tokens,
153
138
  locale,
154
139
  );
155
- const totalCostDisplay = displayCurrency(currency === "BRL" ? totals.brl : totals.usd);
140
+ const totalCostDisplay = periodValues.currency(currency === "BRL" ? totals.brl : totals.usd);
156
141
  const totalAriaLabel = costPeriodTotalAriaLabel({
157
142
  locale,
158
143
  duration: totalDurationDisplay,
@@ -294,8 +279,8 @@ export function CostGroupsTable({
294
279
  {
295
280
  key: "conversations",
296
281
  label: "Conversations",
297
- render: (row: CostGroupRow) => displayCount(conversationCount(row)),
298
- sortValue: (row: CostGroupRow) => conversationCount(row) ?? 0,
282
+ render: (row: CostGroupRow) => displayCount(costGroupConversationCount(row)),
283
+ sortValue: (row: CostGroupRow) => costGroupConversationCount(row) ?? 0,
299
284
  align: "right" as const,
300
285
  footer: displayCount(totals.conversations),
301
286
  description: localizedDescription(
@@ -355,7 +340,10 @@ export function CostGroupsTable({
355
340
  {
356
341
  key: "duration",
357
342
  label: "Tempo",
358
- render: (row) => displayDuration(row.duration),
343
+ render: (row) =>
344
+ period !== undefined && !hasKnownDuration(row.duration)
345
+ ? periodValues.disclose("0", true)
346
+ : periodValues.duration(row.duration),
359
347
  sortValue: (row) =>
360
348
  hasKnownDuration(row.duration)
361
349
  ? row.duration.known_sum
@@ -364,19 +352,23 @@ export function CostGroupsTable({
364
352
  : null,
365
353
  align: "right",
366
354
  footer: <output aria-label={totalAriaLabel}>{totalDurationDisplay}</output>,
355
+ ...periodDescription,
367
356
  },
368
357
  {
369
358
  key: "tokens",
370
359
  label: "Tokens",
371
- render: (row) =>
372
- formatAuditableCompactNumber(
360
+ render: (row) => {
361
+ const displayed = formatAuditableCompactNumber(
373
362
  showModelLifecycleDetails ? (row.total_tokens ?? 0) : row.total_tokens,
374
363
  locale,
375
- ),
364
+ );
365
+ return periodValues.disclose(displayed, row.total_tokens === null);
366
+ },
376
367
  sortValue: (row) =>
377
368
  showModelLifecycleDetails ? (row.total_tokens ?? 0) : row.total_tokens,
378
369
  align: "right",
379
370
  footer: <output aria-label={totalAriaLabel}>{totalTokensDisplay}</output>,
371
+ ...periodDescription,
380
372
  },
381
373
  {
382
374
  key: "cost",
@@ -392,14 +384,17 @@ export function CostGroupsTable({
392
384
  : locale === "pt-BR"
393
385
  ? "Custos"
394
386
  : "Costs",
395
- render: (row) =>
396
- displayCurrency(currency === "BRL" ? row.estimated_brl : row.estimated_usd),
387
+ render: (row) => {
388
+ const observed = currency === "BRL" ? row.estimated_brl : row.estimated_usd;
389
+ return periodValues.disclose(periodValues.currency(observed), observed === null);
390
+ },
397
391
  sortValue: (row) =>
398
392
  showModelLifecycleDetails
399
393
  ? (sortCostValue(currency, row.estimated_brl, row.estimated_usd) ?? 0)
400
394
  : sortCostValue(currency, row.estimated_brl, row.estimated_usd),
401
395
  align: "right",
402
396
  footer: <output aria-label={totalAriaLabel}>{totalCostDisplay}</output>,
397
+ ...periodDescription,
403
398
  },
404
399
  ...(onOpenTrace
405
400
  ? [
@@ -264,7 +264,7 @@ export function CostMonitorContent({
264
264
  ) : null}
265
265
  {(["providers", "agents", "models"] as const).map((key) =>
266
266
  view === key && !dimensionalDetailPending && !detailLoading && detailError === null ? (
267
- <>
267
+ <div key={key}>
268
268
  <CostGroupsTable
269
269
  currency={currency}
270
270
  key={key}
@@ -294,7 +294,7 @@ export function CostMonitorContent({
294
294
  : {})}
295
295
  />
296
296
  {groupPager}
297
- </>
297
+ </div>
298
298
  ) : null,
299
299
  )}
300
300
  {view === "daily" || view === "monthly" || view === "annual" ? (
@@ -0,0 +1,11 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { describe, expect, it } from "vitest";
3
+
4
+ describe("embedded Cost Monitor", () => {
5
+ it("keeps global filters as the sole embedded filter surface", () => {
6
+ const page = readFileSync(new URL("./CostMonitorPage.tsx", import.meta.url), "utf8");
7
+ expect(page).not.toContain('className="cost-embedded-filters"');
8
+ expect(page).toContain("<CostMonitorContent");
9
+ expect(page).toContain("<CostFilters");
10
+ });
11
+ });
@@ -83,7 +83,7 @@ export {
83
83
  export function CostMonitorPage({
84
84
  capabilityNavigation,
85
85
  data,
86
- dashboardTitle = "Dashboard Zaip AIOS",
86
+ dashboardTitle = "Dashboard ZAIP AIOS",
87
87
  embedded = false,
88
88
  globalFilters,
89
89
  lastUpdatedAt,
@@ -708,18 +708,6 @@ export function CostMonitorPage({
708
708
  </div>
709
709
  ) : null}
710
710
  </div>{" "}
711
- {embedded && view !== "developers" ? (
712
- <details className="cost-embedded-filters">
713
- <summary>{locale === "pt-BR" ? "Filtros de custos" : "Cost filters"}</summary>
714
- <CostFilters
715
- available={data.filters_available}
716
- hideQuery
717
- locale={locale}
718
- onChange={setFilters}
719
- value={filters}
720
- />
721
- </details>
722
- ) : null}
723
711
  {/* CostMonitorContent owns `view === "models" && useAggregateGroups ? (` and `rows={groups.models}`. */}{" "}
724
712
  <CostMonitorContent
725
713
  canonicalDurationTotal={canonicalDurationTotal}
@@ -0,0 +1,23 @@
1
+ import type { ReactElement } from "react";
2
+ import { describe, expect, it } from "vitest";
3
+ import { CostPeriodAverageCards } from "./CostPeriodAverageCards.js";
4
+
5
+ describe("CostPeriodAverageCards", () => {
6
+ const props = { currency: "BRL", locale: "pt-BR", period: "daily", rows: [] } as const;
7
+
8
+ it("shows zero for loaded missing numeric averages without changing the source", () => {
9
+ const element = CostPeriodAverageCards({ ...props, average: null }) as ReactElement<{
10
+ metrics: readonly { value: string }[];
11
+ }>;
12
+ expect(element.props.metrics.map((metric) => metric.value)).toEqual(Array(6).fill("0"));
13
+ });
14
+
15
+ it("keeps loading distinct from a loaded zero", () => {
16
+ const element = CostPeriodAverageCards({ ...props, loading: true }) as ReactElement<{
17
+ role: string;
18
+ children: string;
19
+ }>;
20
+ expect(element.props.role).toBe("status");
21
+ expect(element.props.children).toContain("Carregando médias");
22
+ });
23
+ });
@@ -39,7 +39,8 @@ export function CostPeriodAverageCards({
39
39
  tokens: null,
40
40
  cost: null,
41
41
  });
42
- const unavailable = locale === "pt-BR" ? "Indisponível" : "Unavailable";
42
+ // A loaded missing metric is presented as zero; the API retains null/coverage.
43
+ const unavailable = "0";
43
44
  return (
44
45
  <PeriodAverageCards
45
46
  ariaLabel={periodAverageAriaLabel(period, locale)}
@@ -22,11 +22,7 @@ export function formatAuditableNumber(value: number | null, locale: CostLocale):
22
22
  }
23
23
 
24
24
  export function formatFactualCount(value: number | null, locale: CostLocale): string {
25
- return value === null
26
- ? locale === "pt-BR"
27
- ? "Indisponível"
28
- : "Unavailable"
29
- : formatAuditableNumber(value, locale);
25
+ return formatAuditableNumber(value, locale);
30
26
  }
31
27
 
32
28
  export function formatAuditableCompactNumber(value: number | null, locale: CostLocale): string {
@@ -38,12 +34,12 @@ export function formatAuditableCurrency(
38
34
  currency: CostCurrency,
39
35
  locale: CostLocale,
40
36
  ): string {
41
- if (value === null) return locale === "pt-BR" ? "Indisponível" : "Unavailable";
37
+ if (value === null) return "0";
42
38
  return formatCurrency(value, currency, locale);
43
39
  }
44
40
 
45
41
  export function formatAuditableDuration(value: number | null, locale: CostLocale): string {
46
- if (value === null) return locale === "pt-BR" ? "Indisponível" : "Unavailable";
42
+ if (value === null) return "0";
47
43
  return value === 0 ? "0" : formatDurationMs(value, locale);
48
44
  }
49
45
 
@@ -71,8 +67,8 @@ export const formatInvocationMultiplierPair = (
71
67
  export function invocationMetricDescription(locale: CostLocale, metric: string): string {
72
68
  const source =
73
69
  locale === "pt-BR"
74
- ? "Valor da projeção SQLite/API desta Invocação de Agente. Fato canônico nulo permanece indisponível e não é convertido em zero."
75
- : "Value from this Agent Invocation's SQLite/API projection. A null canonical fact remains unavailable and is not converted to zero.";
70
+ ? "Valor da projeção SQLite/API desta Invocação de Agente. O zero exibido para um fato não medido não altera o valor canônico nulo."
71
+ : "Value from this Agent Invocation's SQLite/API projection. A displayed zero for an unmeasured fact does not change its canonical null value.";
76
72
  const details: Record<string, readonly [string, string]> = {
77
73
  duration: [
78
74
  "Tempo factual da invocação, quando capturado ou reconciliado.",
@@ -171,8 +167,7 @@ export function invocationMetricDescription(locale: CostLocale, metric: string):
171
167
  }
172
168
 
173
169
  export function formatDurationCoverage(value: DurationCoverage, locale: CostLocale): string {
174
- if (value.known_sum <= 0 && !value.complete)
175
- return locale === "pt-BR" ? "Indisponível" : "Unavailable";
170
+ if (value.known_sum <= 0 && !value.complete) return "0";
176
171
  return value.known_sum <= 0 ? "0" : formatDurationMs(value.known_sum, locale);
177
172
  }
178
173
 
@@ -176,10 +176,10 @@ describe("grouped cost availability", () => {
176
176
  ...model,
177
177
  duration: { known_sum: 0, unknown: 1, complete: false, coverage_ratio: 0 },
178
178
  }),
179
- ).toBe("Indisponível");
179
+ ).toBe("0");
180
180
  expect(
181
181
  columns.find((column) => column.key === "cost")?.render?.({ ...model, estimated_brl: null }),
182
- ).toBe("Indisponível");
182
+ ).toBe("0");
183
183
  expect(
184
184
  columns
185
185
  .find((column) => column.key === "tokens")
@@ -324,14 +324,14 @@ describe("grouped cost availability", () => {
324
324
  expect(column(key)?.description).toContain("projeção SQLite/API");
325
325
  expect(column(key)?.hidden).not.toBe(true);
326
326
  }
327
- expect(column("duration")?.render?.(unavailableRun)).toBe("Indisponível");
327
+ expect(column("duration")?.render?.(unavailableRun)).toBe("0");
328
328
  expect(column("inputTokens")?.render?.(unavailableRun)).toBe("0");
329
329
  expect(column("inputRate")?.render?.(unavailableRun)).toBe("0,00");
330
- expect(column("contextMultiplier")?.render?.(unavailableRun)).toBe("Indisponível");
330
+ expect(column("contextMultiplier")?.render?.(unavailableRun)).toBe("0");
331
331
  expect(column("inputCost")?.render?.(unavailableRun)).toContain("0,00");
332
332
  expect(column("fxRate")?.render?.(unavailableRun)).toBe("R$ 0,00");
333
- expect(column("recordedBrl")?.render?.(unavailableRun)).toBe("Indisponível");
334
- expect(column("cost")?.render?.(unavailableRun)).toBe("Indisponível");
333
+ expect(column("recordedBrl")?.render?.(unavailableRun)).toBe("0");
334
+ expect(column("cost")?.render?.(unavailableRun)).toBe("0");
335
335
  });
336
336
  it("provides a clear scroll label for the detailed Runs table", () => {
337
337
  expect(costRunsScrollLabel("pt-BR")).toBe(
@@ -107,11 +107,11 @@ describe("monetary formatting", () => {
107
107
  expect(ratio(106.73, 19.583444)).toBeCloseTo(5.45, 2);
108
108
  expect(ratio(100, 0)).toBeNull();
109
109
  });
110
- it("keeps unavailable cost metrics distinct from zero", () => {
110
+ it("shows zero for unmeasured metrics without changing their source values", () => {
111
111
  expect(formatRate(null, "pt-BR")).toBe("0,00");
112
112
  expect(formatUsd(null, "en-US")).toContain("0.00");
113
- expect(formatAuditableCurrency(null, "BRL", "pt-BR")).toBe("Indisponível");
114
- expect(formatAuditableDuration(null, "pt-BR")).toBe("Indisponível");
113
+ expect(formatAuditableCurrency(null, "BRL", "pt-BR")).toBe("0");
114
+ expect(formatAuditableDuration(null, "pt-BR")).toBe("0");
115
115
  expect(formatAuditableCompactNumber(null, "pt-BR")).toBe("0");
116
116
  });
117
117
  });
@@ -134,7 +134,7 @@ describe("duration coverage", () => {
134
134
  { known_sum: 0, unknown: 2, complete: false, coverage_ratio: 0 },
135
135
  "pt-BR",
136
136
  ),
137
- ).toBe("Indisponível");
137
+ ).toBe("0");
138
138
  });
139
139
  it("does not treat financial tokens as elapsed-time evidence", () => {
140
140
  expect(
@@ -242,9 +242,7 @@ describe("grouped cost availability", () => {
242
242
  ]);
243
243
  expect(columns.find((column) => column.key === "tests")?.render(unavailable)).toBe("—");
244
244
  expect(columns.find((column) => column.key === "evidences")?.render(unavailable)).toBe("—");
245
- expect(columns.find((column) => column.key === "duration")?.render(unavailable)).toBe(
246
- "Indisponível",
247
- );
245
+ expect(columns.find((column) => column.key === "duration")?.render(unavailable)).toBe("0");
248
246
  });
249
247
  it("marks incomplete aggregates in place without adding source or coverage columns", () => {
250
248
  const partial = {
@@ -349,13 +347,13 @@ describe("grouped cost availability", () => {
349
347
  }>;
350
348
  const column = (key: string) => columns.find((candidate) => candidate.key === key);
351
349
  expect(table.props.scrollLabel).toBe(costGroupsScrollLabel("pt-BR"));
352
- expect(column("duration")?.render(unavailableGroup)).toBe("Indisponível");
350
+ expect(column("duration")?.render(unavailableGroup)).toBe("0");
353
351
  expect(column("tokens")?.render(unavailableGroup)).toBe("0");
354
- expect(column("cost")?.render(unavailableGroup)).toBe("Indisponível");
352
+ expect(column("cost")?.render(unavailableGroup)).toBe("0");
355
353
  expect(column("duration")?.sortValue?.(unavailableGroup)).toBeNull();
356
354
  expect(column("tokens")?.sortValue?.(unavailableGroup)).toBeNull();
357
355
  expect(column("cost")?.sortValue?.(unavailableGroup)).toBeNull();
358
- const totalSummary = "Total geral — Tempo: Indisponível; Tokens: 0; BRL: Indisponível";
356
+ const totalSummary = "Total geral — Tempo: 0; Tokens: 0; BRL: 0";
359
357
  const footerLabel = (key: string) => {
360
358
  const footer = column(key)?.footer as { props?: { "aria-label"?: string } } | undefined;
361
359
  return footer?.props?.["aria-label"];
@@ -455,7 +453,7 @@ describe("grouped cost availability", () => {
455
453
  for (const key of ["tasks", "prds", "specs", "tests", "evidences", "tokens"]) {
456
454
  expect(columns.find((c) => c.key === key)?.render?.(missing)).toBe("0");
457
455
  }
458
- expect(columns.find((c) => c.key === "duration")?.render?.(missing)).toBe("Indisponível");
459
- expect(columns.find((c) => c.key === "cost")?.render?.(missing)).toBe("Indisponível");
456
+ expect(columns.find((c) => c.key === "duration")?.render?.(missing)).toBe("0");
457
+ expect(columns.find((c) => c.key === "cost")?.render?.(missing)).toBe("0");
460
458
  });
461
459
  });
@@ -0,0 +1,23 @@
1
+ import type { CostGroupRow } from "../contracts.js";
2
+ import { sumNumbers } from "./cost-table-metrics.js";
3
+
4
+ export function costGroupConversationCount(row: unknown): number | null {
5
+ return (row as { conversation_count?: number | null }).conversation_count ?? null;
6
+ }
7
+
8
+ export function costGroupPageTotals(rows: readonly CostGroupRow[]) {
9
+ return {
10
+ runs: rows.reduce((sum, row) => sum + row.run_count, 0),
11
+ tasks: sumNumbers(rows.map((row) => row.task_count)),
12
+ prds: sumNumbers(rows.map((row) => row.prd_count)),
13
+ specs: sumNumbers(rows.map((row) => row.spec_count)),
14
+ evidences: sumNumbers(rows.map((row) => row.evidence_count)),
15
+ tests: sumNumbers(rows.map((row) => row.test_count)),
16
+ conversations: sumNumbers(rows.map(costGroupConversationCount)),
17
+ durationKnownMs: rows.reduce((sum, row) => sum + row.duration.known_sum, 0),
18
+ durationPartial: rows.some((row) => !row.duration.complete),
19
+ tokens: sumNumbers(rows.map((row) => row.total_tokens)),
20
+ usd: sumNumbers(rows.map((row) => row.estimated_usd)),
21
+ brl: sumNumbers(rows.map((row) => row.estimated_brl)),
22
+ };
23
+ }