@zaip-ai/cli 2.2.0 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (420) hide show
  1. package/dist/services/adapters.d.ts +1 -1
  2. package/dist/services/adapters.d.ts.map +1 -1
  3. package/dist/services/adapters.js +23 -9
  4. package/dist/services/adapters.js.map +1 -1
  5. package/dist/services/agent-adapters.js +25 -9
  6. package/dist/services/agent-adapters.js.map +1 -1
  7. package/dist/services/agent-discovery.d.ts +27 -0
  8. package/dist/services/agent-discovery.d.ts.map +1 -0
  9. package/dist/services/agent-discovery.js +73 -0
  10. package/dist/services/agent-discovery.js.map +1 -0
  11. package/dist/services/codex-conversation-lifecycle-hook.d.ts.map +1 -1
  12. package/dist/services/codex-conversation-lifecycle-hook.js +153 -24
  13. package/dist/services/codex-conversation-lifecycle-hook.js.map +1 -1
  14. package/dist/services/codex-pre-tool-hook-template.d.ts.map +1 -1
  15. package/dist/services/codex-pre-tool-hook-template.js +146 -72
  16. package/dist/services/codex-pre-tool-hook-template.js.map +1 -1
  17. package/dist/services/codex-rules.d.ts +1 -1
  18. package/dist/services/codex-rules.d.ts.map +1 -1
  19. package/dist/services/codex-rules.js +3 -3
  20. package/dist/services/codex-subagent-hook-template.d.ts +1 -1
  21. package/dist/services/codex-subagent-hook-template.d.ts.map +1 -1
  22. package/dist/services/codex-subagent-hook-template.js +117 -66
  23. package/dist/services/codex-subagent-hook-template.js.map +1 -1
  24. package/dist/services/install-agents-template.d.ts.map +1 -1
  25. package/dist/services/install-agents-template.js +56 -6
  26. package/dist/services/install-agents-template.js.map +1 -1
  27. package/dist/services/logger.js +1 -1
  28. package/dist/services/logger.js.map +1 -1
  29. package/dist/services/logger.test.js +22 -1
  30. package/dist/services/logger.test.js.map +1 -1
  31. package/dist/services/runtime-template.test.js +129 -0
  32. package/dist/services/runtime-template.test.js.map +1 -1
  33. package/package.json +1 -1
  34. package/templates/.agents/.env.example +9 -3
  35. package/templates/.agents/README-ENG.md +30 -5
  36. package/templates/.agents/README.md +30 -7
  37. package/templates/.agents/architecture/software-architecture.md +1 -59
  38. package/templates/.agents/branding/brandbook.md +3 -0
  39. package/templates/.agents/config/agents/catalog.yaml +22 -10
  40. package/templates/.agents/config/agents/discovery.json +319 -0
  41. package/templates/.agents/config/clients/codex/agents/zaip-agent-backend.toml +2 -2
  42. package/templates/.agents/config/clients/codex/agents/zaip-agent-builder.toml +2 -2
  43. package/templates/.agents/config/clients/codex/agents/zaip-agent-cloud-engineer.toml +11 -8
  44. package/templates/.agents/config/clients/codex/agents/zaip-agent-designer.toml +2 -2
  45. package/templates/.agents/config/clients/codex/agents/zaip-agent-frontend.toml +2 -2
  46. package/templates/.agents/config/clients/codex/agents/zaip-agent-task-executor.toml +2 -2
  47. package/templates/.agents/config/clients/codex/agents/zaip-agent-website-developer.toml +2 -2
  48. package/templates/.agents/config/pricing/models.json +9 -5
  49. package/templates/.agents/config/project.yaml +12 -1
  50. package/templates/.agents/dashboard/.zaip-managed.json +375 -95
  51. package/templates/.agents/dashboard/apps/frontend/dist/assets/CostCalculationGuidePage-CGxeQak4.js +1 -0
  52. package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog-DaSllaJp.js → DashboardHelpDialog-tzS7YxYB.js} +57 -33
  53. package/templates/.agents/dashboard/apps/frontend/dist/assets/PricingSettingsDialog-DEbCi1gP.js +1 -0
  54. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-1L7Qw-Gq.js +3 -0
  55. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-EInWr641.css +1 -0
  56. package/templates/.agents/dashboard/apps/frontend/dist/assets/TraceabilityWorkspace-t1Om0Abw.js +1 -0
  57. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-B6aZfo5f.js +68 -0
  58. package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-TPUT2IUt.js → icons-vendor-DiGC-dsJ.js} +1 -1
  59. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-DgTmNHN5.js +2 -0
  60. package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-Ct48XHGL.js → react-vendor-kPO6yNed.js} +1 -1
  61. package/templates/.agents/dashboard/apps/frontend/dist/assets/rolldown-runtime-Bh1tDfsg.js +1 -0
  62. package/templates/.agents/dashboard/apps/frontend/dist/assets/traceability-DQpKX9c1.js +1 -0
  63. package/templates/.agents/dashboard/apps/frontend/dist/index.html +6 -6
  64. package/templates/.agents/dashboard/apps/frontend/package.json +1 -1
  65. package/templates/.agents/dashboard/apps/frontend/src/App.tsx +3 -1264
  66. package/templates/.agents/dashboard/apps/frontend/src/AppDashboardContent.tsx +363 -0
  67. package/templates/.agents/dashboard/apps/frontend/src/ProjectSettingsPage.dom.test.ts +99 -0
  68. package/templates/.agents/dashboard/apps/frontend/src/ProjectSettingsPage.tsx +62 -6
  69. package/templates/.agents/dashboard/apps/frontend/src/ProjectStackEditor.tsx +3 -1
  70. package/templates/.agents/dashboard/apps/frontend/src/__tests__/app-composition-support.ts +7 -0
  71. package/templates/.agents/dashboard/apps/frontend/src/cost-period-refresh.test.ts +9 -7
  72. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api-settings.ts +151 -0
  73. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.ts +28 -153
  74. package/templates/.agents/dashboard/apps/frontend/src/dashboard-app-shell.tsx +33 -0
  75. package/templates/.agents/dashboard/apps/frontend/src/dashboard-cost-facts.ts +30 -0
  76. package/templates/.agents/dashboard/apps/frontend/src/dashboard-project-overview.ts +7 -1
  77. package/templates/.agents/dashboard/apps/frontend/src/dashboard-retry.ts +17 -0
  78. package/templates/.agents/dashboard/apps/frontend/src/dashboard-setup-route.ts +29 -0
  79. package/templates/.agents/dashboard/apps/frontend/src/dashboard-view-actions.ts +66 -0
  80. package/templates/.agents/dashboard/apps/frontend/src/operations-period-render.test.ts +3 -2
  81. package/templates/.agents/dashboard/apps/frontend/src/overview-status-page.test.ts +2 -2
  82. package/templates/.agents/dashboard/apps/frontend/src/project-installation-status.test.ts +48 -0
  83. package/templates/.agents/dashboard/apps/frontend/src/project-installation-status.ts +29 -0
  84. package/templates/.agents/dashboard/apps/frontend/src/project-settings-page.css +68 -0
  85. package/templates/.agents/dashboard/apps/frontend/src/project-settings.test.ts +2 -2
  86. package/templates/.agents/dashboard/apps/frontend/src/sao-paulo-day-rollover.test.ts +36 -0
  87. package/templates/.agents/dashboard/apps/frontend/src/sao-paulo-day-rollover.ts +30 -0
  88. package/templates/.agents/dashboard/apps/frontend/src/snapshot-banner-semantics.test.ts +2 -5
  89. package/templates/.agents/dashboard/apps/frontend/src/snapshot-coherence.test.ts +30 -19
  90. package/templates/.agents/dashboard/apps/frontend/src/use-app-effects.ts +9 -1
  91. package/templates/.agents/dashboard/apps/frontend/src/use-app-trace-task-pagination.ts +77 -0
  92. package/templates/.agents/dashboard/apps/frontend/src/use-app-work-board.ts +397 -0
  93. package/templates/.agents/dashboard/apps/frontend/src/use-dashboard-app-model.tsx +782 -0
  94. package/templates/.agents/dashboard/apps/frontend/src/use-snapshot-notice.tsx +49 -0
  95. package/templates/.agents/dashboard/modules/cost-monitor/package.json +1 -1
  96. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostDeveloperPanelSlot.tsx +16 -0
  97. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostGroupsTable.tsx +459 -0
  98. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.test.ts +27 -17
  99. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.tsx +62 -186
  100. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorRunDialog.tsx +20 -0
  101. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables-hides-calls-that-have-no-collected.test.ts +80 -0
  102. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables-keeps-legacy-codex-identity-fields-blank.test.ts +455 -0
  103. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.test.ts +18 -525
  104. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.tsx +32 -508
  105. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-export-sources.ts +74 -0
  106. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-page-actions.ts +19 -0
  107. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-page-selectors.ts +49 -0
  108. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-table-formatters.ts +115 -0
  109. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-monitor-projection.test-fixtures.ts +2 -0
  110. package/templates/.agents/dashboard/modules/operations/package.json +1 -1
  111. package/templates/.agents/dashboard/modules/operations/src/contracts.ts +4 -0
  112. package/templates/.agents/dashboard/modules/operations/src/react/EntityTableBadges.tsx +34 -0
  113. package/templates/.agents/dashboard/modules/operations/src/react/EntityTables.tsx +8 -38
  114. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview-keeps-structure-classes-mutually-exclusive.test.ts +243 -0
  115. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.test.ts +27 -275
  116. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.tsx +70 -247
  117. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverviewCharts.tsx +171 -0
  118. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverviewPanels.tsx +64 -0
  119. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPage.tsx +4 -3
  120. package/templates/.agents/dashboard/modules/operations/src/react/PrdDocumentDialog.test.tsx +2 -1
  121. package/templates/.agents/dashboard/modules/operations/src/react/manual-work-priority.test.ts +2 -1
  122. package/templates/.agents/dashboard/modules/operations/src/react/operations-coverage-toast.test.ts +6 -2
  123. package/templates/.agents/dashboard/modules/operations/src/react/operations-overview-period-filter.test.ts +6 -14
  124. package/templates/.agents/dashboard/modules/operations/src/react/operations-overview.status-cards.test.ts +3 -1
  125. package/templates/.agents/dashboard/modules/operations/src/react/operations-page-contract.ts +2 -0
  126. package/templates/.agents/dashboard/modules/operations/src/react/overview-metric-format.ts +32 -0
  127. package/templates/.agents/dashboard/modules/traceability/package.json +1 -1
  128. package/templates/.agents/dashboard/packages/branding/package.json +1 -1
  129. package/templates/.agents/dashboard/packages/branding/src/index.ts +1 -1
  130. package/templates/.agents/dashboard/packages/contracts/package.json +1 -1
  131. package/templates/.agents/dashboard/packages/data-export/package.json +1 -1
  132. package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +1458 -1458
  133. package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +1425 -1425
  134. package/templates/.agents/dashboard/packages/local-runtime/package.json +2 -1
  135. package/templates/.agents/dashboard/packages/local-runtime/scripts/build-sqlite-runtime.mjs +7 -3
  136. package/templates/.agents/dashboard/packages/local-runtime/server.mjs +1458 -1459
  137. package/templates/.agents/dashboard/packages/local-runtime/src/active-codex-telemetry.ts +25 -0
  138. package/templates/.agents/dashboard/packages/local-runtime/src/agent-catalog-settings.test.ts +1 -1
  139. package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.test.ts +53 -8
  140. package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.ts +79 -24
  141. package/templates/.agents/dashboard/packages/local-runtime/src/projection-refresh-diagnostics.ts +35 -0
  142. package/templates/.agents/dashboard/packages/local-runtime/src/server-costs-routes.ts +168 -0
  143. package/templates/.agents/dashboard/packages/local-runtime/src/server-dual-reads-manifest-summaries-without-losing.test.ts +344 -0
  144. package/templates/.agents/dashboard/packages/local-runtime/src/server-http-contracts.ts +31 -0
  145. package/templates/.agents/dashboard/packages/local-runtime/src/server-operations-routes.ts +345 -0
  146. package/templates/.agents/dashboard/packages/local-runtime/src/server-read-projections.ts +461 -0
  147. package/templates/.agents/dashboard/packages/local-runtime/src/server-reconciliation-refresh.test.ts +78 -0
  148. package/templates/.agents/dashboard/packages/local-runtime/src/server-route-context.ts +13 -0
  149. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-synchronization.test.ts +18 -0
  150. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-synchronization.ts +19 -3
  151. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot.test.ts +22 -21
  152. package/templates/.agents/dashboard/packages/local-runtime/src/server-sqlite-view-allowlist.test.ts +25 -0
  153. package/templates/.agents/dashboard/packages/local-runtime/src/server-trace-routes.ts +64 -0
  154. package/templates/.agents/dashboard/packages/local-runtime/src/server-work-reconciliation.test.ts +2 -30
  155. package/templates/.agents/dashboard/packages/local-runtime/src/server-work-reconciliation.ts +6 -23
  156. package/templates/.agents/dashboard/packages/local-runtime/src/server.test-fixtures.ts +1 -1
  157. package/templates/.agents/dashboard/packages/local-runtime/src/server.test.ts +2 -330
  158. package/templates/.agents/dashboard/packages/local-runtime/src/server.ts +73 -1060
  159. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-materialize-first-fold.ts +78 -0
  160. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-materialize-projections.ts +561 -0
  161. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-materialize-runs.ts +600 -0
  162. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-metric-contract.test.ts +1 -1
  163. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-query-agent-dimension.ts +269 -0
  164. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-query-context.ts +32 -0
  165. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-query-entity-group.ts +244 -0
  166. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-query-filtered-dimension.ts +292 -0
  167. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-query-financial.ts +547 -0
  168. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-query-model-dimension.ts +360 -0
  169. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-query-provider-dimension.ts +169 -0
  170. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-query-summary.ts +475 -0
  171. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-query-temporal-dimension.ts +132 -0
  172. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-query-timing.ts +299 -0
  173. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-query-view-page.ts +692 -0
  174. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-counts-every-root-execution-day-returns.test.ts +739 -0
  175. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-does-not-convert-delayed-historical-finalizer.test.ts +706 -0
  176. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-filesystem.ts +748 -0
  177. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-first-fold-types.ts +1 -1
  178. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-keeps-orphan-telemetry-out-kanban-without.test.ts +710 -0
  179. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-marks-token-cost-telemetry-unavailable-when.test.ts +618 -0
  180. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-rebuild.ts +755 -0
  181. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-rebuilds-disposable-snapshot-serves-zaip-records.test.ts +276 -0
  182. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-schema.ts +193 -0
  183. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-types.ts +123 -0
  184. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.test.ts +1 -2805
  185. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.ts +17 -6930
  186. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-schema-base-views.ts +385 -0
  187. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-schema-dashboard-views.ts +412 -0
  188. package/templates/.agents/dashboard/packages/local-runtime/src/work-priority-contract.test.ts +4 -3
  189. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-build.mjs +195 -0
  190. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-lifecycle.test.mjs +2 -7
  191. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-logging.mjs +35 -0
  192. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-navigation.mjs +114 -0
  193. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-paths.mjs +90 -0
  194. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.mjs +89 -418
  195. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.test.mjs +22 -12
  196. package/templates/.agents/dashboard/packages/ui/package.json +1 -1
  197. package/templates/.agents/dashboard/tests/e2e/package.json +1 -1
  198. package/templates/.agents/dashboard/tests/e2e/playwright.config.ts +4 -1
  199. package/templates/.agents/design/.zaip-managed.json +5 -5
  200. package/templates/.agents/design/DESIGN.md +2 -186
  201. package/templates/.agents/design/README.md +12 -2
  202. package/templates/.agents/design/apps/catalog/package.json +1 -1
  203. package/templates/.agents/design/packages/local-runtime/package.json +1 -1
  204. package/templates/.agents/growth/strategy.md +3 -0
  205. package/templates/.agents/marketing/strategy.md +3 -0
  206. package/templates/.agents/requirements/autonomous-assurance.md +2 -1
  207. package/templates/.agents/requirements/cloud-hostinger-autonomy.md +1 -1
  208. package/templates/.agents/requirements/comparative-audit.md +1 -1
  209. package/templates/.agents/requirements/context-engine.md +1 -1
  210. package/templates/.agents/requirements/cost-observability.md +1 -1
  211. package/templates/.agents/requirements/dashboard-launch.md +1 -1
  212. package/templates/.agents/requirements/data-export.md +1 -1
  213. package/templates/.agents/requirements/design-system.md +13 -4
  214. package/templates/.agents/requirements/execution-engine.md +1 -1
  215. package/templates/.agents/requirements/kanban-dashboard.md +1 -1
  216. package/templates/.agents/requirements/model-orchestration.md +1 -1
  217. package/templates/.agents/requirements/public-distribution.md +4 -2
  218. package/templates/.agents/requirements/software-architecture.md +1 -1
  219. package/templates/.agents/requirements/strategic-improvements.md +1 -1
  220. package/templates/.agents/requirements/work-lifecycle.md +3 -3
  221. package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
  222. package/templates/.agents/rules/architecture.md +2 -0
  223. package/templates/.agents/rules/astra-diagnostic.md +1 -1
  224. package/templates/.agents/rules/autonomy-decision.md +3 -1
  225. package/templates/.agents/rules/cloud-hostinger-autonomy.md +2 -0
  226. package/templates/.agents/rules/context.md +2 -1
  227. package/templates/.agents/rules/core.md +3 -3
  228. package/templates/.agents/rules/design-system-central-contract.md +17 -0
  229. package/templates/.agents/rules/evolution.md +3 -1
  230. package/templates/.agents/rules/frontend-lite.md +3 -1
  231. package/templates/.agents/rules/local-sqlite-storage.md +2 -2
  232. package/templates/.agents/rules/meta-mcp.md +3 -1
  233. package/templates/.agents/rules/model-orchestration.md +1 -1
  234. package/templates/.agents/rules/run-lifecycle-closure.md +6 -4
  235. package/templates/.agents/rules/security.md +3 -1
  236. package/templates/.agents/rules/specialist-routing.md +3 -1
  237. package/templates/.agents/rules/text-writing-utf8.md +1 -1
  238. package/templates/.agents/rules/workflow.md +7 -5
  239. package/templates/.agents/runtime/VERSION +1 -1
  240. package/templates/.agents/runtime/bin/zaip.mjs +519 -247
  241. package/templates/.agents/runtime/checksums.sha256 +6 -6
  242. package/templates/.agents/runtime/manifest.json +1 -1
  243. package/templates/.agents/runtime/pnpm-lock.sha256 +1 -1
  244. package/templates/.agents/runtime/provenance.json +5 -5
  245. package/templates/.agents/runtime/sbom.cdx.json +1 -1
  246. package/templates/.agents/sales/playbook.md +3 -0
  247. package/templates/.agents/skills/zaip-aios/SKILL.md +5 -3
  248. package/templates/.agents/skills/zaip-skill-adapt-stack/SKILL.md +1 -0
  249. package/templates/.agents/skills/zaip-skill-api-contracts/SKILL.md +14 -0
  250. package/templates/.agents/skills/zaip-skill-api-contracts/agents/openai.yaml +4 -0
  251. package/templates/.agents/skills/zaip-skill-application-observability/SKILL.md +15 -0
  252. package/templates/.agents/skills/zaip-skill-application-observability/agents/openai.yaml +4 -0
  253. package/templates/.agents/skills/zaip-skill-application-observability/references/signal-design.md +3 -0
  254. package/templates/.agents/skills/zaip-skill-application-readiness/SKILL.md +15 -0
  255. package/templates/.agents/skills/zaip-skill-application-readiness/agents/openai.yaml +4 -0
  256. package/templates/.agents/skills/zaip-skill-application-readiness/references/evidence-matrix.md +3 -0
  257. package/templates/.agents/skills/zaip-skill-architecture-design/SKILL.md +14 -0
  258. package/templates/.agents/skills/zaip-skill-architecture-design/agents/openai.yaml +4 -0
  259. package/templates/.agents/skills/zaip-skill-async-jobs/SKILL.md +14 -0
  260. package/templates/.agents/skills/zaip-skill-async-jobs/agents/openai.yaml +4 -0
  261. package/templates/.agents/skills/zaip-skill-auth-tenancy/SKILL.md +15 -0
  262. package/templates/.agents/skills/zaip-skill-auth-tenancy/agents/openai.yaml +4 -0
  263. package/templates/.agents/skills/zaip-skill-auth-tenancy/references/data-access-paths.md +9 -0
  264. package/templates/.agents/skills/zaip-skill-auth-tenancy/references/worker-scenario.md +5 -0
  265. package/templates/.agents/skills/zaip-skill-auth-tenancy/scripts/worker-contract.test.mjs +27 -0
  266. package/templates/.agents/skills/zaip-skill-author-skills/SKILL.md +2 -0
  267. package/templates/.agents/skills/zaip-skill-author-skills/references/engineering-skill-index.md +9 -0
  268. package/templates/.agents/skills/zaip-skill-author-skills/scripts/audit-skill-catalog.mjs +5 -3
  269. package/templates/.agents/skills/zaip-skill-author-skills/scripts/author-skill-scripts.test.mjs +27 -0
  270. package/templates/.agents/skills/zaip-skill-backend/SKILL.md +4 -0
  271. package/templates/.agents/skills/zaip-skill-backend/references/deployment-environments.md +75 -0
  272. package/templates/.agents/skills/zaip-skill-backend/references/supabase-migration-contract.md +23 -0
  273. package/templates/.agents/skills/zaip-skill-brand-governance/SKILL.md +2 -0
  274. package/templates/.agents/skills/zaip-skill-cache-strategy/SKILL.md +14 -0
  275. package/templates/.agents/skills/zaip-skill-cache-strategy/agents/openai.yaml +4 -0
  276. package/templates/.agents/skills/zaip-skill-change/SKILL.md +1 -1
  277. package/templates/.agents/skills/zaip-skill-cloud-engineering/SKILL.md +7 -0
  278. package/templates/.agents/skills/zaip-skill-cloudflare-github-deploy/SKILL.md +37 -0
  279. package/templates/.agents/skills/zaip-skill-cloudflare-github-deploy/agents/openai.yaml +6 -0
  280. package/templates/.agents/skills/zaip-skill-cloudflare-github-deploy/references/workers-static-assets.md +26 -0
  281. package/templates/.agents/skills/zaip-skill-cloudflare-github-deploy/references/workflow-templates.md +71 -0
  282. package/templates/.agents/skills/zaip-skill-cloudflare-github-deploy/scripts/cloudflare-github-deploy-contract.test.mjs +77 -0
  283. package/templates/.agents/skills/zaip-skill-config/SKILL.md +21 -11
  284. package/templates/.agents/skills/zaip-skill-config/agents/openai.yaml +2 -2
  285. package/templates/.agents/skills/zaip-skill-container-runtime/SKILL.md +14 -0
  286. package/templates/.agents/skills/zaip-skill-container-runtime/agents/openai.yaml +4 -0
  287. package/templates/.agents/skills/zaip-skill-context/SKILL.md +6 -1
  288. package/templates/.agents/skills/zaip-skill-context/references/prd-spec-task-handoff.md +10 -0
  289. package/templates/.agents/skills/zaip-skill-context/references/rule-selection.md +20 -0
  290. package/templates/.agents/skills/zaip-skill-cqrs/SKILL.md +1 -0
  291. package/templates/.agents/skills/zaip-skill-cybersecurity/SKILL.md +18 -0
  292. package/templates/.agents/skills/zaip-skill-cybersecurity/agents/openai.yaml +4 -0
  293. package/templates/.agents/skills/zaip-skill-cybersecurity/references/audit-contract.md +94 -0
  294. package/templates/.agents/skills/zaip-skill-cybersecurity/scripts/score-audit.mjs +185 -0
  295. package/templates/.agents/skills/zaip-skill-cybersecurity/scripts/score-audit.test.mjs +101 -0
  296. package/templates/.agents/skills/zaip-skill-data-modeling/SKILL.md +14 -0
  297. package/templates/.agents/skills/zaip-skill-data-modeling/agents/openai.yaml +4 -0
  298. package/templates/.agents/skills/zaip-skill-database-migrations/SKILL.md +20 -0
  299. package/templates/.agents/skills/zaip-skill-database-migrations/agents/openai.yaml +4 -0
  300. package/templates/.agents/skills/zaip-skill-database-migrations/references/manual-mode.md +15 -0
  301. package/templates/.agents/skills/zaip-skill-database-migrations/references/promotion-example.md +5 -0
  302. package/templates/.agents/skills/zaip-skill-database-migrations/scripts/export-manual-sql.mjs +145 -0
  303. package/templates/.agents/skills/zaip-skill-database-migrations/scripts/export-manual-sql.test.mjs +178 -0
  304. package/templates/.agents/skills/zaip-skill-dependency-upgrade/SKILL.md +14 -0
  305. package/templates/.agents/skills/zaip-skill-dependency-upgrade/agents/openai.yaml +4 -0
  306. package/templates/.agents/skills/zaip-skill-deployment-pipeline/SKILL.md +15 -0
  307. package/templates/.agents/skills/zaip-skill-deployment-pipeline/agents/openai.yaml +4 -0
  308. package/templates/.agents/skills/zaip-skill-deployment-pipeline/references/runtime-shapes.md +3 -0
  309. package/templates/.agents/skills/zaip-skill-design-system/SKILL.md +20 -9
  310. package/templates/.agents/skills/zaip-skill-dev-environment/SKILL.md +14 -0
  311. package/templates/.agents/skills/zaip-skill-dev-environment/agents/openai.yaml +4 -0
  312. package/templates/.agents/skills/zaip-skill-dev-environment/references/inventory-contract.md +7 -0
  313. package/templates/.agents/skills/zaip-skill-dev-environment/scripts/inventory.mjs +77 -0
  314. package/templates/.agents/skills/zaip-skill-disaster-recovery/SKILL.md +15 -0
  315. package/templates/.agents/skills/zaip-skill-disaster-recovery/agents/openai.yaml +4 -0
  316. package/templates/.agents/skills/zaip-skill-disaster-recovery/references/recovery-scope.md +3 -0
  317. package/templates/.agents/skills/zaip-skill-execution/SKILL.md +3 -3
  318. package/templates/.agents/skills/zaip-skill-execution/references/correction-cycle.md +7 -0
  319. package/templates/.agents/skills/zaip-skill-execution/references/runtime-governance.md +1 -5
  320. package/templates/.agents/skills/zaip-skill-external-integrations/SKILL.md +14 -0
  321. package/templates/.agents/skills/zaip-skill-external-integrations/agents/openai.yaml +4 -0
  322. package/templates/.agents/skills/zaip-skill-frontend/SKILL.md +4 -0
  323. package/templates/.agents/skills/zaip-skill-frontend-componentization/SKILL.md +44 -0
  324. package/templates/.agents/skills/zaip-skill-frontend-componentization/agents/openai.yaml +4 -0
  325. package/templates/.agents/skills/zaip-skill-frontend-data-flows/SKILL.md +14 -0
  326. package/templates/.agents/skills/zaip-skill-frontend-data-flows/agents/openai.yaml +4 -0
  327. package/templates/.agents/skills/zaip-skill-general-operations/SKILL.md +2 -0
  328. package/templates/.agents/skills/zaip-skill-github-actions-setup/SKILL.md +36 -0
  329. package/templates/.agents/skills/zaip-skill-github-actions-setup/agents/openai.yaml +4 -0
  330. package/templates/.agents/skills/zaip-skill-github-actions-setup/references/cloudflare-pages.md +33 -0
  331. package/templates/.agents/skills/zaip-skill-github-actions-setup/references/github.md +63 -0
  332. package/templates/.agents/skills/zaip-skill-github-actions-setup/references/hostinger-vps.md +55 -0
  333. package/templates/.agents/skills/zaip-skill-github-actions-setup/references/npm.md +30 -0
  334. package/templates/.agents/skills/zaip-skill-github-actions-setup/scripts/check-skill.mjs +55 -0
  335. package/templates/.agents/skills/zaip-skill-github-actions-setup/scripts/verify-cloudflare.mjs +61 -0
  336. package/templates/.agents/skills/zaip-skill-github-actions-setup/scripts/verify-cloudflare.test.mjs +128 -0
  337. package/templates/.agents/skills/zaip-skill-github-actions-setup/scripts/verify-ssh.integration.test.mjs +106 -0
  338. package/templates/.agents/skills/zaip-skill-github-deploy/SKILL.md +43 -76
  339. package/templates/.agents/skills/zaip-skill-github-deploy/references/deployment-workflow.md +131 -0
  340. package/templates/.agents/skills/zaip-skill-github-deploy/references/github-release-workflow-template.md +31 -0
  341. package/templates/.agents/skills/zaip-skill-github-deploy/references/release-gate-matrix.md +24 -0
  342. package/templates/.agents/skills/zaip-skill-github-deploy/references/release-identity.md +44 -0
  343. package/templates/.agents/skills/zaip-skill-github-deploy/references/release-notes.md +134 -0
  344. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/consumer-release-preparation.mjs +389 -0
  345. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/consumer-release-preparation.test.mjs +134 -0
  346. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/github-deploy-contract.test.mjs +25 -2
  347. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/github-readiness-api.test.mjs +134 -0
  348. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/github-readiness.mjs +98 -0
  349. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/github-readiness.test.mjs +25 -0
  350. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/install-github-readiness-workflow.mjs +159 -0
  351. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/install-github-readiness-workflow.test.mjs +160 -0
  352. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/preflight.mjs +149 -12
  353. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/preflight.test.mjs +64 -5
  354. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/release-notes-coverage.test.mjs +126 -0
  355. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/resolve-release-identity.mjs +78 -0
  356. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/resolve-release-identity.test.mjs +51 -0
  357. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/validate-release-notes.mjs +184 -0
  358. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/validate-release-notes.test.mjs +109 -0
  359. package/templates/.agents/skills/zaip-skill-growth-strategy-revops/SKILL.md +2 -0
  360. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/SKILL.md +18 -10
  361. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/agents/openai.yaml +3 -4
  362. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/references/aws-cli-terraform.md +62 -0
  363. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/references/guia-iniciante-hostinger-vps.md +183 -0
  364. package/templates/.agents/skills/zaip-skill-marketing/SKILL.md +2 -0
  365. package/templates/.agents/skills/zaip-skill-module-scaffold/SKILL.md +14 -0
  366. package/templates/.agents/skills/zaip-skill-module-scaffold/agents/openai.yaml +4 -0
  367. package/templates/.agents/skills/zaip-skill-module-scaffold/references/scaffold-contract.md +9 -0
  368. package/templates/.agents/skills/zaip-skill-module-scaffold/scripts/helpers.test.mjs +121 -0
  369. package/templates/.agents/skills/zaip-skill-module-scaffold/scripts/scaffold.mjs +169 -0
  370. package/templates/.agents/skills/zaip-skill-move-prd/SKILL.md +1 -1
  371. package/templates/.agents/skills/zaip-skill-move-spec/SKILL.md +1 -1
  372. package/templates/.agents/skills/zaip-skill-performance-engineering/SKILL.md +14 -0
  373. package/templates/.agents/skills/zaip-skill-performance-engineering/agents/openai.yaml +4 -0
  374. package/templates/.agents/skills/zaip-skill-prd/SKILL.md +1 -1
  375. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-local-telemetry.mjs +1 -0
  376. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-lib.mjs +26 -9
  377. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-lib.test.mjs +39 -13
  378. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-remeasurement.mjs +1 -0
  379. package/templates/.agents/skills/zaip-skill-refactor-landing-page/SKILL.md +66 -0
  380. package/templates/.agents/skills/zaip-skill-refactor-landing-page/agents/openai.yaml +4 -0
  381. package/templates/.agents/skills/zaip-skill-refactor-landing-page/references/behavioral-dry-run.md +23 -0
  382. package/templates/.agents/skills/zaip-skill-release-qualification/SKILL.md +40 -1
  383. package/templates/.agents/skills/zaip-skill-release-qualification/references/local-preparation.md +98 -0
  384. package/templates/.agents/skills/zaip-skill-review/SKILL.md +1 -0
  385. package/templates/.agents/skills/zaip-skill-rules/SKILL.md +3 -3
  386. package/templates/.agents/skills/zaip-skill-sales-methodology/SKILL.md +2 -0
  387. package/templates/.agents/skills/zaip-skill-setup/SKILL.md +19 -21
  388. package/templates/.agents/skills/zaip-skill-setup/agents/openai.yaml +2 -2
  389. package/templates/.agents/skills/zaip-skill-spec/SKILL.md +3 -2
  390. package/templates/.agents/skills/zaip-skill-storage-uploads/SKILL.md +14 -0
  391. package/templates/.agents/skills/zaip-skill-storage-uploads/agents/openai.yaml +4 -0
  392. package/templates/.agents/skills/zaip-skill-systematic-debugging/SKILL.md +14 -0
  393. package/templates/.agents/skills/zaip-skill-systematic-debugging/agents/openai.yaml +4 -0
  394. package/templates/.agents/skills/zaip-skill-task/SKILL.md +16 -7
  395. package/templates/.agents/skills/zaip-skill-task/references/commit-policy.md +25 -0
  396. package/templates/.agents/skills/zaip-skill-test-data/SKILL.md +14 -0
  397. package/templates/.agents/skills/zaip-skill-test-data/agents/openai.yaml +4 -0
  398. package/templates/.agents/skills/zaip-skill-test-data/scripts/generate.mjs +72 -0
  399. package/templates/.agents/skills/zaip-skill-testing-strategy/SKILL.md +15 -0
  400. package/templates/.agents/skills/zaip-skill-testing-strategy/agents/openai.yaml +4 -0
  401. package/templates/.agents/skills/zaip-skill-testing-strategy/references/example-map.md +3 -0
  402. package/templates/.agents/skills/zaip-skill-website-readiness/SKILL.md +8 -0
  403. package/templates/.agents/zaip-aios/VERSION +1 -1
  404. package/templates/.agents/zaip-aios/guides/10-seguranca.md +2 -1
  405. package/templates/.agents/zaip-aios/guides/20-instalacao-detalhada.md +5 -3
  406. package/templates/scripts/validate-cloud-hostinger-contract.mjs +34 -0
  407. package/templates/template-manifest.json +955 -211
  408. package/templates/.agents/branding/.gitkeep +0 -0
  409. package/templates/.agents/dashboard/apps/frontend/dist/assets/CostCalculationGuidePage-snUCQYTC.js +0 -1
  410. package/templates/.agents/dashboard/apps/frontend/dist/assets/PricingSettingsDialog-BPtVUGgs.js +0 -1
  411. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-3Awyaovx.js +0 -3
  412. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-GgzpZ_PY.css +0 -1
  413. package/templates/.agents/dashboard/apps/frontend/dist/assets/TraceabilityWorkspace-BCvZFMEf.js +0 -1
  414. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-DbaLbU2q.js +0 -68
  415. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-_MEktQNM.js +0 -2
  416. package/templates/.agents/dashboard/apps/frontend/dist/assets/rolldown-runtime-D9-fqq9M.js +0 -1
  417. package/templates/.agents/dashboard/apps/frontend/dist/assets/traceability-BDe4_Ubl.js +0 -1
  418. package/templates/.agents/growth/.gitkeep +0 -0
  419. package/templates/.agents/marketing/.gitkeep +0 -0
  420. package/templates/.agents/sales/.gitkeep +0 -0
