@zaip-ai/cli 1.3.0 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/README.md +3 -2
  2. package/dist/services/agent-adapters.js +1 -1
  3. package/dist/services/agent-adapters.js.map +1 -1
  4. package/dist/services/agent-adapters.test.js +2 -1
  5. package/dist/services/agent-adapters.test.js.map +1 -1
  6. package/dist/services/codex-pre-tool-hook-template.d.ts.map +1 -1
  7. package/dist/services/codex-pre-tool-hook-template.js +89 -7
  8. package/dist/services/codex-pre-tool-hook-template.js.map +1 -1
  9. package/dist/services/codex-rules.d.ts +1 -1
  10. package/dist/services/codex-rules.d.ts.map +1 -1
  11. package/dist/services/codex-rules.js +7 -0
  12. package/dist/services/codex-rules.js.map +1 -1
  13. package/dist/services/install-agents-template.d.ts.map +1 -1
  14. package/dist/services/install-agents-template.js +1 -0
  15. package/dist/services/install-agents-template.js.map +1 -1
  16. package/dist/services/runtime-template.test.js +31 -7
  17. package/dist/services/runtime-template.test.js.map +1 -1
  18. package/dist/services/template-manifest.d.ts.map +1 -1
  19. package/dist/services/template-manifest.js +7 -3
  20. package/dist/services/template-manifest.js.map +1 -1
  21. package/package.json +1 -1
  22. package/templates/.agents/config/agents/catalog.yaml +7 -7
  23. package/templates/.agents/config/clients/codex/agents/zaip-agent-backend.toml +2 -2
  24. package/templates/.agents/config/clients/codex/agents/zaip-agent-designer.toml +2 -2
  25. package/templates/.agents/config/clients/codex/agents/zaip-agent-frontend.toml +8 -8
  26. package/templates/.agents/dashboard/.zaip-managed.json +373 -205
  27. package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-DxR_pgSV.js → CostCalculationGuidePage-ovxurkIk.js} +1 -1
  28. package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog-4jZLTWE_.js → DashboardHelpDialog-BXy6nti1.js} +268 -86
  29. package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-CKcsqb-r.js → PricingSettingsDialog-vSvvOHQp.js} +1 -1
  30. package/templates/.agents/dashboard/apps/frontend/dist/assets/{ProjectSettingsPage-DmmJD2Th.js → ProjectSettingsPage-zH758vfs.js} +1 -1
  31. package/templates/.agents/dashboard/apps/frontend/dist/assets/TraceabilityWorkspace-CTH2wDs5.js +1 -0
  32. package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-CaKwQP43.css → TraceabilityWorkspace-DvvavDLY.css} +1 -1
  33. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-DKwP1stV.js +68 -0
  34. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-PoJA2PI1.css +1 -0
  35. package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-C0fj2uU9.js → icons-vendor-B_lDDHn1.js} +1 -1
  36. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-B31GMG5O.css +1 -0
  37. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-CuMcoUni.js +2 -0
  38. package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-BEsJ-FYH.js → react-vendor-B3TJrD4O.js} +1 -1
  39. package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-B6y6Gw12.js → traceability-7f1j5wx3.js} +1 -1
  40. package/templates/.agents/dashboard/apps/frontend/dist/index.html +7 -7
  41. package/templates/.agents/dashboard/apps/frontend/playwright.dom.config.ts +32 -0
  42. package/templates/.agents/dashboard/apps/frontend/src/App.tsx +132 -245
  43. package/templates/.agents/dashboard/apps/frontend/src/AppOverlays.tsx +0 -21
  44. package/templates/.agents/dashboard/apps/frontend/src/DashboardAlternateContent.tsx +9 -0
  45. package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.dom.e2e.ts +56 -0
  46. package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.test.ts +18 -1
  47. package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.tsx +75 -8
  48. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.test.ts +25 -1
  49. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.ts +7 -9
  50. package/templates/.agents/dashboard/apps/frontend/src/operations-period-render.test.ts +4 -4
  51. package/templates/.agents/dashboard/apps/frontend/src/overview-status-page.test.ts +1 -1
  52. package/templates/.agents/dashboard/apps/frontend/src/snapshot-banner-semantics.test.ts +7 -0
  53. package/templates/.agents/dashboard/apps/frontend/src/snapshot-coherence.test.ts +20 -8
  54. package/templates/.agents/dashboard/apps/frontend/src/sqlite-page-export.test.ts +32 -0
  55. package/templates/.agents/dashboard/apps/frontend/src/sqlite-page-export.ts +21 -0
  56. package/templates/.agents/dashboard/apps/frontend/src/styles.css +19 -0
  57. package/templates/.agents/dashboard/apps/frontend/src/traceability-task-pagination.test.ts +32 -0
  58. package/templates/.agents/dashboard/apps/frontend/src/traceability-task-pagination.ts +36 -0
  59. package/templates/.agents/dashboard/apps/frontend/src/traceability-workspace.css +6 -2
  60. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.test.ts +48 -0
  61. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.ts +31 -27
  62. package/templates/.agents/dashboard/apps/frontend/src/use-app-trace-documents.ts +7 -1
  63. package/templates/.agents/dashboard/apps/frontend/src/use-app-work-list-page.test.ts +163 -0
  64. package/templates/.agents/dashboard/apps/frontend/src/use-app-work-list-page.ts +136 -0
  65. package/templates/.agents/dashboard/apps/frontend/src/use-operations-detail-export.ts +76 -0
  66. package/templates/.agents/dashboard/apps/frontend/src/work-list-load-dedup.test.ts +23 -0
  67. package/templates/.agents/dashboard/apps/frontend/src/work-list-load-dedup.ts +27 -0
  68. package/templates/.agents/dashboard/apps/frontend/src/work-list-pagination.test.ts +38 -14
  69. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostDeveloperAttributionPanel.tsx +60 -0
  70. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostDeveloperAttributionTable.tsx +191 -0
  71. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostFilters.loading.test.ts +14 -0
  72. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostFilters.run-page.test.ts +18 -0
  73. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostFilters.tsx +18 -3
  74. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorContent.tsx +8 -2
  75. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorLoadErrors.tsx +35 -0
  76. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.test.ts +47 -1
  77. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.tsx +50 -58
  78. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTableLoading.test.ts +132 -0
  79. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTableLoading.tsx +70 -0
  80. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.test.ts +128 -138
  81. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.tsx +17 -67
  82. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-copy.ts +2 -0
  83. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-views.ts +3 -1
  84. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-page-range.ts +35 -0
  85. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-compact-cost-page.ts +16 -4
  86. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-group-page.test.ts +37 -2
  87. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-group-page.ts +44 -8
  88. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-run-page.ts +6 -1
  89. package/templates/.agents/dashboard/modules/operations/src/contracts.ts +20 -3
  90. package/templates/.agents/dashboard/modules/operations/src/react/DeveloperAttributionPanel.test.ts +54 -0
  91. package/templates/.agents/dashboard/modules/operations/src/react/DeveloperAttributionPanel.tsx +247 -0
  92. package/templates/.agents/dashboard/modules/operations/src/react/OperationsMainContent.tsx +39 -13
  93. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.helpers.tsx +107 -2
  94. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.macro-metrics.test.ts +55 -0
  95. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.test.ts +20 -28
  96. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.tsx +162 -165
  97. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverviewTopPanel.tsx +127 -0
  98. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPage.tsx +13 -46
  99. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPageOverview.tsx +3 -0
  100. package/templates/.agents/dashboard/modules/operations/src/react/PagedWorkList.test.tsx +43 -0
  101. package/templates/.agents/dashboard/modules/operations/src/react/PagedWorkList.tsx +181 -0
  102. package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkList.test.ts +4 -2
  103. package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkList.tsx +13 -0
  104. package/templates/.agents/dashboard/modules/operations/src/react/developer-identities.test.ts +18 -0
  105. package/templates/.agents/dashboard/modules/operations/src/react/developer-identities.ts +30 -0
  106. package/templates/.agents/dashboard/modules/operations/src/react/execution-metrics.ts +1 -0
  107. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.test.ts +8 -0
  108. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.tsx +3 -0
  109. package/templates/.agents/dashboard/modules/operations/src/react/export/export-layout.test.ts +14 -5
  110. package/templates/.agents/dashboard/modules/operations/src/react/index.ts +1 -0
  111. package/templates/.agents/dashboard/modules/operations/src/react/operations-coverage-toast.test.ts +4 -3
  112. package/templates/.agents/dashboard/modules/operations/src/react/operations-date-filters.css +1 -1
  113. package/templates/.agents/dashboard/modules/operations/src/react/operations-overview-period-filter.test.ts +21 -11
  114. package/templates/.agents/dashboard/modules/operations/src/react/operations-overview.css +4 -0
  115. package/templates/.agents/dashboard/modules/operations/src/react/operations-page-contract.ts +8 -1
  116. package/templates/.agents/dashboard/modules/operations/src/react/operations-view-order.test.ts +1 -0
  117. package/templates/.agents/dashboard/modules/operations/src/react/period-summary.ts +1 -0
  118. package/templates/.agents/dashboard/modules/operations/src/server/metric-coverage.ts +1 -0
  119. package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +527 -335
  120. package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +501 -309
  121. package/templates/.agents/dashboard/packages/local-runtime/gitignore.template +2 -0
  122. package/templates/.agents/dashboard/packages/local-runtime/scripts/rebuild-sqlite-read-model.mjs +3 -1
  123. package/templates/.agents/dashboard/packages/local-runtime/scripts/test-sqlite-read-model.mjs +3 -3
  124. package/templates/.agents/dashboard/packages/local-runtime/scripts/validate-sqlite-read-model.mjs +25 -15
  125. package/templates/.agents/dashboard/packages/local-runtime/server.mjs +527 -335
  126. package/templates/.agents/dashboard/packages/local-runtime/src/server-board.test.ts +34 -0
  127. package/templates/.agents/dashboard/packages/local-runtime/src/server-board.ts +15 -8
  128. package/templates/.agents/dashboard/packages/local-runtime/src/server-classification-page.test.ts +87 -0
  129. package/templates/.agents/dashboard/packages/local-runtime/src/server-period-macro-metric.test.ts +44 -0
  130. package/templates/.agents/dashboard/packages/local-runtime/src/server-period-macro-metric.ts +125 -0
  131. package/templates/.agents/dashboard/packages/local-runtime/src/server-reconciliation-refresh.test.ts +66 -0
  132. package/templates/.agents/dashboard/packages/local-runtime/src/server-request-authority.test.ts +61 -0
  133. package/templates/.agents/dashboard/packages/local-runtime/src/server-request-authority.ts +18 -0
  134. package/templates/.agents/dashboard/packages/local-runtime/src/server-run-projections.test.ts +42 -0
  135. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-refresh.ts +97 -0
  136. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot.test.ts +3 -1
  137. package/templates/.agents/dashboard/packages/local-runtime/src/server-sqlite-task-trace.ts +91 -0
  138. package/templates/.agents/dashboard/packages/local-runtime/src/server.test-fixtures.ts +36 -0
  139. package/templates/.agents/dashboard/packages/local-runtime/src/server.test.ts +98 -123
  140. package/templates/.agents/dashboard/packages/local-runtime/src/server.ts +111 -135
  141. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-artifact-counts.ts +28 -0
  142. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-agent-page.test.ts +63 -0
  143. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-agent-page.ts +109 -0
  144. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-aggregates.ts +497 -0
  145. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-fast-page-parity.test-support.ts +65 -0
  146. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-group-page.test.ts +176 -0
  147. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-group-page.ts +119 -0
  148. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-group-projection.ts +41 -0
  149. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-page.test.ts +16 -0
  150. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-page.ts +29 -0
  151. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-first-fold-cache.ts +355 -0
  152. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-first-fold-persistence.ts +121 -0
  153. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-first-fold-timing.ts +27 -0
  154. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-operations-projection.ts +15 -0
  155. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-operations-work-list.test.ts +293 -0
  156. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-operations-work-list.ts +371 -0
  157. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-cache.test-support.ts +23 -0
  158. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-first-fold-types.ts +89 -0
  159. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider-fixture.ts +227 -0
  160. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider.test.ts +691 -0
  161. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-source-fingerprint.ts +81 -0
  162. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.test.ts +440 -863
  163. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.ts +1120 -1086
  164. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-cache.ts +129 -0
  165. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-install.test.ts +26 -0
  166. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-install.ts +28 -0
  167. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-seed.test.ts +116 -0
  168. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.mjs +146 -0
  169. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.test.mjs +165 -0
  170. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-refresh.mjs +140 -0
  171. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.mjs +188 -299
  172. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.test.mjs +51 -70
  173. package/templates/.agents/dashboard/packages/windows-launcher/Program.cs +5 -2
  174. package/templates/.agents/dashboard/tests/e2e/playwright.config.ts +4 -1
  175. package/templates/.agents/dashboard/tests/e2e/specs/cost-monitor-loading.spec.ts +142 -0
  176. package/templates/.agents/dashboard/tests/e2e/specs/cost-monitor-pagination.spec.ts +232 -0
  177. package/templates/.agents/dashboard/tests/e2e/specs/dashboard-performance-matrix.spec.ts +743 -0
  178. package/templates/.agents/dashboard/tests/e2e/specs/dashboard.spec.ts +183 -210
  179. package/templates/.agents/dashboard/zaip-dashboard-windows.exe +0 -0
  180. package/templates/.agents/requirements/autonomous-assurance.md +6 -6
  181. package/templates/.agents/requirements/comparative-audit.md +1 -1
  182. package/templates/.agents/requirements/context-engine.md +1 -1
  183. package/templates/.agents/requirements/cost-observability.md +1 -1
  184. package/templates/.agents/requirements/dashboard-launch.md +1 -1
  185. package/templates/.agents/requirements/data-export.md +1 -1
  186. package/templates/.agents/requirements/design-system.md +1 -1
  187. package/templates/.agents/requirements/execution-engine.md +3 -3
  188. package/templates/.agents/requirements/kanban-dashboard.md +10 -2
  189. package/templates/.agents/requirements/model-orchestration.md +1 -1
  190. package/templates/.agents/requirements/public-distribution.md +1 -1
  191. package/templates/.agents/requirements/software-architecture.md +1 -1
  192. package/templates/.agents/requirements/strategic-improvements.md +1 -1
  193. package/templates/.agents/requirements/work-lifecycle.md +4 -4
  194. package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
  195. package/templates/.agents/rules/dashboard-ui-consistency.md +14 -0
  196. package/templates/.agents/rules/frontend-lite.md +4 -4
  197. package/templates/.agents/rules/run-lifecycle-closure.md +4 -4
  198. package/templates/.agents/runtime/bin/zaip.mjs +1137 -1037
  199. package/templates/.agents/runtime/checksums.sha256 +2 -2
  200. package/templates/.agents/runtime/provenance.json +2 -2
  201. package/templates/.agents/skills/criar-pasta-obsidian/scripts/generate-obsidian-vault.test.mjs +20 -16
  202. package/templates/.agents/skills/zaip-skill-agent-catalog/SKILL.md +22 -0
  203. package/templates/.agents/skills/zaip-skill-agent-catalog/agents/openai.yaml +4 -0
  204. package/templates/.agents/skills/zaip-skill-author-skills/SKILL.md +3 -2
  205. package/templates/.agents/skills/zaip-skill-author-skills/scripts/audit-skill-catalog.mjs +49 -0
  206. package/templates/.agents/skills/zaip-skill-author-skills/scripts/author-skill-scripts.test.mjs +27 -0
  207. package/templates/.agents/skills/zaip-skill-author-skills/scripts/run-skill-tests.mjs +37 -0
  208. package/templates/.agents/skills/zaip-skill-backend/SKILL.md +1 -0
  209. package/templates/.agents/skills/zaip-skill-change/SKILL.md +1 -1
  210. package/templates/.agents/skills/zaip-skill-change/references/observable-evidence.md +83 -0
  211. package/templates/.agents/skills/zaip-skill-dashboard-diagnostics/SKILL.md +22 -0
  212. package/templates/.agents/skills/zaip-skill-dashboard-diagnostics/agents/openai.yaml +4 -0
  213. package/templates/.agents/skills/zaip-skill-execution/SKILL.md +1 -1
  214. package/templates/.agents/skills/zaip-skill-frontend/SKILL.md +4 -3
  215. package/templates/.agents/skills/zaip-skill-frontend/agents/openai.yaml +2 -2
  216. package/templates/.agents/skills/zaip-skill-github-deploy/SKILL.md +10 -10
  217. package/templates/.agents/skills/zaip-skill-lifecycle-closure/SKILL.md +27 -0
  218. package/templates/.agents/skills/zaip-skill-lifecycle-closure/agents/openai.yaml +4 -0
  219. package/templates/.agents/skills/zaip-skill-prd/SKILL.md +4 -0
  220. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-local-telemetry.mjs +72 -22
  221. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-explicit-recovery.mjs +223 -0
  222. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-lib.mjs +42 -1
  223. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-lib.test.mjs +101 -0
  224. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-remeasurement.test.mjs +53 -0
  225. package/templates/.agents/skills/zaip-skill-release-qualification/SKILL.md +20 -0
  226. package/templates/.agents/skills/zaip-skill-release-qualification/agents/openai.yaml +4 -0
  227. package/templates/.agents/skills/zaip-skill-review/SKILL.md +5 -0
  228. package/templates/.agents/skills/zaip-skill-rules/SKILL.md +1 -1
  229. package/templates/.agents/skills/zaip-skill-spec/SKILL.md +4 -0
  230. package/templates/.agents/skills/zaip-skill-sqlite-read-model/SKILL.md +22 -90
  231. package/templates/.agents/zaip-aios/INDEX.md +57 -32
  232. package/templates/.agents/zaip-aios/README.md +22 -12
  233. package/templates/.agents/zaip-aios/guides/02-requirements.md +5 -0
  234. package/templates/.agents/zaip-aios/guides/06-execucao.md +13 -4
  235. package/templates/.agents/zaip-aios/guides/07-revisao-e-validacao.md +7 -0
  236. package/templates/.agents/zaip-aios/guides/10-seguranca.md +23 -0
  237. package/templates/.agents/zaip-aios/guides/11-observabilidade-e-custos.md +6 -6
  238. package/templates/.agents/zaip-aios/guides/13-boas-praticas.md +3 -0
  239. package/templates/.agents/zaip-aios/guides/16-lifecycle-e-reconciliacao.md +12 -3
  240. package/templates/.agents/zaip-aios/guides/17-como-o-custo-e-calculado.md +5 -5
  241. package/templates/.agents/zaip-aios/guides/19-assurance-e-autonomia.md +23 -0
  242. package/templates/.agents/zaip-aios/guides/20-instalacao-detalhada.md +44 -8
  243. package/templates/.agents/zaip-aios/guides/21-telemetria-monitor-custos.md +8 -14
  244. package/templates/.agents/zaip-aios/guides/22-fluxos-e-roteamento.md +5 -0
  245. package/templates/.agents/zaip-aios/guides/23-orquestracao-de-modelos.md +6 -6
  246. package/templates/.agents/zaip-aios/guides/24-calculo-de-custos-openai.md +8 -8
  247. package/templates/.agents/zaip-aios/guides/25-fontes-de-precos-openai.md +3 -2
  248. package/templates/.agents/zaip-aios/guides/26-qualidade-e-release.md +92 -10
  249. package/templates/.agents/zaip-aios/guides/27-telemetria-git-native.md +8 -3
  250. package/templates/.agents/zaip-aios/guides/28-lifecycle-completo.md +5 -0
  251. package/templates/template-manifest.json +555 -170
  252. package/templates/.agents/dashboard/apps/frontend/dist/assets/TraceabilityWorkspace-BwtitWa_.js +0 -1
  253. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-BZ5WdaMN.css +0 -1
  254. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-N5Csr0RP.js +0 -68
  255. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-C4aIB1PV.js +0 -2
  256. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-DK_enB5Z.css +0 -1
  257. package/templates/.agents/dashboard/apps/frontend/src/use-idle-cost-prefetch.ts +0 -45
  258. package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs.metafile.json +0 -7333
