@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 { spawn } from "node:child_process";
2
2
  import { createHash, randomBytes } from "node:crypto";
3
- import { appendFileSync, closeSync, mkdirSync, openSync } from "node:fs";
3
+ import { appendFileSync, mkdirSync } from "node:fs";
4
4
  import {
5
5
  access,
6
6
  mkdir,
@@ -12,18 +12,20 @@ import {
12
12
  utimes,
13
13
  writeFile,
14
14
  } from "node:fs/promises";
15
- import { createServer as createHttpServer } from "node:http";
16
15
  import { createServer } from "node:net";
17
16
  import { tmpdir } from "node:os";
18
17
  import { dirname, join, relative, resolve, win32 } from "node:path";
19
18
  import process from "node:process";
20
19
  import { fileURLToPath } from "node:url";
21
20
  import { collectPreviousDashboardSessions } from "./start-dashboard-history.mjs";
21
+ import { createDashboardLaunchPage } from "./start-dashboard-launch-page.mjs";
22
22
  import {
23
+ isProcessAlive,
23
24
  stopOwnedDashboardProcess,
24
25
  stopProcessTree,
25
26
  teardownWarning,
26
27
  } from "./start-dashboard-processes.mjs";
28
+ import { refreshIsRunning, startSeedRefresh } from "./start-dashboard-refresh.mjs";
27
29
 
28
30
  export {
29
31
  isAncestorProcess,
@@ -49,20 +51,15 @@ export const lockPath = join(sessionDirectory, "dashboard-sqlite-launcher.lock")
49
51
  export const launcherLogPath = join(sessionDirectory, "dashboard-sqlite-launcher.log");
50
52
  const frontendRoot = resolve(workspaceRoot, ".agents/dashboard/apps/frontend/dist");
51
53
  const sqliteReadModelDirectory = resolve(workspaceRoot, ".local", "dashboard-sqlite");
52
- // Keep the immutable seed cache in one durable workspace location. Runtime
53
- // generations and the isolated background refresher must never derive a
54
- // different cache directory from their temporary SQLite path.
54
+ // Keep the immutable seed cache durable and independent of temporary SQLite generations.
55
55
  const sqliteSnapshotCacheDirectory = join(sqliteReadModelDirectory, "cache");
56
- // Node's bytecode cache is disposable runtime acceleration, never dashboard
57
- // data. Keep it beside the private SQLite generations so fresh-port semantics
58
- // and canonical sources remain unchanged.
56
+ // Node's bytecode cache is disposable acceleration beside private SQLite generations.
59
57
  const nodeCompileCacheDirectory = join(sqliteReadModelDirectory, "node-compile-cache");
60
58
  const installedServer = resolve(packageRoot, "server.mjs");
61
59
  const sourceServer = resolve(packageRoot, "dist/server.cjs");
62
60
  const healthPath = "/api/v2/health";
63
- // Fresh-generation probe marker: changes the immutable source fingerprint only.
64
61
  // Keep the preparation lock through a full build so concurrent clicks coalesce safely.
65
- const lockTimeoutMs = 120_000;
62
+ const lockTimeoutMs = 210_000;
66
63
  const staleLockMs = 30_000;
67
64
  const lockHeartbeatFloorMs = 10;
68
65
  // A cold immutable seed can legitimately take ~176 s on a busy Windows
@@ -262,6 +259,10 @@ export async function findReusableDashboardSession({
262
259
  const port = matches.find((candidate) => candidate !== null);
263
260
  return port === undefined ? null : { ...session, port };
264
261
  }
262
+
263
+ async function backgroundRefreshIsRunning() {
264
+ return refreshIsRunning(join(sessionDirectory, "dashboard-sqlite-refresh.json"), isProcessAlive);
265
+ }
265
266
  async function latestModifiedAt(paths) {
266
267
  let latest = 0;
267
268
  for (const path of paths) {
@@ -345,6 +346,7 @@ export async function withLauncherLock(
345
346
  path = lockPath,
346
347
  timeoutMs = lockTimeoutMs,
347
348
  staleAfterMs = staleLockMs,
349
+ onWait = () => {},
348
350
  } = {},
349
351
  ) {
350
352
  await mkdir(directory, { recursive: true });
@@ -371,6 +373,7 @@ export async function withLauncherLock(
371
373
  await rm(path, { recursive: true, force: true });
372
374
  continue;
373
375
  }
376
+ onWait();
374
377
  await delay(100);
375
378
  }
376
379
  }
@@ -438,7 +441,12 @@ export function browserLaunchCommands(
438
441
  ],
439
442
  ];
440
443
  }
441
- return [[platform === "darwin" ? "open" : "xdg-open", [target]]];
444
+ return platform === "darwin"
445
+ ? [["open", [target]]]
446
+ : [
447
+ ["xdg-open", [target]],
448
+ ["gio", ["open", target]],
449
+ ];
442
450
  }