@@ -1,79 +1,5 @@
1
- import type { CostMonitorProjection } from "@zaip-ai/dashboard-cost-monitor/contracts";
2
- import type { CostCurrency, CostLocale } from "@zaip-ai/dashboard-cost-monitor/react";
3
- import type { OperationsProjection } from "@zaip-ai/dashboard-operations/contracts";
4
- import { OperationsPage } from "@zaip-ai/dashboard-operations/react";
5
- import { useCallback, useEffect, useMemo, useRef, useState } from "react";
6
- import { AppLoadingBoundary } from "./AppLoadingBoundary.js";
7
- import type { LoadingArea } from "./AppLoadingPanels.js";
8
- import { AppOverlays } from "./AppOverlays.js";
9
- import {
10
- isAbortError,
11
- isCurrentCostSummaryRequest,
12
- isCurrentDashboardRequest,
13
- isCurrentOperationsPeriodRequest,
14
- isCurrentPeriodTransition,
15
- mergeBoardPages,
16
- PeriodTransitionOverlay,
17
- PeriodTransitionShell,
18
- shouldLoadCostSummary,
19
- } from "./AppRequestGuards.js";
20
- import {
21
- costCalculationGuidePath,
22
- pricingSetupPath,
23
- projectSetupPath,
24
- renderAppSetupRoute,
25
- } from "./AppSetupRoutes.js";
26
- import {
27
- type BoardStageState,
28
- initialWorkBoardStages,
29
- type OverviewClassification,
30
- type OverviewClassificationPages,
31
- type OverviewStatusKind,
32
- type OverviewStatusPages,
33
- overviewClassificationPageKey,
34
- overviewStatusPageKey,
35
- traceTaskSearchStages,
36
- } from "./AppWorkBoard.js";
37
- import { currencyKey, initialDashboardView, savedLocale } from "./app-preferences.js";
38
- import { readDashboardSessionToken } from "./app-session-token.js";
39
- import { renderDashboardAlternateContent } from "./DashboardAlternateContent.js";
40
- import { DashboardCapabilityNav, type DashboardView } from "./DashboardCapabilityNav.js";
41
- import { DashboardGlobalSettings } from "./DashboardGlobalSettings.js";
42
- import type {
43
- FxRate,
44
- OperationsBoardPage,
45
- OperationsEntityPageKind,
46
- OperationsLifecyclePageKind,
47
- OperationsPageFilters,
48
- PricingCatalog,
49
- ReconciliationResult,
50
- WorkBoardStage,
51
- } from "./dashboard-api.js";
52
- import { DashboardApiClient } from "./dashboard-api.js";
53
- import { reconciliationCommandOutcome } from "./dashboard-api-reconciliation.js";
54
- import {
55
- type DashboardIdentityResponse,
56
- defaultDashboardIdentity,
57
- mergeDashboardIdentity,
58
- } from "./dashboard-identity.js";
59
- import {
60
- defaultOperationsPeriod,
61
- isAllTimeDisplayRangeChange,
62
- requestPeriodForOperations,
63
- } from "./operations-period.js";
64
- import { shouldShowSnapshotNotice, snapshotNoticeDelayMs } from "./snapshot-notice.js";
65
- import {
66
- appendTraceabilityTaskPage,
67
- traceabilityTaskPageSize,
68
- } from "./traceability-task-pagination.js";
69
- import { useAppCostLoaders } from "./use-app-cost-loaders.js";
70
- import { useAppEffects } from "./use-app-effects.js";
71
- import { useAppOperationsPages } from "./use-app-operations-pages.js";
72
- import { useAppTraceDocuments } from "./use-app-trace-documents.js";
73
- import { useAppWorkListPage } from "./use-app-work-list-page.js";
74
- import { useOperationsDetailExport } from "./use-operations-detail-export.js";
75
- import "./loading-feedback.css";
76
- import "./period-transition-overlay.css";
1
+ import { AppDashboardContent } from "./AppDashboardContent.js";
2
+ import { useDashboardAppModel } from "./use-dashboard-app-model.js";
77
3
 