@@ -1,6 +1,6 @@
1
1
  import { EventEmitter } from "node:events";
2
2
  import { mkdir, mkdtemp, readFile, rm, utimes } from "node:fs/promises";
3
- import { connect, createServer } from "node:net";
3
+ import { createServer } from "node:net";
4
4
  import { tmpdir } from "node:os";
5
5
  import { join } from "node:path";
6
6
  import { afterEach, describe, expect, it } from "vitest";
@@ -21,7 +21,6 @@ import {
21
21
  recordPortStatus,
22
22
  registeredListenerMatches,
23
23
  runtimeKind,
24
- startDashboardLaunchPage,
25
24
  stopOwnedDashboardProcess,
26
25
  stripTerminalControlCodes,
27
26
  teardownWarning,
@@ -95,7 +94,7 @@ describe("dashboard executable session", () => {
95
94
 
96
95
  it("opens preparation before checking freshness and keeps it through server health", async () => {
97
96
  const source = await readFile(new URL("./start-dashboard.mjs", import.meta.url), "utf8");
98
- const lock = source.indexOf("await withLauncherLock(async () => {");
97
+ const lock = source.indexOf("await withLauncherLock(");
99
98
  const preparation = source.indexOf("launchPage = await startDashboardLaunchPage(0);");
100
99
  const freshness = source.indexOf("const buildSignature = await ensureFreshDashboardBuild();");
101
100
  const server = source.indexOf(
@@ -118,6 +117,7 @@ describe("dashboard executable session", () => {
118
117
  );
119
118
  expect(wait).toContain("Dashboard data readiness returned ${readiness.status}");
120
119
  expect(wait).toContain("unavailable.retryable = false;");
120
+ expect(wait).toContain("timedOut.retryable = false;");
121
121
  expect(wait).toContain("if (error?.retryable === false) throw error;");
122
122
  expect(wait).toContain("await delay(100);");
123
123
  });
@@ -135,27 +135,49 @@ describe("dashboard executable session", () => {
135
135
 
136
136
  it("uses one durable cache and an append-only worker log for a stale snapshot refresh", async () => {
137
137
  const source = await readFile(new URL("./start-dashboard.mjs", import.meta.url), "utf8");
138
-
138
+ const refresh = await readFile(
139
+ new URL("./start-dashboard-refresh.mjs", import.meta.url),
140
+ "utf8",
141
+ );
139
142
  expect(source).toContain(
140
143
  'const sqliteSnapshotCacheDirectory = join(sqliteReadModelDirectory, "cache");',
141
144
  );
142
- expect(source).toContain("ZAIP_SQLITE_CACHE_DIR: sqliteSnapshotCacheDirectory,");
145
+ expect(refresh).toContain("ZAIP_WORKSPACE: workspaceRoot,");
146
+ expect(refresh).toContain("ZAIP_SQLITE_CACHE_DIR: cacheDirectory,");
143
147
  expect(source).toContain(
144
148
  'const refreshLogPath = join(sessionDirectory, "dashboard-sqlite-refresh.log");',
145
149
  );
146
- expect(source).toContain('stdio: ["ignore", refreshLogDescriptor, refreshLogDescriptor]');
147
- expect(source).toContain("worker log ${refreshLogPath}");
150
+ expect(refresh).toContain('stdio: ["ignore", descriptor, descriptor]');
151
+ expect(refresh).toContain("worker log ${refreshLogPath}");
148
152
  });
149
153
 
150
- it("keeps a visible, actionable handoff watchdog instead of closing the preparation page before navigation", async () => {
151
- const source = await readFile(new URL("./start-dashboard.mjs", import.meta.url), "utf8");
154
+ it("persists the refresh worker PID before another launcher click may coalesce it", async () => {
155
+ const refresh = await readFile(
156
+ new URL("./start-dashboard-refresh.mjs", import.meta.url),
157
+ "utf8",
158
+ );
152
159
 
153
- expect(source).toContain('id="handoff-link"');
160
+ expect(refresh).toContain("await acquireRefreshLock(lockPath, isProcessAlive)");
161
+ expect(refresh).toContain("if (await isRunning())");
162
+ expect(refresh).toContain("await writeJsonAtomic(refreshStatusPath, {");
163
+ expect(refresh).toContain("await stopProcessTree(worker.pid).catch(() => {});");
164
+ const runningBarrier = refresh.slice(0, refresh.indexOf('worker.once("error"'));
165
+ expect(runningBarrier).not.toContain("void writeJsonAtomic(refreshStatusPath, {");
166
+ });
167
+
168
+ it("keeps the automatic handoff status visible instead of offering an unreliable local link", async () => {
169
+ const [source, launcherSource] = await Promise.all([
170
+ readFile(new URL("./start-dashboard-launch-page.mjs", import.meta.url), "utf8"),
171
+ readFile(new URL("./start-dashboard.mjs", import.meta.url), "utf8"),
172
+ ]);
173
+
174
+ expect(source).toContain("O dashboard está pronto. Abrindo a tela principal automaticamente…");
175
+ expect(source).not.toContain('id="handoff-link"');
154
176
  expect(source).toContain("function showHandoff(target)");
155
177
  expect(source).toContain("setTimeout(()=>location.replace(next.target),0)");
156
178
  expect(source).toContain("closeAfterHandoffGrace: (graceMs = 12_000)");
157
- expect(source).toContain("launchPage.closeAfterHandoffGrace();");
158
- expect(source).not.toContain("await launchPage.close();\n if (!redirected");
179
+ expect(launcherSource).toContain("launchPage.closeAfterHandoffGrace();");
180
+ expect(launcherSource).not.toContain("await launchPage.close();\n if (!redirected");
159
181
  });
160
182
 
161
183
  it("tears down prior owned sessions only after the new browser handoff", async () => {
@@ -279,13 +301,22 @@ describe("dashboard executable session", () => {
279
301
  ]);
280
302
  });
281
303
 
304
+ it("keeps a Linux browser fallback when xdg-open is unavailable", () => {
305
+ const target = "http://127.0.0.1:4317/?token=local-session-token";
306
+
307
+ expect(browserLaunchCommands(target, "linux")).toEqual([
308
+ ["xdg-open", [target]],
309
+ ["gio", ["open", target]],
310
+ ]);
311
+ });
312
+
282
313
  it("falls back to another browser command when the first option fails", async () => {
283
314
  let calls = 0;
284
315
  const spawnImpl = () => {
285
- calls += 1;
316
+ const attempt = ++calls;
286
317
  const child = new EventEmitter();
287
318
  child.unref = () => {};
288
- queueMicrotask(() => child.emit(calls === 1 ? "error" : "spawn", new Error("failed")));
319
+ queueMicrotask(() => child.emit(attempt === 1 ? "error" : "spawn", new Error("failed")));
289
320
  return child;
290
321
  };
291
322
 
@@ -293,58 +324,6 @@ describe("dashboard executable session", () => {
293
324
  expect(calls).toBe(2);
294
325
  });
295
326
 
296
- it("serves a viewport-covering centered progress overlay while the runtime starts", async () => {
297
- const launchPage = await startDashboardLaunchPage(0);
298
- try {
299
- const page = await fetch(`http://${host}:${launchPage.port}/?token=temporary`);
300
- const health = await fetch(`http://${host}:${launchPage.port}/api/v2/health`);
301
-
302
- expect(page.status).toBe(200);
303
- const html = await page.text();
304
- expect(html).toContain('class="launch-overlay"');
305
- expect(html).toContain("position:fixed;inset:0;width:100vw;height:100vh");
306
- expect(html).toContain("display:grid;place-items:center");
307
- expect(html).toContain('aria-live="polite" role="status"');
308
- expect(html).toContain("abra o dashboard agora");
309
- expect(html).toContain("function showHandoff(target)");
310
- const progress = await fetch(`http://${host}:${launchPage.port}/api/v2/progress`);
311
- await expect(progress.json()).resolves.toMatchObject({
312
- phase: "Iniciando o launcher local",
313
- percent: expect.any(Number),
314
- });
315
- expect(health.status).toBe(503);
316
- await expect(health.json()).resolves.toEqual({
317
- kind: "zaip-dashboard-launcher",
318
- status: "starting",
319
- });
320
-
321
- launchPage.redirect("http://127.0.0.1:4317/?token=reused-session");
322
- const ready = await fetch(`http://${host}:${launchPage.port}/api/v2/ready`);
323
- await expect(ready.json()).resolves.toEqual({
324
- target: "http://127.0.0.1:4317/?token=reused-session",
325
- });
326
- await expect(launchPage.waitForRedirect(10)).resolves.toBe(true);
327
- } finally {
328
- await launchPage.close();
329
- }
330
- });
331
-
332
- it("bounds launch-page shutdown when a browser connection never finishes polling", async () => {
333
- const launchPage = await startDashboardLaunchPage(0);
334
- const connection = connect({ host, port: launchPage.port });
335
- await new Promise((resolveConnected, rejectConnected) => {
336
- connection.once("connect", resolveConnected);
337
- connection.once("error", rejectConnected);
338
- });
339
- const closed = new Promise((resolveClosed) => connection.once("close", resolveClosed));
340
- const startedAt = Date.now();
341
- await launchPage.close({ forceAfterMs: 20 });
342
- await closed;
343
-
344
- expect(Date.now() - startedAt).toBeLessThan(500);
345
- expect(connection.destroyed).toBe(true);
346
- });
347
-
348
327
  it("reuses a healthy session owned by the same workspace", async () => {
349
328
  const directory = await temporaryDirectory();
350
329
  const path = join(directory, "dashboard-session.json");
@@ -630,7 +609,7 @@ describe("dashboard executable session", () => {
630
609
  it("opens preparation before waiting and only reuses after checking the current build", async () => {
631
610
  const source = await readFile(new URL("./start-dashboard.mjs", import.meta.url), "utf8");
632
611
  const preparation = source.indexOf("launchPage = await startDashboardLaunchPage(0);");
633
- const lock = source.indexOf("await withLauncherLock(async () => {");
612
+ const lock = source.indexOf("await withLauncherLock(");
634
613
  const buildSignature = source.indexOf(
635
614
  "const buildSignature = await ensureFreshDashboardBuild();",
636
615
  );
@@ -645,7 +624,8 @@ describe("dashboard executable session", () => {
645
624
  expect(source).toContain(
646
625
  'snapshotState: snapshotCache === "stale_seed" ? "updating" : "current",',
647
626
  );
648
- expect(source).toContain("if (launchPage !== null) await launchPage.close().catch(() => {});");
627
+ expect(source).toContain("launchPage.setFailure({");
628
+ expect(source).toContain("launchPage.closeAfterFailureObservation();");
649
629
  expect(source).toContain('await writeLauncherStatus("ok");');
650
630
  });
651
631
 
@@ -666,8 +646,9 @@ describe("dashboard executable session", () => {
666
646
  "utf8",
667
647
  );
668
648
 
669
- expect(source).toContain("DateTime.UtcNow.AddSeconds(135)");
670
- expect(source).toContain("mais de 135 segundos");
649
+ expect(source).toContain("DateTime.UtcNow.AddSeconds(420)");
650
+ expect(source).toContain("lock window (210 s)");
651
+ expect(source).toContain("bounded 180 s server");
671
652
  });
672
653
  });
673
654
 
@@ -82,7 +82,10 @@ namespace ZaipDashboardLauncher
82
82
  // The Node launcher coalesces concurrent clicks and may wait for a
83
83
  // healthy build/handoff. Keep this wrapper alive for that bounded
84
84
  // handoff instead of showing a second failure dialog.
85
- var deadline = DateTime.UtcNow.AddSeconds(135);
85
+ // A waiting click can spend the Node lock window (210 s) behind
86
+ // the current leader, then still needs the bounded 180 s server
87
+ // startup window if that leader fails before publishing a session.
88
+ var deadline = DateTime.UtcNow.AddSeconds(420);
86
89
  while (DateTime.UtcNow < deadline)
87
90
  {
88
91
  try
@@ -122,7 +125,7 @@ namespace ZaipDashboardLauncher
122
125
  }
123
126
  Thread.Sleep(100);
124
127
  }
125
- return ReportFailure("O dashboard demorou mais de 135 segundos para abrir. As tentativas foram interrompidas; tente novamente.");
128
+ return ReportFailure("O dashboard ainda não publicou uma sessão pronta após 420 segundos. O processo local pode continuar registrando detalhes no log temporário; feche esta mensagem e tente novamente após verificar o log.");
126
129
  }
127
130
 
128
131
  private static bool VerifyDashboardLaunch(string workspaceRoot, out string error)
@@ -22,12 +22,15 @@ export default defineConfig({
22
22
  cwd: workspaceRoot,
23
23
  url: `${baseURL}/api/v2/health`,
24
24
  reuseExistingServer: false,
25
- timeout: 30_000,
25
+ timeout: 120_000,
26
26
  env: {
27
27
  ZAIP_UI_TOKEN: "e2e-token",
28
28
  ZAIP_SERVER_PORT: serverPort,
29
29
  ZAIP_WORKSPACE: workspaceRoot,
30
30
  ZAIP_UI_ROOT: resolve(workspaceRoot, ".agents/dashboard/apps/frontend/dist"),
31
+ // Match the Windows launcher: first paint uses the latest validated
32
+ // immutable seed while canonical refresh work stays off the served path.
33
+ ZAIP_SQLITE_PREFER_LAST_VALID_SEED: "1",
31
34
  },
32
35
  },
33
36
  });
@@ -0,0 +1,142 @@
1
+ import { expect, test } from "@playwright/test";
2
+
3
+ declare global {
4
+ interface Window {
5
+ __costAbortRaceProbe: {
6
+ abortObserved: boolean;
7
+ overlayPresentAtSwitch: boolean | null;
8
+ shellInertAtSwitch: boolean | null;
9
+ switched: boolean;
10
+ };
11
+ }
12
+ }
13
+
14
+ test("releases an aborted Cost group load and keeps Operation reachable", async ({ page }) => {
15
+ await page.addInitScript(() => {
16
+ const probe = {
17
+ abortObserved: false,
18
+ overlayPresentAtSwitch: null as boolean | null,
19
+ shellInertAtSwitch: null as boolean | null,
20
+ switched: false,
21
+ };
22
+ const originalFetch = window.fetch.bind(window);
23
+ Object.assign(window, { __costAbortRaceProbe: probe });
24
+ window.fetch = (input, init) => {
25
+ const requestUrl = new URL(
26
+ typeof input === "string" ? input : input instanceof URL ? input.href : input.url,
27
+ window.location.href,
28
+ );
29
+ if (
30
+ !probe.switched &&
31
+ requestUrl.pathname === "/api/v2/sqlite-views" &&
32
+ requestUrl.searchParams.get("view") === "cost_monitor_daily"
33
+ ) {
34
+ const executionsTab = Array.from(
35
+ document.querySelectorAll<HTMLButtonElement>(".cost-tabs button"),
36
+ ).find((button) => button.textContent?.trim() === "Execuções");
37
+ if (executionsTab !== undefined) {
38
+ probe.overlayPresentAtSwitch =
39
+ document.querySelector(".period-transition-overlay") !== null;
40
+ probe.shellInertAtSwitch = executionsTab.closest("[inert]") !== null;
41
+ init?.signal?.addEventListener("abort", () => {
42
+ probe.abortObserved = true;
43
+ });
44
+ probe.switched = true;
45
+ executionsTab.click();
46
+ }
47
+ }
48
+ return originalFetch(input, init);
49
+ };
50
+ });
51
+
52
+ await openDashboard(page);
53
+ await expectGlobalMonthPreset(page);
54
+ const capabilityNav = page.getByRole("navigation", { name: "Áreas do dashboard" });
55
+ await capabilityNav.getByRole("button", { name: /Monitor de Custos/ }).click();
56
+ const costTabs = page.locator(".cost-tabs");
57
+ await expect(costTabs).toBeVisible();
58
+ await expect
59
+ .poll(() => page.evaluate(() => window.__costAbortRaceProbe))
60
+ .toMatchObject({
61
+ abortObserved: true,
62
+ overlayPresentAtSwitch: false,
63
+ shellInertAtSwitch: false,
64
+ switched: true,
65
+ });
66
+
67
+ const executionsTab = costTabs.getByRole("button", { exact: true, name: "Execuções" });
68
+ await expect(executionsTab).toHaveAttribute("aria-pressed", "true");
69
+ await expect(page.locator(".period-transition-overlay")).toHaveCount(0, { timeout: 15_000 });
70
+
71
+ await capabilityNav.getByRole("button", { name: /Operação/ }).click();
72
+ await expect(page.getByLabel("Kanban de trabalho")).toBeVisible();
73
+ await expect(page.locator(".period-transition-overlay")).toHaveCount(0);
74
+ });
75
+
76
+ test("loads every Cost Monitor tab before returning to Operation", async ({ page }) => {
77
+ const pageErrors: string[] = [];
78
+ const failedApiResponses: string[] = [];
79
+ page.on("pageerror", (error) => pageErrors.push(error.message));
80
+ page.on("response", (response) => {
81
+ if (
82
+ response.url().includes("/api/v2/") &&
83
+ !response.url().includes("/api/v2/reconcile-open-work") &&
84
+ response.status() >= 400
85
+ )
86
+ failedApiResponses.push(`${response.status()} ${response.url()}`);
87
+ });
88
+
89
+ await openDashboard(page);
90
+ await expectGlobalMonthPreset(page);
91
+ const capabilityNav = page.getByRole("navigation", { name: "Áreas do dashboard" });
92
+ await capabilityNav.getByRole("button", { name: /Monitor de Custos/ }).click();
93
+ const costTabs = page.locator(".cost-tabs");
94
+ await expect(costTabs).toBeVisible();
95
+
96
+ for (const [tab, view] of [
97
+ ["Diário", "daily"],
98
+ ["Mensal", "monthly"],
99
+ ["Anual", "annual"],
100
+ ["Visão geral", "overview"],
101
+ ["Execuções", "executions"],
102
+ ["Invocações de Agentes", "agentCalls"],
103
+ ["Zaip Agentes", "agents"],
104
+ ["Modelos LLM", "models"],
105
+ ["Provedores", "providers"],
106
+ ["Tarefas", "tasks"],
107
+ ["SPEC", "specs"],
108
+ ["PRD", "prds"],
109
+ ["Conversas", "conversations"],
110
+ ] as const) {
111
+ await expect(page.locator(".period-transition-overlay")).toHaveCount(0, { timeout: 30_000 });
112
+ const button = costTabs.getByRole("button", { exact: true, name: tab });
113
+ await button.click();
114
+ await expect(button).toHaveAttribute("aria-pressed", "true");
115
+ await expect(page.locator(".period-transition-overlay")).toHaveCount(0, { timeout: 30_000 });
116
+ await expect(page.locator(`.cost-content-${view}`)).toBeVisible();
117
+ await expect(page.locator(".cost-period-error")).toHaveCount(0);
118
+ }
119
+
120
+ await capabilityNav.getByRole("button", { name: /Operação/ }).click();
121
+ await expect(page.getByLabel("Kanban de trabalho")).toBeVisible();
122
+ await expect(page.locator(".period-transition-overlay")).toHaveCount(0);
123
+ expect(pageErrors).toEqual([]);
124
+ expect(failedApiResponses).toEqual([]);
125
+ });
126
+
127
+ async function expectGlobalMonthPreset(page: import("@playwright/test").Page) {
128
+ await expect(
129
+ page.locator(".dashboard-global-controls").getByRole("combobox", { name: "Período" }),
130
+ ).toHaveValue("this-month");
131
+ }
132
+
133
+ async function openDashboard(page: import("@playwright/test").Page) {
134
+ const operations = page.waitForResponse(
135
+ (response) =>
136
+ response.status() === 200 &&
137
+ response.url().includes("/api/v2/operations") &&
138
+ response.url().includes("scope=quick"),
139
+ );
140
+ await page.goto("/?token=e2e-token");
141
+ return operations;
142
+ }
@@ -0,0 +1,232 @@
1
+ import { expect, type Page, type Response, test } from "@playwright/test";
2
+
3
+ declare global {
4
+ interface Window {
5
+ __paginationExportProbe: { closes: number; writes: number };
6
+ }
7
+ }
8
+
9
+ type TargetView = {
10
+ tab: string | RegExp;
11
+ content: string;
12
+ sqliteView: string;
13
+ pagerName: string;
14
+ exportLimit: number;
15
+ };
16
+
17
+ type SqlitePage = {
18
+ offset: number;
19
+ limit: number;
20
+ total_count: number;
21
+ has_more: boolean;
22
+ rows: readonly Record<string, unknown>[];
23
+ };
24
+
25
+ const targets: readonly TargetView[] = [
26
+ {
27
+ tab: "Execuções",
28
+ content: "executions",
29
+ sqliteView: "cost_monitor_executions",
30
+ pagerName: "Paginação dos dados agregados",
31
+ exportLimit: 50,
32
+ },
33
+ {
34
+ tab: "Invocações de Agentes",
35
+ content: "agentCalls",
36
+ sqliteView: "cost_monitor_agent_invocations",
37
+ pagerName: "Paginação das invocações",
38
+ exportLimit: 100,
39
+ },
40
+ {
41
+ tab: /Zaip (?:Agentes|Agents)/,
42
+ content: "agents",
43
+ sqliteView: "cost_monitor_zaip_agents",
44
+ pagerName: "Paginação de grupos",
45
+ exportLimit: 50,
46
+ },
47
+ {
48
+ tab: "Tarefas",
49
+ content: "tasks",
50
+ sqliteView: "cost_monitor_tasks",
51
+ pagerName: "Paginação dos dados agregados",
52
+ exportLimit: 50,
53
+ },
54
+ {
55
+ tab: "Conversas",
56
+ content: "conversations",
57
+ sqliteView: "cost_monitor_conversations",
58
+ pagerName: "Paginação dos dados agregados",
59
+ exportLimit: 50,
60
+ },
61
+ ];
62
+
63
+ test("serves ten rows and exercises Next/Previous on every reported slow tab", async ({ page }) => {
64
+ const tabs = await openAllTimeCostMonitor(page);
65
+ const measurements: Array<{ tab: string; first_ms: number; next_ms: number }> = [];
66
+
67
+ for (const target of targets) {
68
+ const button = tabs.getByRole("button", { exact: true, name: target.tab });
69
+ const first = await requestPage(page, target.sqliteView, 0, 10, () => button.click());
70
+ const content = page.locator(`.cost-content-${target.content}`);
71
+ await expect(button).toHaveAttribute("aria-pressed", "true");
72
+ await expect(content).toBeVisible();
73
+ await expect(content.locator("table.ui-table tbody tr")).toHaveCount(10);
74
+ expect(first.payload).toMatchObject({ offset: 0, limit: 10, has_more: true });
75
+ expect(first.payload.total_count).toBeGreaterThan(10);
76
+ expect(first.elapsedMs).toBeLessThan(5_000);
77
+
78
+ let pager = content.getByRole("navigation", { name: target.pagerName });
79
+ await expect(pager).toContainText(`1–10 de ${first.payload.total_count}`);
80
+ await expect(pager.getByRole("button", { name: "Anterior" })).toBeDisabled();
81
+ await expect(pager.getByRole("button", { name: "Próxima" })).toBeEnabled();
82
+
83
+ const next = await requestPage(page, target.sqliteView, 10, 10, () =>
84
+ pager.getByRole("button", { name: "Próxima" }).click(),
85
+ );
86
+ pager = content.getByRole("navigation", { name: target.pagerName });
87
+ await expect(content.locator("table.ui-table tbody tr")).toHaveCount(10);
88
+ await expect(pager).toContainText(`11–20 de ${first.payload.total_count}`);
89
+ await expect(pager.getByRole("button", { name: "Anterior" })).toBeEnabled();
90
+ expect(next.elapsedMs).toBeLessThan(5_000);
91
+
92
+ await requestPage(page, target.sqliteView, 0, 10, () =>
93
+ pager.getByRole("button", { name: "Anterior" }).click(),
94
+ );
95
+ await expect(content.getByRole("navigation", { name: target.pagerName })).toContainText(
96
+ `1–10 de ${first.payload.total_count}`,
97
+ );
98
+ measurements.push({
99
+ tab: typeof target.tab === "string" ? target.tab : "Zaip Agents",
100
+ first_ms: first.elapsedMs,
101
+ next_ms: next.elapsedMs,
102
+ });
103
+ }
104
+
105
+ console.log(`COST_PAGINATION_TIMINGS ${JSON.stringify(measurements)}`);
106
+ });
107
+
108
+ for (const target of targets) {
109
+ const label = typeof target.tab === "string" ? target.tab : "Zaip Agents";
110
+ test(`exports the complete ${label} population independently from its ten-row visual page`, async ({
111
+ page,
112
+ }) => {
113
+ test.setTimeout(360_000);
114
+ await installExportSink(page);
115
+ const tabs = await openAllTimeCostMonitor(page);
116
+ const pages: SqlitePage[] = [];
117
+ page.on("response", async (response) => {
118
+ if (!isSqliteResponse(response, target.sqliteView)) return;
119
+ try {
120
+ const payload = (await response.json()) as SqlitePage;
121
+ if (payload.limit === target.exportLimit) pages.push(payload);
122
+ } catch {
123
+ // A failed/non-JSON response is asserted by the missing complete page below.
124
+ }
125
+ });
126
+ const button = tabs.getByRole("button", { exact: true, name: target.tab });
127
+ await requestPage(page, target.sqliteView, 0, 10, () => button.click());
128
+ await page.getByRole("button", { name: "Exportar Excel" }).click();
129
+ await expect
130
+ .poll(() => page.evaluate(() => window.__paginationExportProbe.closes), { timeout: 300_000 })
131
+ .toBe(1);
132
+ await expect(page.getByText("Exportação concluída.", { exact: true })).toBeVisible({
133
+ timeout: 30_000,
134
+ });
135
+ await expect.poll(() => pages.some((entry) => !entry.has_more), { timeout: 30_000 }).toBe(true);
136
+
137
+ expect(pages.length).toBeGreaterThan(0);
138
+ expect(pages.every((entry) => entry.limit === target.exportLimit)).toBe(true);
139
+ expect(pages.map((entry) => entry.offset)).toEqual(
140
+ pages.map((_, index) => index * target.exportLimit),
141
+ );
142
+ const total = pages[0]!.total_count;
143
+ expect(pages.every((entry) => entry.total_count === total)).toBe(true);
144
+ expect(pages.reduce((sum, entry) => sum + entry.rows.length, 0)).toBe(total);
145
+ expect(pages.at(-1)?.has_more).toBe(false);
146
+ console.log(`COST_EXPORT_TOTAL ${label} ${total}`);
147
+ });
148
+ }
149
+
150
+ async function openAllTimeCostMonitor(page: Page) {
151
+ const operations = page.waitForResponse(
152
+ (response) =>
153
+ response.status() === 200 &&
154
+ response.url().includes("/api/v2/operations") &&
155
+ response.url().includes("scope=quick"),
156
+ );
157
+ await page.goto("/?token=e2e-token");
158
+ await operations;
159
+ const period = page
160
+ .locator(".dashboard-global-controls")
161
+ .getByRole("combobox", { name: "Período" });
162
+ if ((await period.inputValue()) !== "all") {
163
+ await period.selectOption("all");
164
+ await expect(period).toHaveValue("all");
165
+ }
166
+ await expect(page.locator(".period-transition-overlay")).toHaveCount(0, { timeout: 60_000 });
167
+ await page
168
+ .getByRole("navigation", { name: "Áreas do dashboard" })
169
+ .getByRole("button", { name: /Monitor de Custos/ })
170
+ .click();
171
+ const tabs = page.locator(".cost-tabs");
172
+ await expect(tabs).toBeVisible();
173
+ await expect(page.locator(".period-transition-overlay")).toHaveCount(0, { timeout: 60_000 });
174
+ return tabs;
175
+ }
176
+
177
+ async function requestPage(
178
+ page: Page,
179
+ view: string,
180
+ offset: number,
181
+ limit: number,
182
+ action: () => Promise<unknown>,
183
+ ) {
184
+ const startedAt = Date.now();
185
+ const responsePromise = page.waitForResponse(
186
+ (response) =>
187
+ isSqliteResponse(response, view) &&
188
+ new URL(response.url()).searchParams.get("offset") === String(offset) &&
189
+ new URL(response.url()).searchParams.get("limit") === String(limit),
190
+ );
191
+ await action();
192
+ const response = await responsePromise;
193
+ const elapsedMs = Date.now() - startedAt;
194
+ expect(response.status()).toBe(200);
195
+ const payload = (await response.json()) as SqlitePage;
196
+ await expect(page.locator(".period-transition-overlay")).toHaveCount(0, { timeout: 60_000 });
197
+ return { payload, elapsedMs };
198
+ }
199
+
200
+ function isSqliteResponse(response: Response, view: string): boolean {
201
+ const url = new URL(response.url());
202
+ return (
203
+ response.status() === 200 &&
204
+ url.pathname === "/api/v2/sqlite-views" &&
205
+ url.searchParams.get("view") === view
206
+ );
207
+ }
208
+
209
+ async function installExportSink(page: Page) {
210
+ await page.addInitScript(() => {
211
+ const probe = { closes: 0, writes: 0 };
212
+ Object.assign(window, { __paginationExportProbe: probe });
213
+ Object.assign(window, {
214
+ showSaveFilePicker: async ({ suggestedName }: { suggestedName: string }) => {
215
+ const writer = {
216
+ abort: async () => undefined,
217
+ close: async () => {
218
+ probe.closes += 1;
219
+ },
220
+ ready: Promise.resolve(),
221
+ write: async () => {
222
+ probe.writes += 1;
223
+ },
224
+ };
225
+ return {
226
+ name: suggestedName,
227
+ createWritable: async () => ({ ...writer, getWriter: () => writer }),
228
+ };
229
+ },
230
+ });
231
+ });
232
+ }