443
451
  export async function openBrowser(target, spawnImpl = spawn) {
444
452
  if (process.env.ZAIP_UI_NO_BROWSER === "1") return true;
@@ -469,130 +477,8 @@ export async function openBrowser(target, spawnImpl = spawn) {
469
477
  appendLauncherLog(`Could not dispatch the dashboard URL to the default browser: ${target}`);
470
478
  return false;
471
479
  }
472
- export async function startDashboardLaunchPage(port) {
473
- let redirectUrl = null;
474
- let progress = {
475
- percent: 4,
476
- phase: "Iniciando o launcher local",
477
- detail: "Abrindo a página de preparação.",
478
- };
479
- const connections = new Set();
480
- let resolveRedirectObserved;
481
- const redirectObserved = new Promise((resolveObserved) => {
482
- resolveRedirectObserved = resolveObserved;
483
- });
484
- const server = createHttpServer((request, response) => {
485
- response.setHeader("Cache-Control", "no-store");
486
- if (request.url?.startsWith("/api/v2/ready")) {
487
- if (redirectUrl !== null) resolveRedirectObserved(true);
488
- response.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
489
- response.end(JSON.stringify({ target: redirectUrl }));
490
- return;
491
- }
492
- if (request.url?.startsWith("/api/v2/progress")) {
493
- response.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
494
- response.end(JSON.stringify(progress));
495
- return;
496
- }
497
- if (request.url?.startsWith(healthPath)) {
498
- response.writeHead(503, { "Content-Type": "application/json; charset=utf-8" });
499
- response.end(JSON.stringify({ kind: "zaip-dashboard-launcher", status: "starting" }));
500
- return;
501
- }
502
- response.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
503
- response.end(`<!doctype html>
504
- <html lang="pt-BR">
505
- <head>
506
- <meta charset="utf-8">
507
- <meta name="viewport" content="width=device-width,initial-scale=1">
508
- <title>Dashboard Zaip AIOS</title>
509
- <style>
510
- :root{color-scheme:dark;font-family:Inter,system-ui,sans-serif;background:#010610;color:#f1f5f9}
511
- *{box-sizing:border-box}body{margin:0;background:#010610}.launch-overlay{position:fixed;inset:0;width:100vw;height:100vh;display:grid;place-items:center;padding:clamp(18px,5vw,72px);background:radial-gradient(circle at 50% 35%,rgba(0,176,240,.11),transparent 42%),#010610}
512
- .progress{width:min(100%,760px);padding:clamp(24px,4vw,42px);border:1px solid rgba(125,211,252,.2);border-radius:20px;background:rgba(3,9,22,.94);box-shadow:0 24px 80px rgba(0,0,0,.45);backdrop-filter:blur(10px)}.progress strong,.progress span{display:block}.progress strong{font-size:clamp(1.05rem,2vw,1.35rem)}.progress span{color:#b6c6db;font-size:clamp(.88rem,1.5vw,1rem);margin-top:7px}.track{height:9px;margin-top:20px;border-radius:999px;background:#172033;overflow:hidden}.bar{height:100%;width:4%;border-radius:inherit;background:linear-gradient(90deg,#38bdf8,#818cf8);transition:width .35s ease}.handoff{display:none;margin-top:16px;color:#dbeafe}.handoff a{color:#7dd3fc;font-weight:700}
513
- </style>
514
- </head>
515
- <body>
516
- <main class="launch-overlay" aria-label="Preparando dashboard">
517
- <section class="progress" aria-live="polite" role="status"><strong id="phase">Iniciando o launcher local</strong><span id="detail">Abrindo a página de preparação.</span><div class="track"><div class="bar" id="bar"></div></div><span class="handoff" id="handoff">O dashboard está pronto. Se ele não abrir automaticamente, <a id="handoff-link" href="#">abra o dashboard agora</a>.</span></section>
518
- </main>
519
- <script>
520
- let handoffTarget=null;
521
- function showHandoff(target){handoffTarget=target;const box=document.getElementById("handoff");const link=document.getElementById("handoff-link");link.href=target;box.style.display="block";document.getElementById("phase").textContent="Dashboard pronto";document.getElementById("detail").textContent="Concluindo a abertura da tela principal."}
522
- async function updateProgress(){try{const r=await fetch("/api/v2/progress",{cache:"no-store"});const p=await r.json();if(!handoffTarget){document.getElementById("phase").textContent=p.phase;document.getElementById("detail").textContent=p.detail};document.getElementById("bar").style.width=Math.max(4,Math.min(100,Number(p.percent)||4))+"%"}catch{}}
523
- async function waitForDashboard(){
524
- try{
525
- await updateProgress();
526
- const ready=await fetch("/api/v2/ready",{cache:"no-store"});
527
- const next=await ready.json();
528
- if(next.target){showHandoff(next.target);setTimeout(()=>location.replace(next.target),0);return}
529
- const response=await fetch("${healthPath}",{cache:"no-store"});
530
- const payload=await response.json();
531
- if(response.ok&&payload.kind==="${runtimeKind}"){location.reload();return}
532
- }catch{}
533
- setTimeout(waitForDashboard,250)
534
- }
535
- updateProgress();setTimeout(waitForDashboard,100)
536
- // A browser extension or a failed navigation must never leave a silent
537
- // skeleton. The visible link remains actionable while the temporary
538
- // launcher server keeps serving this page through the handoff grace.
539
- setTimeout(()=>{if(handoffTarget)showHandoff(handoffTarget)},4000)
540
- </script>
541
- </body>
542
- </html>`);
543
- });
544
- server.on("connection", (connection) => {
545
- connections.add(connection);
546
- connection.once("close", () => connections.delete(connection));
547
- });
548
- await new Promise((resolveListen, rejectListen) => {
549
- server.once("error", rejectListen);
550
- server.listen({ host, port }, resolveListen);
551
- });
552
- server.unref();
553
- const address = server.address();
554
- const listeningPort = typeof address === "object" && address !== null ? address.port : port;
555
- return {
556
- port: listeningPort,
557
- redirect: (target) => {
558
- redirectUrl = target;
559
- },
560
- setProgress: (next) => {
561
- progress = {
562
- ...progress,
563
- ...next,
564
- percent: Math.max(progress.percent, next.percent ?? progress.percent),
565
- };
566
- },
567
- waitForRedirect: async (timeoutMs = 1_500) =>
568
- Promise.race([redirectObserved, delay(timeoutMs).then(() => false)]),
569
- closeAfterHandoffGrace: (graceMs = 12_000) => {
570
- const timer = setTimeout(() => {
571
- void server.close();
572
- server.closeAllConnections?.();
573
- for (const connection of connections) connection.destroy();
574
- }, graceMs);
575
- timer.unref?.();
576
- },
577
- close: ({ forceAfterMs = 750 } = {}) =>
578
- new Promise((resolveClose, rejectClose) => {
579
- let forceTimer = null;
580
- const finish = (error) => {
581
- if (forceTimer !== null) clearTimeout(forceTimer);
582
- if (error) rejectClose(error);
583
- else resolveClose();
584
- };
585
- server.close(finish);
586
- // The preparation page polls while redirecting. A browser keep-alive
587
- // or an interrupted polling request must not keep this transient
588
- // server alive indefinitely after its handoff was issued.
589
- forceTimer = setTimeout(() => {
590
- server.closeAllConnections?.();
591
- for (const connection of connections) connection.destroy();
592
- }, forceAfterMs);
593
- forceTimer.unref?.();
594
- }),
595
- };
480
+ export function startDashboardLaunchPage(port) {
481
+ return createDashboardLaunchPage({ port, host, runtimeKind, healthPath, delay });
596
482
  }
597
483
  async function redirectLaunchPage(launchPage, target) {
598
484
  launchPage.redirect(target);
@@ -794,71 +680,24 @@ async function removeGeneratedReadModel(candidate) {
794
680
  );
795
681
  return false;
796
682
  }
797
- function refreshSnapshotSeedInBackground() {
798
- const refreshPath = resolve(
799
- sqliteReadModelDirectory,
800
- `refresh-${process.pid}-${randomBytes(4).toString("hex")}.sqlite`,
801
- );
683
+ async function refreshSnapshotSeedInBackground() {
802
684
  const refreshStatusPath = join(sessionDirectory, "dashboard-sqlite-refresh.json");
803
685
  const refreshLogPath = join(sessionDirectory, "dashboard-sqlite-refresh.log");
804
- mkdirSync(sessionDirectory, { recursive: true });
805
- const refreshLogDescriptor = openSync(refreshLogPath, "a");
806
- let worker;
807
- try {
808
- worker = spawn(
809
- process.execPath,
810
- [resolve(packageRoot, "scripts/rebuild-sqlite-read-model.mjs")],
811
- {
812
- cwd: workspaceRoot,
813
- env: {
814
- ...process.env,
815
- ZAIP_SQLITE_DB_PATH: refreshPath,
816
- ZAIP_SQLITE_PREFER_LAST_VALID_SEED: "0",
817
- ZAIP_DASHBOARD_LAUNCH_PROGRESS: "0",
818
- ZAIP_SQLITE_REFRESH_CLEANUP: "1",
819
- ZAIP_SQLITE_CACHE_DIR: sqliteSnapshotCacheDirectory,
820
- },
821
- // A detached worker keeps its own append-only evidence file. Pipes can
822
- // disappear with a fast launcher exit and silently abort the refresh.
823
- stdio: ["ignore", refreshLogDescriptor, refreshLogDescriptor],
824
- detached: true,
825
- windowsHide: true,
826
- },
827
- );
828
- } finally {
829
- closeSync(refreshLogDescriptor);
830
- }
831
- void writeJsonAtomic(refreshStatusPath, {
832
- status: "running",
833
- pid: worker.pid ?? null,
834
- path: refreshPath,
686
+ return startSeedRefresh({
687
+ appendLog: appendLauncherLog,
835
688
  cacheDirectory: sqliteSnapshotCacheDirectory,
836
- startedAt: new Date().toISOString(),
837
- }).catch(() => {});
838
- worker.once("error", (error) => {
839
- appendLauncherLog(`Background SQLite seed refresh failed to start: ${error.message}`);
840
- void writeJsonAtomic(refreshStatusPath, {
841
- status: "failed",
842
- error: error.message,
843
- finishedAt: new Date().toISOString(),
844
- }).catch(() => {});
845
- });
846
- worker.once("exit", (code) => {
847
- const status = code === 0 ? "completed" : "failed";
848
- appendLauncherLog(
849
- `Background SQLite seed refresh ${status} (${code ?? "unknown"}); inspect ${refreshLogPath}.`,
850
- );
851
- void writeJsonAtomic(refreshStatusPath, {
852
- status,
853
- exitCode: code ?? null,
854
- logPath: refreshLogPath,
855
- finishedAt: new Date().toISOString(),
856
- }).catch(() => {});
689
+ isRunning: backgroundRefreshIsRunning,
690
+ isProcessAlive,
691
+ lockPath: join(sessionDirectory, "dashboard-sqlite-refresh.lock"),
692
+ packageRoot,
693
+ randomBytes,
694
+ refreshLogPath,
695
+ refreshStatusPath,
696
+ sqliteDirectory: sqliteReadModelDirectory,
697
+ stopProcessTree,
698
+ workspaceRoot,
699
+ writeJsonAtomic,
857
700
  });
858
- worker.unref();
859
- appendLauncherLog(
860
- `Started background SQLite seed refresh at ${refreshPath}; cache ${sqliteSnapshotCacheDirectory}; worker log ${refreshLogPath}.`,
861
- );
862
701
  }
863
702
  async function writeLauncherStatus(status, message = "") {
864
703
  const path = process.env.ZAIP_LAUNCH_STATUS;
@@ -906,116 +745,162 @@ async function main() {
906
745
  phase: "Preparando a sessão do dashboard",
907
746
  detail: "Coordenando esta abertura local com segurança.",
908
747
  });
909
- await withLauncherLock(async () => {
910
- phaseTiming("lock_acquired", launchStartedAt);
911
- const buildStartedAt = Date.now();
912
- launchPage.setProgress({
913
- percent: 28,
914
- phase: "Verificando os artefatos do dashboard",
915
- detail: "Usando o build existente quando ele já está atualizado.",
916
- });
917
- const buildSignature = await ensureFreshDashboardBuild();
918
- phaseTiming("build_complete", buildStartedAt, { build_signature: buildSignature });
919
- // A second deliberate click must use the already healthy compatible
920
- // runtime. Build signature makes this safe across dashboard updates;
921
- // an incompatible session continues through a replacement handoff.
922
- const reusable = await findReusableDashboardSession({
923
- expectedBuildSignature: buildSignature,
924
- expectedSnapshotState: "current",
925
- });
926
- if (reusable !== null) {
927
- appendLauncherLog(
928
- `Reused healthy compatible dashboard session on port ${reusable.port}, PID ${reusable.serverPid ?? "unknown"}.`,
929
- );
930
- await redirectLaunchPage(launchPage, dashboardUrl(reusable.port, reusable.token));
931
- launchPage = null;
932
- await writeLauncherStatus("ok");
933
- return;
934
- }
935
- const previousSessions = await previousDashboardSessions();
936
- const excludedPorts = new Set([
937
- ...previousSessions.map((session) => session.port),
938
- launchPort,
939
- ]);
940
- const serverPath = await assertRuntimeReady();
941
- const readyStartedAt = Date.now();
942
- launchPage.setProgress({
943
- percent: 42,
944
- phase: "Abrindo o banco local descartável",
945
- detail: "Criando uma cópia privada do read model SQLite para esta sessão.",
946
- });
947
- const { child, port, dbPath, snapshotCache } = await startDashboardServer(
948
- serverPath,
949
- token,
950
- excludedPorts,
951
- null,
952
- (progress) => launchPage?.setProgress(progress),
953
- );
954
- phaseTiming("server_ready", readyStartedAt, { server_pid: child.pid, port, db_path: dbPath });
955
- ownedChild = child;
956
- try {
748
+ let reportedLockWait = false;
749
+ await withLauncherLock(
750
+ async () => {
751
+ phaseTiming("lock_acquired", launchStartedAt);
752
+ const buildStartedAt = Date.now();
957
753
  launchPage.setProgress({
958
- percent: 92,
959
- phase:
960
- snapshotCache === "stale_seed" ? "Abrindo snapshot local validado" : "Dashboard pronto",
961
- detail:
962
- snapshotCache === "stale_seed"
963
- ? "Os dados canônicos estão sendo atualizados em segundo plano para a próxima abertura."
964
- : "Validando saúde e dados iniciais antes de abrir a tela principal.",
754
+ percent: 28,
755
+ phase: "Verificando os artefatos do dashboard",
756
+ detail: "Usando o build existente quando ele já está atualizado.",
757
+ });
758
+ const buildSignature = await ensureFreshDashboardBuild();
759
+ phaseTiming("build_complete", buildStartedAt, { build_signature: buildSignature });
760
+ // A second deliberate click must use the already healthy compatible
761
+ // runtime. Build signature makes this safe across dashboard updates;
762
+ // an incompatible session continues through a replacement handoff.
763
+ const reusable = await findReusableDashboardSession({
764
+ expectedBuildSignature: buildSignature,
765
+ expectedSnapshotState: "current",
965
766
  });
966
- if (snapshotCache === "stale_seed") await delay(350);
967
- await redirectLaunchPage(
968
- launchPage,
969
- dashboardUrl(port, token, snapshotCache === "stale_seed"),
767
+ if (reusable !== null) {
768
+ appendLauncherLog(
769
+ `Reused healthy compatible dashboard session on port ${reusable.port}, PID ${reusable.serverPid ?? "unknown"}.`,
770
+ );
771
+ await redirectLaunchPage(launchPage, dashboardUrl(reusable.port, reusable.token));
772
+ launchPage = null;
773
+ await writeLauncherStatus("ok");
774
+ return;
775
+ }
776
+ // A stale seed is still a valid read-only dashboard while its one
777
+ // canonical refresh runs. Reuse it instead of turning repeated clicks
778
+ // into competing full SQLite rebuilds and private server generations.
779
+ if (await backgroundRefreshIsRunning()) {
780
+ const updating = await findReusableDashboardSession({
781
+ expectedBuildSignature: buildSignature,
782
+ expectedSnapshotState: "updating",
783
+ });
784
+ if (updating !== null) {
785
+ appendLauncherLog(
786
+ `Reused updating dashboard session on port ${updating.port}; canonical seed refresh is already running.`,
787
+ );
788
+ await redirectLaunchPage(launchPage, dashboardUrl(updating.port, updating.token, true));
789
+ launchPage = null;
790
+ await writeLauncherStatus("ok");
791
+ return;
792
+ }
793
+ }
794
+ const previousSessions = await previousDashboardSessions();
795
+ const excludedPorts = new Set([
796
+ ...previousSessions.map((session) => session.port),
797
+ launchPort,
798
+ ]);
799
+ const serverPath = await assertRuntimeReady();
800
+ const readyStartedAt = Date.now();
801
+ launchPage.setProgress({
802
+ percent: 42,
803
+ phase: "Abrindo o banco local descartável",
804
+ detail: "Criando uma cópia privada do read model SQLite para esta sessão.",
805
+ });
806
+ const { child, port, dbPath, snapshotCache } = await startDashboardServer(
807
+ serverPath,
808
+ token,
809
+ excludedPorts,
810
+ null,
811
+ (progress) => launchPage?.setProgress(progress),
970
812
  );
971
- phaseTiming("handoff_complete", launchStartedAt, { server_pid: child.pid, port });
972
- launchPage = null;
973
- if (snapshotCache === "stale_seed") refreshSnapshotSeedInBackground();
974
- currentSession = {
975
- kind: runtimeKind,
976
- workspace: workspaceRoot,
977
- workspaceKey,
978
- rootPid: process.pid,
979
- serverPid: child.pid,
813
+ phaseTiming("server_ready", readyStartedAt, {
814
+ server_pid: child.pid,
980
815
  port,
981
- token,
982
- dbPath,
983
- buildSignature,
984
- generationId: `${process.pid}-${child.pid}-${port}-${randomBytes(6).toString("hex")}`,
985
- // Never pin subsequent clicks to a stale seed. A completed
986
- // background refresh is consumed by a fresh handoff on the next
987
- // click, then the old updating generation is retired below.
988
- snapshotState: snapshotCache === "stale_seed" ? "updating" : "current",
989
- createdAt: new Date().toISOString(),
990
- };
991
- await writeDashboardSession(currentSession);
992
- await recordPortStatus(currentSession, "active");
993
- const warnings = [];
994
- for (const previous of previousSessions) {
995
- const status = await stopOwnedDashboard(previous);
996
- await recordPortStatus(previous, status);
997
- if (status === "closed") await removeGeneratedReadModel(previous.dbPath);
998
- if (teardownWarning(status))
999
- warnings.push(`não foi possível fechar a porta anterior ${previous.port}`);
1000
- else if (status === "skipped_unowned")
816
+ db_path: dbPath,
817
+ });
818
+ ownedChild = child;
819
+ try {
820
+ launchPage.setProgress({
821
+ percent: 92,
822
+ phase:
823
+ snapshotCache === "stale_seed"
824
+ ? "Abrindo snapshot local validado"
825
+ : "Dashboard pronto",
826
+ detail:
827
+ snapshotCache === "stale_seed"
828
+ ? "Os dados canônicos estão sendo atualizados em segundo plano para a próxima abertura."
829
+ : "Validando saúde e dados iniciais antes de abrir a tela principal.",
830
+ });
831
+ if (snapshotCache === "stale_seed") await delay(350);
832
+ await redirectLaunchPage(
833
+ launchPage,
834
+ dashboardUrl(port, token, snapshotCache === "stale_seed"),
835
+ );
836
+ phaseTiming("handoff_complete", launchStartedAt, { server_pid: child.pid, port });
837
+ launchPage = null;
838
+ if (snapshotCache === "stale_seed") await refreshSnapshotSeedInBackground();
839
+ currentSession = {
840
+ kind: runtimeKind,
841
+ workspace: workspaceRoot,
842
+ workspaceKey,
843
+ rootPid: process.pid,
844
+ serverPid: child.pid,
845
+ port,
846
+ token,
847
+ dbPath,
848
+ buildSignature,
849
+ generationId: `${process.pid}-${child.pid}-${port}-${randomBytes(6).toString("hex")}`,
850
+ // Never pin subsequent clicks to a stale seed. A completed
851
+ // background refresh is consumed by a fresh handoff on the next
852
+ // click, then the old updating generation is retired below.
853
+ snapshotState: snapshotCache === "stale_seed" ? "updating" : "current",
854
+ createdAt: new Date().toISOString(),
855
+ };
856
+ await writeDashboardSession(currentSession);
857
+ await recordPortStatus(currentSession, "active");
858
+ const warnings = [];
859
+ for (const previous of previousSessions) {
860
+ const status = await stopOwnedDashboard(previous);
861
+ await recordPortStatus(previous, status);
862
+ if (status === "closed") await removeGeneratedReadModel(previous.dbPath);
863
+ if (teardownWarning(status))
864
+ warnings.push(`não foi possível fechar a porta anterior ${previous.port}`);
865
+ else if (status === "skipped_unowned")
866
+ appendLauncherLog(
867
+ `Skipped teardown of port ${previous.port}: registered server PID does not own its live listener.`,
868
+ );
869
+ }
870
+ if (warnings.length > 0)
1001
871
  appendLauncherLog(
1002
- `Skipped teardown of port ${previous.port}: registered server PID does not own its live listener.`,
872
+ `Dashboard launched on port ${port}; prior-port cleanup: ${warnings.join("; ")}`,
1003
873
  );
874
+ await writeLauncherStatus("ok");
875
+ } catch (error) {
876
+ await stopProcessTree(child.pid);
877
+ if (currentSession !== null) await recordPortStatus(currentSession, "failed");
878
+ await clearOwnedDashboardSession();
879
+ throw error;
1004
880
  }
1005
- if (warnings.length > 0)
1006
- appendLauncherLog(
1007
- `Dashboard launched on port ${port}; prior-port cleanup: ${warnings.join("; ")}`,
1008
- );
1009
- await writeLauncherStatus("ok");
1010
- } catch (error) {
1011
- await stopProcessTree(child.pid);
1012
- if (currentSession !== null) await recordPortStatus(currentSession, "failed");
1013
- await clearOwnedDashboardSession();
1014
- throw error;
1015
- }
1016
- });
881
+ },
882
+ {
883
+ onWait: () => {
884
+ if (reportedLockWait) return;
885
+ reportedLockWait = true;
886
+ launchPage?.setProgress({
887
+ percent: 22,
888
+ phase: "Aguardando outra abertura do dashboard",
889
+ detail: "A outra sessão está concluindo o carregamento local antes desta continuar.",
890
+ });
891
+ },
892
+ },
893
+ );
1017
894
  } catch (error) {
1018
- if (launchPage !== null) await launchPage.close().catch(() => {});
895
+ if (launchPage !== null) {
896
+ launchPage.setFailure({
897
+ percent: 100,
898
+ phase: "Não foi possível preparar o dashboard",
899
+ detail: formatLauncherError(error instanceof Error ? error.message : String(error), 900),
900
+ });
901
+ launchPage.closeAfterFailureObservation();
902
+ launchPage = null;
903
+ }
1019
904
  throw error;
1020
905
  }
1021
906
  if (ownedChild === null) return;
@@ -1077,7 +962,11 @@ async function waitForDashboard(port, child) {
1077
962
  }
1078
963
  await delay(100);
1079
964
  }
1080
- throw new Error(`Timed out waiting for ${dashboardHealthUrl(port)}`);
965
+ // A cold read-model timeout is not a port collision. Retrying would start
966
+ // up to four more full builds and exceed the launcher deadline.
967
+ const timedOut = new Error(`Timed out waiting for ${dashboardHealthUrl(port)}`);
968
+ timedOut.retryable = false;
969
+ throw timedOut;
1081
970
  }
1082
971
  function exists(path) {
1083
972
  return access(path).then(