78
4
  export {
79
5
  isAbortError,
@@ -89,1192 +15,5 @@ export {
89
15
  shouldLoadCostSummary,
90
16
  } from "./AppRequestGuards.js";
91
17
  export function App() {
92
- useEffect(() => {
93
- const token = readDashboardSessionToken();
94
- if (!token) return undefined;
95
- // EventSource remains open for hidden tabs and reconnects after reload,
96
- // giving the local runtime a browser-owned lifecycle without a fast poll.
97
- const lease = new EventSource(`/api/v2/browser-lease?token=${encodeURIComponent(token)}`);
98
- return () => lease.close();
99
- }, []);
100
- const [snapshotUpdating, setSnapshotUpdating] = useState(false);
101
- const [snapshotNoticeDismissed, setSnapshotNoticeDismissed] = useState(false);
102
- const [snapshotNoticeDelayElapsed, setSnapshotNoticeDelayElapsed] = useState(false);
103
- const token = useMemo(readDashboardSessionToken, []);
104
- const api = useMemo(() => new DashboardApiClient(token), [token]);
105
- const { loadCostDetail, loadCostReadModelPage, loadCostRunPage, loadCostGroupPage } =
106
- useAppCostLoaders(api);
107
- const [dashboardView, setDashboardView] = useState<DashboardView | null>(() =>
108
- initialDashboardView(window.location.pathname),
109
- );
110
- const dashboardViewRef = useRef(dashboardView);
111
- dashboardViewRef.current = dashboardView;
112
- const [operations, setOperations] = useState<OperationsProjection | null>(null);
113
- const [entityPage, setEntityPage] = useState<{
114
- offset: number;
115
- total: number | null;
116
- loading: boolean;
117
- error: string | null;
118
- aggregation?: "canonical_entity" | "page_partial" | undefined;
119
- }>({ offset: 0, total: null, loading: false, error: null });
120
- const [entityPageKind, setEntityPageKind] = useState<
121
- OperationsEntityPageKind | OperationsLifecyclePageKind
122
- >("runs");
123
- const entityFiltersRef = useRef<OperationsPageFilters>({ query: "", status: "", risk: "" });
124
- const [boardTotals, setBoardTotals] = useState<Partial<Record<WorkBoardStage, number>>>({});
125
- const [boardKindTotals, setBoardKindTotals] = useState<
126
- Partial<Record<WorkBoardStage, OperationsBoardPage["totals_by_kind"]>>
127
- >({});
128
- const [boardStates, setBoardStates] = useState<Partial<Record<WorkBoardStage, BoardStageState>>>(
129
- {},
130
- );
131
- const [overviewStatusPages, setOverviewStatusPages] = useState<OverviewStatusPages>({});
132
- const [overviewClassificationPages, setOverviewClassificationPages] =
133
- useState<OverviewClassificationPages>({});
134
- const [costs, setCosts] = useState<CostMonitorProjection | null>(null);
135
- const [displayedCosts, setDisplayedCosts] = useState<CostMonitorProjection | null>(null);
136
- const [costLocale, setCostLocale] = useState<CostLocale>(savedLocale);
137
- const [currency, setCurrency] = useState<CostCurrency>(() =>
138
- window.localStorage.getItem(currencyKey) === "USD" ? "USD" : "BRL",
139
- );
140
- const currencyRef = useRef(currency);
141
- currencyRef.current = currency;
142
- // Language is the global selector's source of truth for the paired display
143
- // currency, so Operations and Cost Monitor cannot diverge.
144
- const changeDashboardLocale = useCallback((next: CostLocale) => {
145
- setCostLocale(next);
146
- setCurrency(next === "en-US" ? "USD" : "BRL");
147
- }, []);
148
- const [operationsPeriod, setOperationsPeriod] = useState(() => defaultOperationsPeriod);
149
- // All Time may update its displayed factual range without changing the
150
- // unbounded request identity. Keep the refresh callback stable for that
151
- // presentation-only synchronization.
152
- const operationsRequestFrom =
153
- operationsPeriod.preset === "all" ? null : operationsPeriod.range.start;
154
- const operationsRequestTo = operationsPeriod.preset === "all" ? null : operationsPeriod.range.end;
155
- const operationsRequestPeriod = useMemo(
156
- () => ({ from: operationsRequestFrom, to: operationsRequestTo }),
157
- [operationsRequestFrom, operationsRequestTo],
158
- );
159
- const operationsPeriodRef = useRef(operationsPeriod);
160
- operationsPeriodRef.current = operationsPeriod;
161
- const [helpOpen, setHelpOpen] = useState(false);
162
- const [pricing, setPricing] = useState<PricingCatalog | null>(null);
163
- const [fxRate, setFxRate] = useState<FxRate | null>(null);
164
- const [pathname, setPathname] = useState(window.location.pathname);
165
- const [operationsLoading, setOperationsLoading] = useState(true);
166
- const [, setOperationsSurfaceLoading] = useState(false);
167
- const [, setCostSurfaceLoading] = useState(false);
168
- const [periodTransitionPending, setPeriodTransitionPending] = useState(false);
169
- const [loadingArea, setLoadingArea] = useState<LoadingArea | null>("operations");
170
- const [failedArea, setFailedArea] = useState<LoadingArea | null>(null);
171
- const [refreshDialog, setRefreshDialog] = useState<
172
- "choose" | "refreshing" | "reconciling" | null
173
- >(null);
174
- useEffect(() => {
175
- void api
176
- .ready()
177
- .then((ready) => setSnapshotUpdating(ready.snapshot_state === "updating"))
178
- .catch(() => {});
179
- }, [api]);
180
- const [error, setError] = useState<string | null>(null);
181
- const [reconciliationResult, setReconciliationResult] = useState<ReconciliationResult | null>(
182
- null,
183
- );
184
- const [lastUpdatedAt, setLastUpdatedAt] = useState<string | null>(null);
185
- const [dashboardIdentity, setDashboardIdentity] = useState(defaultDashboardIdentity);
186
- const applyDashboardIdentity = useCallback((identity: DashboardIdentityResponse) => {
187
- setDashboardIdentity((current) => mergeDashboardIdentity(current, identity));
188
- }, []);
189
- useEffect(() => {
190
- let cancelled = false;
191
- // Identity is workspace metadata, rather than period data. Start it beside
192
- // the compact projection and never make period/filter refreshes erase it.
193
- void api
194
- .dashboardIdentity()
195
- .then((identity) => {
196
- if (!cancelled) applyDashboardIdentity(identity);
197
- })
198
- // The compact Operations projection remains usable when an older local
199
- // runtime has not yet supplied this optional endpoint.
200
- .catch(() => {});
201
- return () => {
202
- cancelled = true;
203
- };
204
- }, [api, applyDashboardIdentity]);
205
- const {
206
- closeDocument,
207
- documentDetail,
208
- openDocument,
209
- openTrace,
210
- openTraceChange,
211
- openTraceMacro,
212
- selectedTraceTaskId,
213
- setSelectedTraceTaskId,
214
- setTrace,
215
- setTraceLink,
216
- setTraceLoadingTaskId,
217
- setTraceTasks,
218
- trace,
219
- traceLink,
220
- traceLoadingTaskId,
221
- traceTasks,
222
- } = useAppTraceDocuments({ api, operations, setError, setFailedArea, setLoadingArea });
223
- const traceTasksRef = useRef(traceTasks);
224
- const traceTaskCursorRef = useRef({ offset: 0, stageIndex: 0 });
225
- const traceTasksLoadingRef = useRef(false);
226
- const [traceTasksHasMore, setTraceTasksHasMore] = useState(false);
227
- const [traceTasksLoading, setTraceTasksLoading] = useState(false);
228
- const refreshVersion = useRef(0);
229
- useEffect(() => {
230
- traceTasksRef.current = traceTasks;
231
- }, [traceTasks]);
232
- const periodTransitionVersionRef = useRef(0);
233
- const detailLoadInFlight = useRef(false);
234
- const costLoadingCount = useRef(0);
235
- const costSummaryRequestVersion = useRef(0);
236
- const costSummaryAbortRef = useRef<AbortController | null>(null);
237
- const costsLoadedRef = useRef(false);
238
- const operationsDetailLoadedRef = useRef(false);
239
- const operationsDetailCancelRef = useRef<(() => void) | null>(null);
240
- const operationsDetailAbortRef = useRef<AbortController | null>(null);
241
- const operationsPageAbortRef = useRef<AbortController | null>(null);
242
- const operationsPageRequestVersionRef = useRef(0);
243
- const boardLoadedRef = useRef<Partial<Record<WorkBoardStage, number>>>({});
244
- const boardInFlightRef = useRef(new Set<WorkBoardStage>());
245
- const boardAbortControllersRef = useRef(new Map<WorkBoardStage, AbortController>());
246
- const overviewStatusInFlightRef = useRef(new Set<string>());
247
- const boardInitialPageRef = useRef(new Set<WorkBoardStage>());
248
- const boardFiltersRef = useRef<OperationsPageFilters>({
249
- query: "",
250
- status: "",
251
- risk: "",
252
- origin: "",
253
- kind: "",
254
- });
255
- const boardFilterKeyRef = useRef("||||");
256
- const boardRequestVersionRef = useRef(0);
257
- const quickOverviewMetricsRef =
258
- useRef<OperationsProjection["summary"]["overview_metrics"]>(undefined);
259
- const {
260
- workListPage,
261
- workListFiltersRef,
262
- loadWorkListFilters,
263
- loadWorkListPage,
264
- resetWorkListPage,
265
- } = useAppWorkListPage({ api, operationsPeriod, operationsPeriodRef });
266
- // A new local SQLite generation is immutable and verified by the launcher.
267
- // Its server begins warming the compact first fold as soon as readiness is
268
- // observed, so the first paint must join that bounded cache instead of
269
- // forcing a second full aggregate calculation.
270
- const initialAggregateFreshRef = useRef(false);
271
- const refresh = useCallback(
272
- async (
273
- visible = true,
274
- reconcileOpenWork = false,
275
- refreshProjection = false,
276
- reloadCostSummary = false,
277
- ) => {
278
- operationsDetailCancelRef.current?.();
279
- operationsDetailCancelRef.current = null;
280
- operationsDetailAbortRef.current?.abort();
281
- operationsDetailAbortRef.current = null;
282
- boardRequestVersionRef.current += 1;
283
- for (const controller of boardAbortControllersRef.current.values()) controller.abort();
284
- boardAbortControllersRef.current.clear();
285
- boardInFlightRef.current.clear();
286
- boardInitialPageRef.current.clear();
287
- boardLoadedRef.current = {};
288
- setBoardTotals({});
289
- setBoardKindTotals({});
290
- setBoardStates({});
291
- setOverviewStatusPages({});
292
- setOverviewClassificationPages({});
293
- const requestVersion = ++refreshVersion.current;
294
- const transitionVersion = periodTransitionVersionRef.current;
295
- const activeDashboardView = dashboardViewRef.current;
296
- const period = operationsRequestPeriod;
297
- const isCurrentRequest = () =>
298
- isCurrentDashboardRequest(requestVersion, refreshVersion.current) &&
299
- isCurrentOperationsPeriodRequest(
300
- requestVersion,
301
- refreshVersion.current,
302
- period,
303
- requestPeriodForOperations(operationsPeriodRef.current),
304
- );
305
- if (visible) {
306
- setOperationsLoading(true);
307
- setLoadingArea("operations");
308
- }
309
- setError(null);
310
- setFailedArea(null);
311
- try {
312
- if (reconcileOpenWork) {
313
- const reconciliationResponse = await api.reconcileOpenWork(currency, period);
314
- const commandOutcome = reconciliationCommandOutcome(
315
- reconciliationResponse,
316
- isCurrentRequest(),
317
- );
318
- // The compact reconciliation outcome is useful on its own. Reveal
319
- // it before the optional quick GET so a historical snapshot load
320
- // cannot keep the operator in the progress dialog.
321
- setReconciliationResult(commandOutcome.reconciliation);
322
- if (commandOutcome.dismissProgressDialog) setRefreshDialog(null);
323
- if (!commandOutcome.applySnapshotState) return false;
324
- setSnapshotUpdating(reconciliationResponse.snapshot_state === "updating");
325
- // Reconciliation returns promptly while a new SQLite generation may
326
- // still be promoted. Only read a compact Operations projection when
327
- // that promotion is already complete; the revision observer handles
328
- // the deferred case without fetching Cost Monitor detail data.
329
- if (commandOutcome.reloadQuickProjection) {
330
- const projection = await api.operations(period, "quick", undefined, true);
331
- if (!isCurrentRequest()) return false;
332
- initialAggregateFreshRef.current = false;
333
- setOperations(projection);
334
- quickOverviewMetricsRef.current = projection.summary.overview_metrics;
335
- operationsDetailLoadedRef.current = false;
336
- setLastUpdatedAt(projection.summary.source_latest_at);
337
- }
338
- } else if (refreshProjection) {
339
- let snapshot: Awaited<ReturnType<typeof api.refreshProjection>>;
340
- snapshot = await api.refreshProjection(currency, period);
341
- if (!isCurrentRequest()) return false;
342
- setOperations(snapshot.operations);
343
- quickOverviewMetricsRef.current = snapshot.operations.summary.overview_metrics;
344
- operationsDetailLoadedRef.current = true;
345
- setCosts(snapshot.costs);
346
- costsLoadedRef.current = true;
347
- setDisplayedCosts(null);
348
- const dashboard = snapshot.dashboard;
349
- if (dashboard) {
350
- setDashboardIdentity((current) => mergeDashboardIdentity(current, dashboard));
351
- }
352
- setLastUpdatedAt(snapshot.projection.source_latest_at);
353
- } else {
354
- {
355
- // First paint is aggregate-only; board and detail data are loaded
356
- // on their own bounded routes rather than hydrating entity arrays.
357
- const [projection, costSummary] = await Promise.all([
358
- api.operations(period, "quick", undefined, initialAggregateFreshRef.current),
359
- // An English first paint must have the factual USD summary
360
- // before Operations formats currency. BRL retains the fast
361
- // Operations-only path until the user opens Cost Monitor.
362
- currency === "USD" || reloadCostSummary
363
- ? api.costMonitor(currency, period, "summary")
364
- : Promise.resolve(null),
365
- ]);
366
- if (!isCurrentRequest()) return false;
367
- initialAggregateFreshRef.current = false;
368
- setOperations(projection);
369
- quickOverviewMetricsRef.current = projection.summary.overview_metrics;
370
- operationsDetailLoadedRef.current = false;
371
- if (costSummary !== null) {
372
- setCosts(costSummary);
373
- // This is only a first-fold placeholder. It must not suppress
374
- // the factual, narrow Cost summary when the user opens Cost.
375
- costsLoadedRef.current = false;
376
- setDisplayedCosts(null);
377
- }
378
- setLastUpdatedAt(projection.summary.source_latest_at);
379
- }
380
- }
381
- return true;
382
- } catch (cause) {
383
- if (!isCurrentRequest()) return false;
384
- setError(cause instanceof Error ? cause.message : String(cause));
385
- setFailedArea(activeDashboardView === "cost-monitor" ? "costs" : "operations");
386
- return false;
387
- } finally {
388
- if (
389
- isCurrentRequest() &&
390
- isCurrentPeriodTransition(transitionVersion, periodTransitionVersionRef.current)
391
- )
392
- setPeriodTransitionPending(false);
393
- if (visible && isCurrentRequest()) {
394
- setOperationsLoading(false);
395
- setLoadingArea((current) => (current === "operations" ? null : current));
396
- }
397
- }
398
- },
399
- [api, currency, operationsRequestPeriod],
400
- );
401
- const loadOperationsDetails = useCallback(
402
- async (signal?: AbortSignal): Promise<OperationsProjection | null> => {
403
- if (operationsDetailLoadedRef.current) return null;
404
- if (detailLoadInFlight.current) return null;
405
- if (signal?.aborted) return null;
406
- operationsDetailAbortRef.current?.abort();
407
- const controller = signal === undefined ? new AbortController() : null;
408
- const requestSignal = signal ?? controller?.signal;
409
- if (requestSignal === undefined) return null;
410
- if (controller !== null) operationsDetailAbortRef.current = controller;
411
- detailLoadInFlight.current = true;
412
- costLoadingCount.current += 1;
413
- setLoadingArea("costs");
414
- setFailedArea(null);
415
- try {
416
- const requestVersion = ++refreshVersion.current;
417
- const period = requestPeriodForOperations(operationsPeriod);
418
- const isCurrentRequest = () =>
419
- isCurrentDashboardRequest(requestVersion, refreshVersion.current) &&
420
- isCurrentOperationsPeriodRequest(
421
- requestVersion,
422
- refreshVersion.current,
423
- period,
424
- requestPeriodForOperations(operationsPeriodRef.current),
425
- );
426
- const projection = await api.operations(period, "detail", requestSignal);
427
- if (!isCurrentRequest()) return null;
428
- setOperations(projection);
429
- operationsDetailLoadedRef.current = true;
430
- return projection;
431
- } catch (cause) {
432
- if (requestSignal.aborted || isAbortError(cause)) return null;
433
- setError(cause instanceof Error ? cause.message : String(cause));
434
- setFailedArea("operations");
435
- return null;
436
- } finally {
437
- if (controller !== null && operationsDetailAbortRef.current === controller) {
438
- operationsDetailAbortRef.current = null;
439
- }
440
- detailLoadInFlight.current = false;
441
- costLoadingCount.current = Math.max(0, costLoadingCount.current - 1);
442
- if (costLoadingCount.current === 0) {
443
- setLoadingArea((current) => (current === "costs" ? null : current));
444
- }
445
- }
446
- },
447
- [api, operationsPeriod],
448
- );
449
- const { loadOperationsEntityPage, loadOperationsLifecyclePage } = useAppOperationsPages({
450
- api,
451
- entityFiltersRef,
452
- operationsPageAbortRef,
453
- operationsPageRequestVersionRef,
454
- operationsPeriod,
455
- operationsPeriodRef,
456
- refreshVersion,
457
- setEntityPage,
458
- setEntityPageKind,
459
- setOperations,
460
- });
461
- const loadFilteredOperationsPage = useCallback(
462
- (
463
- view: "runs" | "conversations" | "executions" | "changes" | "decisions",
464
- filters: OperationsPageFilters,
465
- ) => {
466
- entityFiltersRef.current = filters;
467
- if (view === "changes" || view === "decisions")
468
- void loadOperationsLifecyclePage(view, 0, filters);
469
- else void loadOperationsEntityPage(view, 0, filters);
470
- },
471
- [loadOperationsEntityPage, loadOperationsLifecyclePage],
472
- );
473
- const loadMoreBoard = useCallback(
474
- async (stage: WorkBoardStage) => {
475
- const current = operations;
476
- if (current === null || boardInFlightRef.current.has(stage)) return;
477
- boardInFlightRef.current.add(stage);
478
- boardAbortControllersRef.current.get(stage)?.abort();
479
- const controller = new AbortController();
480
- boardAbortControllersRef.current.set(stage, controller);
481
- const requestVersion = boardRequestVersionRef.current;
482
- const period = requestPeriodForOperations(operationsPeriod);
483
- setBoardStates((previous) => ({ ...previous, [stage]: { loading: true, error: null } }));
484
- const loaded = boardLoadedRef.current[stage] ?? 0;
485
- try {
486
- const page = await api.operationsBoard(
487
- period,
488
- stage,
489
- loaded,
490
- boardFiltersRef.current,
491
- controller.signal,
492
- 10,
493
- );
494
- if (
495
- controller.signal.aborted ||
496
- requestVersion !== boardRequestVersionRef.current ||
497
- period.from !== requestPeriodForOperations(operationsPeriodRef.current).from ||
498
- period.to !== requestPeriodForOperations(operationsPeriodRef.current).to
499
- )
500
- return;
501
- setOperations((previous) =>
502
- previous === null
503
- ? previous
504
- : {
505
- ...mergeBoardPages(previous, [page]),
506
- // Board pages are entity data only. The quick aggregate keeps charts coherent.
507
- summary: previous.summary,
508
- },
509
- );
510
- setBoardTotals((previous) => ({ ...previous, [stage]: page.total }));
511
- setBoardKindTotals((previous) => ({ ...previous, [stage]: page.totals_by_kind }));
512
- const next = loaded + page.prds.length + page.specs.length + page.tasks.length;
513
- boardLoadedRef.current = { ...boardLoadedRef.current, [stage]: next };
514
- } catch (cause) {
515
- if (
516
- controller.signal.aborted ||
517
- requestVersion !== boardRequestVersionRef.current ||
518
- isAbortError(cause)
519
- )
520
- return;
521
- setBoardStates((previous) => ({
522
- ...previous,
523
- [stage]: {
524
- loading: false,
525
- error: cause instanceof Error ? cause.message : String(cause),
526
- },
527
- }));
528
- } finally {
529
- if (requestVersion === boardRequestVersionRef.current) {
530
- boardInFlightRef.current.delete(stage);
531
- if (boardAbortControllersRef.current.get(stage) === controller)
532
- boardAbortControllersRef.current.delete(stage);
533
- setBoardStates((previous) =>
534
- previous[stage]?.error === null
535
- ? { ...previous, [stage]: { loading: false, error: null } }
536
- : previous,
537
- );
538
- }
539
- }
540
- },
541
- [api, operations, operationsPeriod],
542
- );
543
- const loadFilteredBoard = useCallback((filters: OperationsPageFilters) => {
544
- const normalizedFilters = {
545
- ...filters,
546
- kind: filters.kind ?? boardFiltersRef.current.kind ?? "",
547
- };
548
- const filterKey = `${normalizedFilters.query}|${normalizedFilters.status}|${normalizedFilters.risk}|${normalizedFilters.origin ?? ""}|${normalizedFilters.kind}`;
549
- if (filterKey === boardFilterKeyRef.current) return;
550
- boardFilterKeyRef.current = filterKey;
551
- boardFiltersRef.current = normalizedFilters;
552
- boardRequestVersionRef.current += 1;
553
- for (const controller of boardAbortControllersRef.current.values()) controller.abort();
554
- boardAbortControllersRef.current.clear();
555
- boardInFlightRef.current.clear();
556
- boardInitialPageRef.current.clear();
557
- boardLoadedRef.current = {};
558
- setBoardTotals({});
559
- setBoardKindTotals({});
560
- setBoardStates({});
561
- setOverviewStatusPages({});
562
- setOverviewClassificationPages({});
563
- setOperations((current) =>
564
- current === null
565
- ? current
566
- : {
567
- ...current,
568
- prds: [],
569
- specs: [],
570
- tasks: [],
571
- changes: [],
572
- summary:
573
- filterKey === "||||" && quickOverviewMetricsRef.current !== undefined
574
- ? { ...current.summary, overview_metrics: quickOverviewMetricsRef.current }
575
- : current.summary,
576
- },
577
- );
578
- }, []);
579
- const loadOverviewStatusPage = useCallback(
580
- async (stage: WorkBoardStage, kind: OverviewStatusKind, offset: number) => {
581
- const key = overviewStatusPageKey(stage, kind);
582
- if (overviewStatusInFlightRef.current.has(key)) return;
583
- overviewStatusInFlightRef.current.add(key);
584
- const requestVersion = boardRequestVersionRef.current;
585
- const period = requestPeriodForOperations(operationsPeriod);
586
- setOverviewStatusPages((previous) => ({
587
- ...previous,
588
- [key]: {
589
- items: offset === 0 ? [] : (previous[key]?.items ?? []),
590
- total: previous[key]?.total ?? 0,
591
- loading: true,
592
- error: null,
593
- },
594
- }));
595
- try {
596
- const page = await api.operationsBoard(period, stage, offset, {
597
- kind: kind === "task" ? "all-tasks" : kind,
598
- });
599
- if (
600
- requestVersion !== boardRequestVersionRef.current ||
601
- period.from !== requestPeriodForOperations(operationsPeriodRef.current).from ||
602
- period.to !== requestPeriodForOperations(operationsPeriodRef.current).to
603
- )
604
- return;
605
- const items = kind === "task" ? page.tasks : kind === "spec" ? page.specs : page.prds;
606
- setOverviewStatusPages((previous) => ({
607
- ...previous,
608
- [key]: {
609
- items: offset === 0 ? items : [...(previous[key]?.items ?? []), ...items],
610
- total: page.total,
611
- loading: false,
612
- error: null,
613
- },
614
- }));
615
- } catch (cause) {
616
- if (requestVersion !== boardRequestVersionRef.current || isAbortError(cause)) return;
617
- setOverviewStatusPages((previous) => ({
618
- ...previous,
619
- [key]: {
620
- items: previous[key]?.items ?? [],
621
- total: previous[key]?.total ?? 0,
622
- loading: false,
623
- error: cause instanceof Error ? cause.message : String(cause),
624
- },
625
- }));
626
- } finally {
627
- overviewStatusInFlightRef.current.delete(key);
628
- }
629
- },
630
- [api, operationsPeriod],
631
- );
632
- const loadOverviewClassificationPage = useCallback(
633
- async (kind: OverviewStatusKind, classification: OverviewClassification, offset: number) => {
634
- const key = overviewClassificationPageKey(kind, classification);
635
- if (overviewStatusInFlightRef.current.has(key)) return;
636
- overviewStatusInFlightRef.current.add(key);
637
- const requestVersion = boardRequestVersionRef.current;
638
- const period = requestPeriodForOperations(operationsPeriod);
639
- setOverviewClassificationPages((previous) => ({
640
- ...previous,
641
- [key]: {
642
- items: offset === 0 ? [] : (previous[key]?.items ?? []),
643
- total: previous[key]?.total ?? 0,
644
- loading: true,
645
- error: null,
646
- },
647
- }));
648
- try {
649
- const page = await api.operationsClassification(period, kind, classification, offset);
650
- if (
651
- requestVersion !== boardRequestVersionRef.current ||
652
- period.from !== requestPeriodForOperations(operationsPeriodRef.current).from ||
653
- period.to !== requestPeriodForOperations(operationsPeriodRef.current).to
654
- )
655
- return;
656
- const items = kind === "task" ? page.tasks : kind === "spec" ? page.specs : page.prds;
657
- setOverviewClassificationPages((previous) => ({
658
- ...previous,
659
- [key]: {
660
- items: offset === 0 ? items : [...(previous[key]?.items ?? []), ...items],
661
- total: page.total,
662
- loading: false,
663
- error: null,
664
- },
665
- }));
666
- } catch (cause) {
667
- if (requestVersion !== boardRequestVersionRef.current || isAbortError(cause)) return;
668
- setOverviewClassificationPages((previous) => ({
669
- ...previous,
670
- [key]: {
671
- items: previous[key]?.items ?? [],
672
- total: previous[key]?.total ?? 0,
673
- loading: false,
674
- error: cause instanceof Error ? cause.message : String(cause),
675
- },
676
- }));
677
- } finally {
678
- overviewStatusInFlightRef.current.delete(key);
679
- }
680
- },
681
- [api, operationsPeriod],
682
- );
683
- const boardExportSource = useCallback(
684
- (selectedFilters: { query: string; status: string; risk: string; origin: string }) => {
685
- const filters = { ...boardFiltersRef.current, ...selectedFilters };
686
- const period = requestPeriodForOperations(operationsPeriod);
687
- return {
688
- prepare: async () => {
689
- const firstPages = await Promise.all(
690
- initialWorkBoardStages.map((stage) => api.operationsBoard(period, stage, 0, filters)),
691
- );
692
- const rowsFor = (page: OperationsBoardPage) => [
693
- ...page.prds.map(({ kind: _kind, ...row }) => ({ kind: "prd", ...row })),
694
- ...page.specs.map(({ kind: _kind, ...row }) => ({ kind: "spec", ...row })),
695
- ...page.tasks.map((row) => ({ kind: "task", ...row })),
696
- ];
697
- return {
698
- totalRows: firstPages.reduce((sum, page) => sum + page.total, 0),
699
- pages: async function* () {
700
- for (const firstPage of firstPages) {
701
- let page = firstPage;
702
- let offset = 0;
703
- while (true) {
704
- const rows = rowsFor(page);
705
- if (rows.length === 0) break;
706
- offset += rows.length;
707
- yield rows;
708
- if (offset >= page.total) break;
709
- page = await api.operationsBoard(period, page.stage, offset, filters);
710
- }
711
- }
712
- },
713
- };
714
- },
715
- };
716
- },
717
- [api, operationsPeriod],
718
- );
719
- const detailExportSource = useOperationsDetailExport({ api, operationsPeriod });
720
- useEffect(() => {
721
- if (dashboardView !== "operations" || operations === null) return;
722
- let cancelled = false;
723
- const hydrateBoard = async () => {
724
- if (cancelled) return;
725
- // A page per column preserves a fast first board without monopolising
726
- // SQLite's single local worker with nine simultaneous aggregates.
727
- const pending = initialWorkBoardStages.filter(
728
- (stage) => !boardInitialPageRef.current.has(stage) && !boardInFlightRef.current.has(stage),
729
- );
730
- for (let offset = 0; offset < pending.length && !cancelled; offset += 3) {
731
- const batch = pending.slice(offset, offset + 3);
732
- for (const stage of batch) boardInitialPageRef.current.add(stage);
733
- await Promise.all(batch.map((stage) => loadMoreBoard(stage)));
734
- }
735
- };
736
- // The quick projection is the complete first-fold contract. Defer the
737
- // bounded Kanban cards until after that first paint so they cannot hold up
738
- // the useful overview when SQLite must materialise the board view.
739
- const idle =
740
- typeof window.requestIdleCallback === "function"
741
- ? { kind: "idle" as const, id: window.requestIdleCallback(() => void hydrateBoard()) }
742
- : { kind: "timeout" as const, id: window.setTimeout(() => void hydrateBoard(), 0) };
743
- return () => {
744
- cancelled = true;
745
- if (idle.kind === "idle") window.cancelIdleCallback(idle.id);
746
- else window.clearTimeout(idle.id);
747
- };
748
- }, [dashboardView, loadMoreBoard, operations]);
749
-
750
- const refreshFromDialog = async (reconcileOpenWork: boolean) => {
751
- if (reconcileOpenWork) {
752
- setReconciliationResult(null);
753
- }
754
- setRefreshDialog(reconcileOpenWork ? "reconciling" : "refreshing");
755
- try {
756
- await refresh(false, reconcileOpenWork, !reconcileOpenWork);
757
- } finally {
758
- // A transport/runtime failure must not trap the user in the progress
759
- // overlay. `refresh` normally reports the error on the affected panel;
760
- // this also covers an unexpected rejection before it reaches that path.
761
- setRefreshDialog(null);
762
- }
763
- };
764
- useAppEffects({
765
- api,
766
- costLocale,
767
- currency,
768
- dashboardView,
769
- loadOperationsDetails,
770
- operations,
771
- operationsDetailAbortRef,
772
- operationsDetailCancelRef,
773
- operationsPageAbortRef,
774
- pathname,
775
- pricing,
776
- refresh,
777
- refreshVersion,
778
- setSnapshotUpdating,
779
- setError,
780
- setFxRate,
781
- setPathname,
782
- setPricing,
783
- snapshotUpdating,
784
- });
785
-
786
- const navigate = (nextPath: string) => {
787
- window.history.pushState(null, "", nextPath);
788
- setPathname(nextPath);
789
- window.scrollTo({ left: 0, top: 0 });
790
- };
791
-
792
- const loadCostSummary = useCallback(async () => {
793
- costSummaryAbortRef.current?.abort();
794
- const controller = new AbortController();
795
- costSummaryAbortRef.current = controller;
796
- const requestVersion = ++costSummaryRequestVersion.current;
797
- const period = requestPeriodForOperations(operationsPeriod);
798
- costLoadingCount.current += 1;
799
- setLoadingArea("costs");
800
- setError(null);
801
- setFailedArea(null);
802
- try {
803
- const summary = await api.costMonitor(currency, period, "summary", controller.signal);
804
- const currentPeriod = requestPeriodForOperations(operationsPeriodRef.current);
805
- if (
806
- !isCurrentCostSummaryRequest({
807
- aborted: controller.signal.aborted,
808
- currentCurrency: currencyRef.current,
809
- currentPeriod,
810
- currentRequestVersion: costSummaryRequestVersion.current,
811
- requestVersion,
812
- requestedCurrency: currency,
813
- requestedPeriod: period,
814
- })
815
- )
816
- return;
817
- setCosts(summary);
818
- costsLoadedRef.current = true;
819
- setDisplayedCosts(null);
820
- } catch (cause) {
821
- if (controller.signal.aborted || requestVersion !== costSummaryRequestVersion.current) return;
822
- setError(cause instanceof Error ? cause.message : String(cause));
823
- setFailedArea("costs");
824
- } finally {
825
- costLoadingCount.current = Math.max(0, costLoadingCount.current - 1);
826
- if (costLoadingCount.current === 0)
827
- setLoadingArea((current) => (current === "costs" ? null : current));
828
- if (costSummaryAbortRef.current === controller) costSummaryAbortRef.current = null;
829
- }
830
- }, [api, currency, operationsPeriod]);
831
-
832
- // The global language selector can request USD while Operations is active.
833
- // Acquire its coherent Cost Monitor snapshot here instead of requiring a
834
- // navigation to the Cost Monitor tab first.
835
- useEffect(() => {
836
- if (currency !== "USD" || costs?.currency === currency) return;
837
- void loadCostSummary();
838
- }, [costs?.currency, currency, loadCostSummary]);
839
-
840
- // A global period change invalidates the Cost snapshot before React commits
841
- // the replacement view. All Time also needs its factual Cost range while
842
- // Operations is active, so its disabled date inputs do not fall back to
843
- // today's Operations range. The request itself remains deliberately
844
- // unbounded. The in-flight guard prevents duplicate requests.
845
- useEffect(() => {
846
- if (
847
- (dashboardView !== "cost-monitor" && operationsPeriod.preset !== "all") ||
848
- costSummaryAbortRef.current !== null
849
- )
850
- return;
851
- if (
852
- shouldLoadCostSummary({
853
- costsLoaded: costsLoadedRef.current,
854
- currency,
855
- snapshotCurrency: costs?.currency,
856
- })
857
- )
858
- void loadCostSummary();
859
- }, [costs?.currency, currency, dashboardView, loadCostSummary, operationsPeriod.preset]);
860
-
861
- const loadMoreTraceTasks = useCallback(
862
- async (loadAll = false, reset = false): Promise<OperationsProjection["tasks"]> => {
863
- if (traceTasksLoadingRef.current) return traceTasksRef.current;
864
- if (reset) {
865
- traceTaskCursorRef.current = { offset: 0, stageIndex: 0 };
866
- traceTasksRef.current = [];
867
- setTraceTasks([]);
868
- setTraceTasksHasMore(true);
869
- }
870
- if (traceTaskCursorRef.current.stageIndex >= traceTaskSearchStages.length)
871
- return traceTasksRef.current;
872
-
873
- traceTasksLoadingRef.current = true;
874
- setTraceTasksLoading(true);
875
- try {
876
- do {
877
- const cursor = traceTaskCursorRef.current;
878
- const stage = traceTaskSearchStages[cursor.stageIndex];
879
- if (stage === undefined) break;
880
- const page = await api.operationsBoard(
881
- { from: null, to: null },
882
- stage,
883
- cursor.offset,
884
- { kind: "all-tasks" },
885
- undefined,
886
- traceabilityTaskPageSize,
887
- );
888
- const updated = appendTraceabilityTaskPage({
889
- cursor,
890
- page,
891
- tasks: traceTasksRef.current,
892
- });
893
- traceTasksRef.current = updated.tasks;
894
- setTraceTasks(traceTasksRef.current);
895
- traceTaskCursorRef.current = updated.cursor;
896
- if (page.tasks.length > 0 && !loadAll) break;
897
- } while (traceTaskCursorRef.current.stageIndex < traceTaskSearchStages.length);
898
- setTraceTasksHasMore(traceTaskCursorRef.current.stageIndex < traceTaskSearchStages.length);
899
- return traceTasksRef.current;
900
- } finally {
901
- traceTasksLoadingRef.current = false;
902
- setTraceTasksLoading(false);
903
- }
904
- },
905
- [api, setTraceTasks],
906
- );
907
-
908
- const selectDashboardView = async (nextView: DashboardView) => {
909
- setDashboardView(nextView);
910
- window.scrollTo({ left: 0, top: 0 });
911
- if (nextView === "cost-monitor") {
912
- if (
913
- shouldLoadCostSummary({
914
- costsLoaded: costsLoadedRef.current,
915
- currency,
916
- snapshotCurrency: costs?.currency,
917
- })
918
- )
919
- void loadCostSummary();
920
- } else {
921
- costSummaryAbortRef.current?.abort();
922
- }
923
- if (nextView === "traceability") {
924
- let candidates = traceTasks;
925
- if (candidates.length === 0) {
926
- const requestVersion = boardRequestVersionRef.current;
927
- // Traceability is a global task selector, not a temporal work-board view.
928
- candidates = await loadMoreTraceTasks(false, true);
929
- if (requestVersion !== boardRequestVersionRef.current) return;
930
- setTraceTasks(candidates);
931
- }
932
- const ordered = candidates.toSorted(
933
- (a, b) =>
934
- Date.parse(b.finished_at ?? b.updated_at) - Date.parse(a.finished_at ?? a.updated_at),
935
- );
936
- const latest = ordered.find((task) => task.status === "completed") ?? ordered[0];
937
- if (latest) {
938
- setSelectedTraceTaskId(latest.id);
939
- void openTrace(latest.id);
940
- }
941
- return;
942
- }
943
- setTrace(null);
944
- setTraceLoadingTaskId(null);
945
- };
946
-
947
- const capabilityNavigation = (
948
- <div
949
- className={`dashboard-capability-shell dashboard-capability-shell--${dashboardView ?? "initial"}`}
950
- >
951
- <DashboardCapabilityNav
952
- activeView={dashboardView}
953
- locale={costLocale}
954
- onChange={(view) => void selectDashboardView(view)}
955
- />
956
- </div>
957
- );
958
- const currentCostByRun = useMemo(
959
- () =>
960
- new Map(
961
- (displayedCosts ?? costs)?.runs.map(
962
- (run) =>
963
- [run.run_id, currency === "USD" ? run.estimated_usd : run.estimated_brl] as const,
964
- ) ?? [],
965
- ),
966
- [costs, currency, displayedCosts],
967
- );
968
- const factualCostTotal = useMemo(() => {
969
- const snapshot = displayedCosts ?? costs;
970
- if (snapshot?.currency !== currency) return null;
971
- return currency === "USD" ? snapshot.overview.estimated_usd : snapshot.overview.estimated_brl;
972
- }, [costs, currency, displayedCosts]);
973
- const factualCostPeriods = useMemo(() => {
974
- const snapshot = displayedCosts ?? costs;
975
- if (snapshot?.currency !== currency) return undefined;
976
- return {
977
- day: snapshot.groups.daily,
978
- month: snapshot.groups.monthly,
979
- year: snapshot.groups.annual,
980
- };
981
- }, [costs, currency, displayedCosts]);
982
- const retryLastLoad = () => {
983
- setError(null);
984
- if (failedArea === "costs") void loadCostSummary();
985
- else if (failedArea === "traceability" && selectedTraceTaskId)
986
- void openTrace(selectedTraceTaskId);
987
- else if (failedArea === "operations" && operations !== null) void loadOperationsDetails();
988
- else void refresh();
989
- };
990
-
991
- const setupRoute = renderAppSetupRoute({
992
- api,
993
- costLocale,
994
- fxRate,
995
- navigate,
996
- pathname,
997
- pricing,
998
- refresh: async () => {
999
- await refresh();
1000
- },
1001
- setFxRate,
1002
- setPricing,
1003
- });
1004
- // A request is not an empty dashboard state. One accessible global gate
1005
- // prevents every tab from briefly presenting zero/empty placeholders while
1006
- // its coherent replacement snapshot or page is still in flight.
1007
- const globalLoadingPending =
1008
- periodTransitionPending || (currency === "USD" && costs?.currency !== currency);
1009
- // The stale snapshot is useful only while the initial screen still waits
1010
- // for data. Once Operations has arrived, the normal dashboard is the
1011
- // clearest confirmation that it opened; do not leave a duplicate toast.
1012
- const initialDataPending = operations === null && operationsLoading;
1013
- useEffect(() => {
1014
- if (!snapshotUpdating || !initialDataPending || snapshotNoticeDismissed) {
1015
- setSnapshotNoticeDelayElapsed(false);
1016
- return undefined;
1017
- }
1018
- const timeout = window.setTimeout(
1019
- () => setSnapshotNoticeDelayElapsed(true),
1020
- snapshotNoticeDelayMs,
1021
- );
1022
- return () => window.clearTimeout(timeout);
1023
- }, [initialDataPending, snapshotNoticeDismissed, snapshotUpdating]);
1024
- const snapshotNoticeVisible = shouldShowSnapshotNotice({
1025
- snapshotUpdating,
1026
- initialDataPending,
1027
- dismissed: snapshotNoticeDismissed,
1028
- delayElapsed: snapshotNoticeDelayElapsed,
1029
- });
1030
- const snapshotNotice = snapshotNoticeVisible ? (
1031
- <div aria-live="polite" className="app-notice app-snapshot-status" role="status">
1032
- <span>
1033
- Exibindo o último snapshot local validado. Os dados canônicos estão sendo atualizados em
1034
- segundo plano e estarão atuais na próxima abertura.
1035
- </span>
1036
- <button
1037
- aria-label={
1038
- costLocale === "pt-BR"
1039
- ? "Fechar aviso de atualização do snapshot"
1040
- : "Dismiss snapshot update notice"
1041
- }
1042
- onClick={() => setSnapshotNoticeDismissed(true)}
1043
- type="button"
1044
- >
1045
- ×
1046
- </button>
1047
- </div>
1048
- ) : null;
1049
- const renderedOperations = operations as OperationsProjection;
1050
- return (
1051
- <AppLoadingBoundary
1052
- error={error}
1053
- globalLoadingPending={globalLoadingPending}
1054
- locale={costLocale}
1055
- onRefresh={() => void refresh()}
1056
- operations={operations}
1057
- snapshotNotice={snapshotNotice}
1058
- setupRoute={setupRoute}
1059
- >
1060
- <PeriodTransitionShell pending={globalLoadingPending}>
1061
- {error ? (
1062
- <div aria-live="assertive" className="app-warning app-error" role="alert">
1063
- <span>{error}</span>
1064
- {failedArea ? (
1065
- <button onClick={retryLastLoad} type="button">
1066
- {costLocale === "pt-BR" ? "Tentar novamente" : "Try again"}
1067
- </button>
1068
- ) : null}
1069
- <button
1070
- aria-label={costLocale === "pt-BR" ? "Fechar erro" : "Dismiss error"}
1071
- onClick={() => setError(null)}
1072
- type="button"
1073
- >
1074
- ×
1075
- </button>
1076
- </div>
1077
- ) : null}
1078
- {/* Cost Monitor boundary retains loadRunPage={loadCostRunPage}, loadReadModelPage={loadCostReadModelPage}, and loadDetail={loadCostDetail}. */}
1079
- <OperationsPage
1080
- alternateContent={renderDashboardAlternateContent({
1081
- costs,
1082
- costLocale,
1083
- currency,
1084
- dashboardView,
1085
- failedArea,
1086
- lastUpdatedAt,
1087
- loadCostDetail,
1088
- loadCostReadModelPage,
1089
- loadCostRunPage,
1090
- loadCostGroupPage,
1091
- loadingArea,
1092
- onActiveDataChange: setDisplayedCosts,
1093
- onCurrencyChange: setCurrency,
1094
- onLoadingChange: setCostSurfaceLoading,
1095
- onLocaleChange: changeDashboardLocale,
1096
- onLoadMoreTraceTasks: loadMoreTraceTasks,
1097
- onOpenTrace: (taskId) => void openTrace(taskId),
1098
- onRefresh: () => setRefreshDialog("choose"),
1099
- onRetry: retryLastLoad,
1100
- onSelectTraceTask: (taskId) => {
1101
- setSelectedTraceTaskId(taskId);
1102
- void openTrace(taskId);
1103
- },
1104
- onOpenTraceChange: openTraceChange,
1105
- onOpenTraceMacro: openTraceMacro,
1106
- operations: renderedOperations,
1107
- selectedTraceTaskId,
1108
- trace,
1109
- traceLoadingTaskId,
1110
- traceTasksHasMore,
1111
- traceTasksLoading,
1112
- traceTasks,
1113
- })}
1114
- capabilityNavigation={capabilityNavigation}
1115
- boardTotals={boardTotals}
1116
- boardKindTotals={boardKindTotals}
1117
- boardExportSource={boardExportSource}
1118
- detailExportSource={detailExportSource}
1119
- boardStates={boardStates}
1120
- data={renderedOperations}
1121
- datePreset={operationsPeriod.preset}
1122
- dateRange={operationsPeriod.range}
1123
- entityPage={entityPage}
1124
- dashboardTitle={dashboardIdentity.title}
1125
- repositoryName={dashboardIdentity.repositoryName}
1126
- loading={operationsLoading}
1127
- locale={costLocale}
1128
- currency={currency}
1129
- factualCostTotal={factualCostTotal}
1130
- factualCostPeriods={factualCostPeriods}
1131
- onLoadingChange={setOperationsSurfaceLoading}
1132
- resolveRunCost={
1133
- costs === null ? undefined : (runId) => currentCostByRun.get(runId) ?? null
1134
- }
1135
- onLocaleChange={changeDashboardLocale}
1136
- onDetailDataRequest={(view) => {
1137
- const kind =
1138
- view === "conversations"
1139
- ? "conversations"
1140
- : view === "executions"
1141
- ? "executions"
1142
- : view === "runs"
1143
- ? "runs"
1144
- : null;
1145
- if (kind !== null) void loadOperationsEntityPage(kind);
1146
- else if (view === "changes" || view === "decisions")
1147
- void loadOperationsLifecyclePage(view);
1148
- else void loadOperationsDetails();
1149
- }}
1150
- onLoadMacroMetrics={() => void loadOperationsDetails()}
1151
- onEntityPageChange={(offset) => {
1152
- if (entityPageKind === "changes" || entityPageKind === "decisions")
1153
- void loadOperationsLifecyclePage(entityPageKind, offset);
1154
- else void loadOperationsEntityPage(entityPageKind, offset);
1155
- }}
1156
- onEntityFiltersChange={loadFilteredOperationsPage}
1157
- onBoardFiltersChange={loadFilteredBoard}
1158
- onWorkListFiltersChange={loadWorkListFilters}
1159
- onWorkListPageChange={(offset) =>
1160
- void loadWorkListPage(workListFiltersRef.current, offset)
1161
- }
1162
- onBoardTypeChange={(kind) =>
1163
- loadFilteredBoard({
1164
- ...boardFiltersRef.current,
1165
- kind: kind === "all" ? "" : kind,
1166
- })
1167
- }
1168
- onWorkListTypeChange={(kind) =>
1169
- void loadWorkListPage({
1170
- ...workListFiltersRef.current,
1171
- kind: kind === "all" ? "" : kind,
1172
- })
1173
- }
1174
- onLoadMoreBoard={(stage) => void loadMoreBoard(stage)}
1175
- workListPage={workListPage}
1176
- statusPage={(stage, kind) => overviewStatusPages[overviewStatusPageKey(stage, kind)]}
1177
- onLoadStatusPage={(stage, kind, offset) =>
1178
- void loadOverviewStatusPage(stage, kind, offset)
1179
- }
1180
- classificationPage={(kind, classification) =>
1181
- overviewClassificationPages[overviewClassificationPageKey(kind, classification)]
1182
- }
1183
- onLoadClassificationPage={(kind, classification, offset) =>
1184
- void loadOverviewClassificationPage(kind, classification, offset)
1185
- }
1186
- onOpenDocument={(kind, id) => void openDocument(kind, id)}
1187
- onOpenTask={openTrace}
1188
- onPriorityChange={async (itemIds) => {
1189
- try {
1190
- await api.saveWorkPriority(itemIds);
1191
- } catch (cause) {
1192
- setError(cause instanceof Error ? cause.message : String(cause));
1193
- throw cause;
1194
- }
1195
- }}
1196
- onDateChange={(preset, range) => {
1197
- const nextPeriod = { preset, range };
1198
- if (isAllTimeDisplayRangeChange(operationsPeriodRef.current, nextPeriod)) {
1199
- setOperationsPeriod(nextPeriod);
1200
- return;
1201
- }
1202
- operationsDetailLoadedRef.current = false;
1203
- operationsDetailCancelRef.current?.();
1204
- operationsDetailCancelRef.current = null;
1205
- operationsDetailAbortRef.current?.abort();
1206
- operationsDetailAbortRef.current = null;
1207
- operationsPageAbortRef.current?.abort();
1208
- operationsPageAbortRef.current = null;
1209
- resetWorkListPage();
1210
- ++operationsPageRequestVersionRef.current;
1211
- boardRequestVersionRef.current += 1;
1212
- for (const controller of boardAbortControllersRef.current.values()) controller.abort();
1213
- boardAbortControllersRef.current.clear();
1214
- boardInFlightRef.current.clear();
1215
- boardInitialPageRef.current.clear();
1216
- boardLoadedRef.current = {};
1217
- setBoardTotals({});
1218
- setBoardKindTotals({});
1219
- setBoardStates({});
1220
- setOverviewStatusPages({});
1221
- setOverviewClassificationPages({});
1222
- ++refreshVersion.current;
1223
- costSummaryAbortRef.current?.abort();
1224
- costSummaryAbortRef.current = null;
1225
- ++costSummaryRequestVersion.current;
1226
- setEntityPage({ offset: 0, total: null, loading: false, error: null });
1227
- costsLoadedRef.current = false;
1228
- setCosts(null);
1229
- setDisplayedCosts(null);
1230
- // Keep OperationsPage mounted so its controlled-looking local date
1231
- // select retains the user's choice; loading gates its old content.
1232
- setOperationsLoading(true);
1233
- periodTransitionVersionRef.current += 1;
1234
- setPeriodTransitionPending(true);
1235
- setLoadingArea("operations");
1236
- setOperationsPeriod(nextPeriod);
1237
- }}
1238
- onRefresh={() => setRefreshDialog("choose")}
1239
- settings={
1240
- <DashboardGlobalSettings
1241
- costCalculationGuidePath={costCalculationGuidePath}
1242
- currency={currency}
1243
- locale={costLocale}
1244
- onCurrencyChange={setCurrency}
1245
- onHelpOpen={() => setHelpOpen(true)}
1246
- onNavigate={navigate}
1247
- pricingSetupPath={pricingSetupPath}
1248
- projectSetupPath={projectSetupPath}
1249
- />
1250
- }
1251
- />
1252
- <AppOverlays
1253
- closeDocument={closeDocument}
1254
- costLocale={costLocale}
1255
- documentDetail={documentDetail}
1256
- helpOpen={helpOpen}
1257
- loadingArea={dashboardView === "cost-monitor" && costs === null ? null : loadingArea}
1258
- operations={renderedOperations}
1259
- reconciliationResult={reconciliationResult}
1260
- refreshDialog={refreshDialog}
1261
- showTrace={dashboardView === "traceability"}
1262
- trace={trace}
1263
- traceLink={traceLink}
1264
- traceLoadingTaskId={traceLoadingTaskId}
1265
- onCloseHelp={() => setHelpOpen(false)}
1266
- onCloseTrace={() => setTrace(null)}
1267
- onDismissReconciliation={() => setReconciliationResult(null)}
1268
- onOpenDocument={(kind, id) => void openDocument(kind, id)}
1269
- onOpenTrace={(taskId) => void openTrace(taskId)}
1270
- onOpenTraceChange={openTraceChange}
1271
- onOpenTraceMacro={openTraceMacro}
1272
- onRefreshCancel={() => setRefreshDialog(null)}
1273
- onRefreshFromDialog={(reconcileOpenWork) => void refreshFromDialog(reconcileOpenWork)}
1274
- setTraceLink={(value) => setTraceLink(value)}
1275
- />
1276
- </PeriodTransitionShell>
1277
- {globalLoadingPending ? <PeriodTransitionOverlay /> : null}
1278
- </AppLoadingBoundary>
1279
- );
18
+ return <AppDashboardContent model={useDashboardAppModel()} />;
1280
19
  }