@zaip-ai/cli 1.3.7 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (332) hide show
  1. package/README.md +86 -1
  2. package/dist/commands/init.d.ts +1 -0
  3. package/dist/commands/init.d.ts.map +1 -1
  4. package/dist/commands/init.js +26 -0
  5. package/dist/commands/init.js.map +1 -1
  6. package/dist/index.d.ts +1 -0
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +3 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/services/adapters.js +4 -4
  11. package/dist/services/adapters.js.map +1 -1
  12. package/dist/services/codex-config-template.d.ts +2 -2
  13. package/dist/services/codex-config-template.d.ts.map +1 -1
  14. package/dist/services/codex-config-template.js +3 -3
  15. package/dist/services/codex-config-template.js.map +1 -1
  16. package/dist/services/codex-rules.d.ts +1 -1
  17. package/dist/services/codex-rules.d.ts.map +1 -1
  18. package/dist/services/codex-rules.js +9 -24
  19. package/dist/services/codex-rules.js.map +1 -1
  20. package/dist/services/delivery-git-preflight.d.ts +23 -0
  21. package/dist/services/delivery-git-preflight.d.ts.map +1 -0
  22. package/dist/services/delivery-git-preflight.js +69 -0
  23. package/dist/services/delivery-git-preflight.js.map +1 -0
  24. package/dist/services/doctor.d.ts.map +1 -1
  25. package/dist/services/doctor.js +13 -1
  26. package/dist/services/doctor.js.map +1 -1
  27. package/dist/services/install-agents-template.d.ts.map +1 -1
  28. package/dist/services/install-agents-template.js +22 -28
  29. package/dist/services/install-agents-template.js.map +1 -1
  30. package/dist/services/install-profile.d.ts +8 -0
  31. package/dist/services/install-profile.d.ts.map +1 -0
  32. package/dist/services/install-profile.js +56 -0
  33. package/dist/services/install-profile.js.map +1 -0
  34. package/dist/services/logger.d.ts.map +1 -1
  35. package/dist/services/logger.js +6 -5
  36. package/dist/services/logger.js.map +1 -1
  37. package/dist/services/stack-git.d.ts +9 -0
  38. package/dist/services/stack-git.d.ts.map +1 -0
  39. package/dist/services/stack-git.js +188 -0
  40. package/dist/services/stack-git.js.map +1 -0
  41. package/dist/services/stack-install.d.ts +14 -0
  42. package/dist/services/stack-install.d.ts.map +1 -0
  43. package/dist/services/stack-install.js +176 -0
  44. package/dist/services/stack-install.js.map +1 -0
  45. package/dist/services/stack-node.d.ts +12 -0
  46. package/dist/services/stack-node.d.ts.map +1 -0
  47. package/dist/services/stack-node.js +150 -0
  48. package/dist/services/stack-node.js.map +1 -0
  49. package/dist/services/stack-package-manager.d.ts +12 -0
  50. package/dist/services/stack-package-manager.d.ts.map +1 -0
  51. package/dist/services/stack-package-manager.js +178 -0
  52. package/dist/services/stack-package-manager.js.map +1 -0
  53. package/dist/services/stack-plan.d.ts +35 -0
  54. package/dist/services/stack-plan.d.ts.map +1 -0
  55. package/dist/services/stack-plan.js +130 -0
  56. package/dist/services/stack-plan.js.map +1 -0
  57. package/dist/services/stack-terraform.d.ts +13 -0
  58. package/dist/services/stack-terraform.d.ts.map +1 -0
  59. package/dist/services/stack-terraform.js +274 -0
  60. package/dist/services/stack-terraform.js.map +1 -0
  61. package/dist/services/template-manifest.d.ts.map +1 -1
  62. package/dist/services/template-manifest.js +14 -1
  63. package/dist/services/template-manifest.js.map +1 -1
  64. package/dist/types.d.ts +4 -0
  65. package/dist/types.d.ts.map +1 -1
  66. package/package.json +1 -1
  67. package/templates/.agents/.env.example +34 -0
  68. package/templates/{README.md → .agents/README.md} +124 -60
  69. package/templates/.agents/config/agents/catalog.yaml +23 -23
  70. package/templates/.agents/config/clients/codex/agents/zaip-agent-analyst.toml +1 -0
  71. package/templates/.agents/config/clients/codex/agents/zaip-agent-auditor.toml +1 -0
  72. package/templates/.agents/config/clients/codex/agents/zaip-agent-backend.toml +5 -4
  73. package/templates/.agents/config/clients/codex/agents/zaip-agent-brand-strategist.toml +1 -0
  74. package/templates/.agents/config/clients/codex/agents/zaip-agent-builder.toml +1 -0
  75. package/templates/.agents/config/clients/codex/agents/zaip-agent-cloud-engineer.toml +10 -7
  76. package/templates/.agents/config/clients/codex/agents/zaip-agent-data-analyst.toml +1 -0
  77. package/templates/.agents/config/clients/codex/agents/zaip-agent-designer.toml +1 -0
  78. package/templates/.agents/config/clients/codex/agents/zaip-agent-finalizer.toml +1 -0
  79. package/templates/.agents/config/clients/codex/agents/zaip-agent-frontend.toml +5 -4
  80. package/templates/.agents/config/clients/codex/agents/zaip-agent-general-operations.toml +1 -0
  81. package/templates/.agents/config/clients/codex/agents/zaip-agent-governance-compliance.toml +1 -0
  82. package/templates/.agents/config/clients/codex/agents/zaip-agent-growth-strategist.toml +1 -0
  83. package/templates/.agents/config/clients/codex/agents/zaip-agent-integrator.toml +1 -0
  84. package/templates/.agents/config/clients/codex/agents/zaip-agent-marketing-analyst.toml +5 -4
  85. package/templates/.agents/config/clients/codex/agents/zaip-agent-orchestrator.toml +1 -0
  86. package/templates/.agents/config/clients/codex/agents/zaip-agent-refactoring-specialist.toml +1 -0
  87. package/templates/.agents/config/clients/codex/agents/zaip-agent-reviewer.toml +1 -0
  88. package/templates/.agents/config/clients/codex/agents/zaip-agent-sales-specialist.toml +1 -0
  89. package/templates/.agents/config/clients/codex/agents/zaip-agent-security-auditor.toml +1 -0
  90. package/templates/.agents/config/clients/codex/agents/zaip-agent-task-executor.toml +1 -0
  91. package/templates/.agents/config/clients/codex/agents/zaip-agent-website-developer.toml +5 -4
  92. package/templates/.agents/config/clients/codex/agents/zaip-agent-writer.toml +1 -0
  93. package/templates/.agents/config/pricing/models.json +25 -1
  94. package/templates/.agents/config/project.yaml +2 -0
  95. package/templates/.agents/dashboard/.zaip-managed.json +214 -94
  96. package/templates/.agents/dashboard/DATA-DICTIONARY.md +1 -1
  97. package/templates/.agents/dashboard/README.md +58 -19
  98. package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-DwVZOmMV.js → CostCalculationGuidePage-snUCQYTC.js} +1 -1
  99. package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog--czwAlcH.js → DashboardHelpDialog-oZVlf-l5.js} +787 -723
  100. package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-CyjMXkm8.js → PricingSettingsDialog-BPtVUGgs.js} +1 -1
  101. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-3Awyaovx.js +3 -0
  102. package/templates/.agents/dashboard/apps/frontend/dist/assets/{ProjectSettingsPage-BomxqCBU.css → ProjectSettingsPage-GgzpZ_PY.css} +1 -1
  103. package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-CAqYVin1.js → TraceabilityWorkspace-BYjVOHI_.js} +1 -1
  104. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-DbaLbU2q.js +68 -0
  105. package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-DXBOtWNI.js → icons-vendor-TPUT2IUt.js} +1 -1
  106. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-Cmlx8OYH.css +1 -0
  107. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-ZJFXmK7m.js +2 -0
  108. package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-DaNXQPyk.js → react-vendor-Ct48XHGL.js} +1 -1
  109. package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-I4AKqVDX.js → traceability-BDe4_Ubl.js} +1 -1
  110. package/templates/.agents/dashboard/apps/frontend/dist/index.html +6 -6
  111. package/templates/.agents/dashboard/apps/frontend/src/App.tsx +73 -67
  112. package/templates/.agents/dashboard/apps/frontend/src/AppLoadingBoundary.tsx +35 -7
  113. package/templates/.agents/dashboard/apps/frontend/src/AppWorkBoard.ts +55 -0
  114. package/templates/.agents/dashboard/apps/frontend/src/ProjectSettingsPage.tsx +15 -2
  115. package/templates/.agents/dashboard/apps/frontend/src/ProjectStackEditor.tsx +33 -6
  116. package/templates/.agents/dashboard/apps/frontend/src/app-loading-boundary.css +98 -0
  117. package/templates/.agents/dashboard/apps/frontend/src/cost-period-refresh.test.ts +5 -5
  118. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api-reconciliation.ts +33 -0
  119. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.test.ts +75 -0
  120. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.ts +9 -8
  121. package/templates/.agents/dashboard/apps/frontend/src/project-settings-model.ts +15 -3
  122. package/templates/.agents/dashboard/apps/frontend/src/project-settings-page.css +0 -104
  123. package/templates/.agents/dashboard/apps/frontend/src/project-settings.test.ts +61 -0
  124. package/templates/.agents/dashboard/apps/frontend/src/project-stack-versions.css +117 -0
  125. package/templates/.agents/dashboard/apps/frontend/src/snapshot-coherence.test.ts +78 -2
  126. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.test.ts +28 -0
  127. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.ts +8 -5
  128. package/templates/.agents/dashboard/apps/frontend/src/use-app-effects.ts +180 -1
  129. package/templates/.agents/dashboard/modules/cost-monitor/src/contracts.ts +7 -4
  130. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorContent.tsx +13 -7
  131. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.tsx +14 -0
  132. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTableDisplay.ts +211 -0
  133. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.test.ts +152 -157
  134. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.tsx +155 -142
  135. package/templates/.agents/dashboard/modules/cost-monitor/src/react/InvocationManifestQuery.test.ts +145 -0
  136. package/templates/.agents/dashboard/modules/cost-monitor/src/react/period-cost-groups.ts +7 -5
  137. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-compact-cost-page.ts +4 -1
  138. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-group-page.ts +4 -1
  139. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-run-page.ts +4 -1
  140. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-projection.ts +4 -4
  141. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.test.ts +79 -1
  142. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.tsx +105 -11
  143. package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +477 -327
  144. package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +430 -285
  145. package/templates/.agents/dashboard/packages/local-runtime/scripts/build-sqlite-runtime.mjs +14 -2
  146. package/templates/.agents/dashboard/packages/local-runtime/scripts/rebuild-sqlite-read-model.mjs +6 -26
  147. package/templates/.agents/dashboard/packages/local-runtime/scripts/validate-sqlite-read-model.mjs +1 -1
  148. package/templates/.agents/dashboard/packages/local-runtime/scripts/verify-persistent-launch.mjs +268 -0
  149. package/templates/.agents/dashboard/packages/local-runtime/server.mjs +482 -332
  150. package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.test.ts +243 -1
  151. package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.ts +252 -41
  152. package/templates/.agents/dashboard/packages/local-runtime/src/server-agent-assets.ts +39 -0
  153. package/templates/.agents/dashboard/packages/local-runtime/src/server-browser-lease.ts +33 -0
  154. package/templates/.agents/dashboard/packages/local-runtime/src/server-reconciliation-refresh.test.ts +148 -11
  155. package/templates/.agents/dashboard/packages/local-runtime/src/server-reconciliation-result.test.ts +68 -0
  156. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-refresh.test.ts +19 -0
  157. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-refresh.ts +39 -43
  158. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-revision.test.ts +42 -0
  159. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-revision.ts +26 -0
  160. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-synchronization.ts +34 -0
  161. package/templates/.agents/dashboard/packages/local-runtime/src/server-startup.ts +30 -0
  162. package/templates/.agents/dashboard/packages/local-runtime/src/server-work-reconciliation.test.ts +89 -0
  163. package/templates/.agents/dashboard/packages/local-runtime/src/server-work-reconciliation.ts +88 -0
  164. package/templates/.agents/dashboard/packages/local-runtime/src/server-workspace.ts +68 -7
  165. package/templates/.agents/dashboard/packages/local-runtime/src/server.test.ts +11 -5
  166. package/templates/.agents/dashboard/packages/local-runtime/src/server.ts +129 -99
  167. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-agent-productivity.test.ts +111 -0
  168. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-agent-productivity.ts +140 -0
  169. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-agent-page.test.ts +127 -4
  170. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-agent-page.ts +145 -5
  171. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-aggregates.ts +156 -23
  172. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-group-projection.ts +18 -9
  173. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-projection.ts +52 -0
  174. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-first-fold-persistence.ts +35 -13
  175. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-force-canonical.test.ts +56 -0
  176. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-incremental-cache.test.ts +29 -0
  177. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-incremental-cache.ts +136 -0
  178. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-incremental-refresh.test.ts +251 -0
  179. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-operations-projection.ts +22 -7
  180. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-cache.test-support.ts +1 -1
  181. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-canonical-parsing.ts +42 -0
  182. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-canonical.ts +793 -0
  183. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider-duration.test.ts +270 -0
  184. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider-fixture.ts +3 -2
  185. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider.test.ts +18 -4
  186. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-source-fingerprint.test.ts +87 -0
  187. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-source-fingerprint.ts +105 -16
  188. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.test.ts +131 -59
  189. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.ts +1000 -1115
  190. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-cache.ts +13 -92
  191. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-claims.test.mjs +79 -0
  192. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.mjs +27 -0
  193. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-lifecycle.mjs +6 -6
  194. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-prerequisites.mjs +1 -1
  195. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-refresh.mjs +2 -10
  196. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-server.mjs +142 -0
  197. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.mjs +231 -239
  198. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.test.mjs +18 -29
  199. package/templates/.agents/dashboard/packages/windows-launcher/Program.cs +6 -0
  200. package/templates/.agents/dashboard/stack-dependencies.json +14 -0
  201. package/templates/.agents/dashboard/zaip-dashboard-windows.exe +0 -0
  202. package/templates/.agents/requirements/autonomous-assurance.md +1 -1
  203. package/templates/.agents/requirements/cloud-hostinger-autonomy.md +47 -0
  204. package/templates/.agents/requirements/comparative-audit.md +1 -1
  205. package/templates/.agents/requirements/context-engine.md +1 -1
  206. package/templates/.agents/requirements/cost-observability.md +1 -1
  207. package/templates/.agents/requirements/dashboard-launch.md +2 -2
  208. package/templates/.agents/requirements/data-export.md +1 -1
  209. package/templates/.agents/requirements/design-system.md +1 -1
  210. package/templates/.agents/requirements/execution-engine.md +1 -1
  211. package/templates/.agents/requirements/kanban-dashboard.md +1 -1
  212. package/templates/.agents/requirements/model-orchestration.md +1 -1
  213. package/templates/.agents/requirements/public-distribution.md +1 -1
  214. package/templates/.agents/requirements/software-architecture.md +1 -1
  215. package/templates/.agents/requirements/strategic-improvements.md +1 -1
  216. package/templates/.agents/requirements/work-lifecycle.md +1 -1
  217. package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
  218. package/templates/.agents/rules/autonomy-decision.md +1 -1
  219. package/templates/.agents/rules/cloud-hostinger-autonomy.md +21 -0
  220. package/templates/.agents/rules/core.md +5 -4
  221. package/templates/.agents/rules/local-sqlite-storage.md +18 -0
  222. package/templates/.agents/rules/meta-mcp.md +21 -0
  223. package/templates/.agents/rules/workflow.md +1 -0
  224. package/templates/.agents/runtime/bin/zaip.mjs +305 -137
  225. package/templates/.agents/runtime/checksums.sha256 +2 -2
  226. package/templates/.agents/runtime/provenance.json +2 -2
  227. package/templates/.agents/skills/zaip-skill-api-to-mcp/SKILL.md +4 -0
  228. package/templates/.agents/skills/zaip-skill-brand-governance/SKILL.md +25 -9
  229. package/templates/.agents/skills/zaip-skill-brand-governance/references/brandbook-framework.md +2 -2
  230. package/templates/.agents/skills/zaip-skill-brand-governance/references/brandbook-standards.md +2 -2
  231. package/templates/.agents/skills/zaip-skill-change/references/observable-evidence.md +15 -0
  232. package/templates/.agents/skills/zaip-skill-dashboard-sqlite/SKILL.md +13 -5
  233. package/templates/.agents/skills/zaip-skill-data-analyst/SKILL.md +10 -5
  234. package/templates/.agents/skills/zaip-skill-execution/SKILL.md +5 -4
  235. package/templates/.agents/skills/zaip-skill-frontend-launcher/SKILL.md +4 -3
  236. package/templates/.agents/skills/zaip-skill-frontend-launcher/references/launcher-contract.md +5 -3
  237. package/templates/.agents/skills/zaip-skill-github-deploy/SKILL.md +5 -5
  238. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/preflight.mjs +78 -13
  239. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/preflight.test.mjs +59 -0
  240. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/yaml-parser.mjs +157 -0
  241. package/templates/.agents/skills/zaip-skill-growth-strategy-revops/SKILL.md +11 -6
  242. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/SKILL.md +17 -0
  243. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/agents/openai.yaml +7 -0
  244. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/references/autonomous-cloud-inputs.example.json +41 -0
  245. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/references/autonomous-runner.md +84 -0
  246. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/references/operational-contract.md +19 -0
  247. package/templates/.agents/skills/zaip-skill-local-credentials/SKILL.md +43 -0
  248. package/templates/.agents/skills/zaip-skill-local-credentials/agents/openai.yaml +6 -0
  249. package/templates/.agents/skills/zaip-skill-local-credentials/references/dotenv-resolution.md +47 -0
  250. package/templates/.agents/skills/zaip-skill-marketing/SKILL.md +8 -3
  251. package/templates/.agents/skills/zaip-skill-meta-ads/SKILL.md +16 -0
  252. package/templates/.agents/skills/zaip-skill-meta-ads/agents/openai.yaml +4 -0
  253. package/templates/.agents/skills/zaip-skill-meta-ads/references/setup.md +3 -0
  254. package/templates/.agents/skills/zaip-skill-meta-ads-setup/SKILL.md +24 -0
  255. package/templates/.agents/skills/zaip-skill-meta-ads-setup/agents/openai.yaml +4 -0
  256. package/templates/.agents/skills/zaip-skill-meta-ads-setup/references/diagnostics.md +13 -0
  257. package/templates/.agents/skills/zaip-skill-meta-ads-setup/references/official-sources.md +10 -0
  258. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/SKILL.md +12 -0
  259. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/agents/openai.yaml +4 -0
  260. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/references/LICENSE +21 -0
  261. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/references/upstream.md +77 -0
  262. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/SKILL.md +12 -0
  263. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/agents/openai.yaml +4 -0
  264. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/references/LICENSE +21 -0
  265. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/references/upstream.md +126 -0
  266. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/SKILL.md +12 -0
  267. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/agents/openai.yaml +4 -0
  268. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/references/LICENSE +21 -0
  269. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/references/upstream.md +67 -0
  270. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/SKILL.md +12 -0
  271. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/agents/openai.yaml +4 -0
  272. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/references/LICENSE +21 -0
  273. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/references/upstream.md +69 -0
  274. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/SKILL.md +12 -0
  275. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/agents/openai.yaml +4 -0
  276. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/references/LICENSE +21 -0
  277. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/references/upstream.md +79 -0
  278. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/SKILL.md +14 -0
  279. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/agents/openai.yaml +4 -0
  280. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/references/LICENSE +21 -0
  281. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/references/upstream.md +144 -0
  282. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/SKILL.md +12 -0
  283. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/agents/openai.yaml +4 -0
  284. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/references/LICENSE +21 -0
  285. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/references/upstream.md +83 -0
  286. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/SKILL.md +10 -0
  287. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/agents/openai.yaml +4 -0
  288. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/references/LICENSE +21 -0
  289. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/references/setup.md +11 -0
  290. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/references/upstream.md +55 -0
  291. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/SKILL.md +12 -0
  292. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/agents/openai.yaml +4 -0
  293. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/references/LICENSE +21 -0
  294. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/references/upstream.md +56 -0
  295. package/templates/.agents/skills/zaip-skill-sales-methodology/SKILL.md +12 -7
  296. package/templates/.agents/skills/zaip-skill-sqlite-read-model/SKILL.md +11 -4
  297. package/templates/.agents/skills/zaip-skill-tool-setup/SKILL.md +6 -4
  298. package/templates/.agents/skills/zaip-skill-tool-setup/references/codex-config.md +4 -2
  299. package/templates/.agents/skills/zaip-skill-tool-setup/references/secrets.md +1 -0
  300. package/templates/.agents/tools/catalog.yaml +6 -0
  301. package/templates/.agents/tools/cloud-runner-dns.mjs +28 -0
  302. package/templates/.agents/tools/cloud-runner-terraform.mjs +212 -0
  303. package/templates/.agents/tools/cloud-runner.integration.test.mjs +239 -0
  304. package/templates/.agents/tools/cloud-runner.mjs +786 -0
  305. package/templates/.agents/tools/cloud-runner.test.mjs +534 -0
  306. package/templates/.agents/tools/cloud-terraform-plan-policy.mjs +262 -0
  307. package/templates/.agents/tools/mcp/development/meta-devtools/README.md +14 -0
  308. package/templates/.agents/tools/mcp/development/meta-devtools/manifest.yaml +28 -0
  309. package/templates/.agents/tools/mcp/infra/cloudflare/manifest.yaml +2 -3
  310. package/templates/.agents/tools/mcp/infra/hostinger/manifest.yaml +1 -3
  311. package/templates/.agents/tools/mcp/marketing/meta-ads/README.md +15 -13
  312. package/templates/.agents/tools/mcp/marketing/meta-ads/manifest.yaml +15 -10
  313. package/templates/.agents/tools/profiles/development.yaml +3 -0
  314. package/templates/infra/terraform/.terraform.lock.hcl +42 -0
  315. package/templates/infra/terraform/README.md +31 -0
  316. package/templates/infra/terraform/backend.tf.json +13 -0
  317. package/templates/infra/terraform/deployment-inputs.example.yaml +22 -0
  318. package/templates/infra/terraform/fixtures/approved-targets.json +32 -0
  319. package/templates/infra/terraform/fixtures/plan-approved.json +67 -0
  320. package/templates/infra/terraform/main.tf +77 -0
  321. package/templates/infra/terraform/outputs.tf +20 -0
  322. package/templates/infra/terraform/terraform.tfvars.example +26 -0
  323. package/templates/infra/terraform/variables.tf +91 -0
  324. package/templates/infra/terraform/versions.tf +18 -0
  325. package/templates/scripts/validate-cloud-hostinger-contract.mjs +89 -0
  326. package/templates/scripts/validate-cloud-terraform-plan.mjs +1 -0
  327. package/templates/template-manifest.json +613 -173
  328. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-B_Oj6Dlf.js +0 -3
  329. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-C09KmlSw.js +0 -68
  330. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-CblPpexd.js +0 -2
  331. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-hPPfVGQv.css +0 -1
  332. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-stale-seed.ts +0 -50
@@ -0,0 +1,88 @@
1
+ import {
2
+ ReconciliationCommandTimeoutError,
3
+ type ReconciliationResult,
4
+ reconciliationCommandTimeoutMs,
5
+ } from "./server-workspace.js";
6
+
7
+ export function reconciliationNeedsRefresh(result: ReconciliationResult): boolean {
8
+ return result.reconciled_ids.length > 0 || result.blocked.length > 0;
9
+ }
10
+
11
+ export type ReconciliationMode = "manual" | "host-terminal";
12
+
13
+ /** Include queue time in the HTTP deadline; the serialized job remains retryable. */
14
+ export async function waitForWorkReconciliation(
15
+ work: Promise<ReconciliationResult>,
16
+ onDeadline: () => void = () => undefined,
17
+ ): Promise<ReconciliationResult> {
18
+ let timer: ReturnType<typeof setTimeout> | undefined;
19
+ try {
20
+ return await Promise.race([
21
+ work,
22
+ new Promise<never>((_resolve, reject) => {
23
+ timer = setTimeout(() => {
24
+ onDeadline();
25
+ reject(new ReconciliationCommandTimeoutError());
26
+ }, reconciliationCommandTimeoutMs);
27
+ timer.unref();
28
+ }),
29
+ ]);
30
+ } finally {
31
+ if (timer !== undefined) clearTimeout(timer);
32
+ }
33
+ }
34
+
35
+ /** Serialize canonical mutations from the button and the host recovery timer. */
36
+ export function createWorkReconciliation(input: {
37
+ reconcile: (mode: ReconciliationMode) => Promise<ReconciliationResult>;
38
+ refresh: () => Promise<void>;
39
+ }) {
40
+ let tail = Promise.resolve();
41
+ const pending = new Map<ReconciliationMode, Promise<ReconciliationResult>>();
42
+ const refresh = () => void input.refresh().catch(() => undefined);
43
+ return (mode: ReconciliationMode): Promise<ReconciliationResult> => {
44
+ const current = pending.get("manual") ?? pending.get(mode);
45
+ if (current !== undefined) return current;
46
+ const job = tail
47
+ .then(() => input.reconcile(mode))
48
+ .then(
49
+ (result) => {
50
+ if (result.reconciled_ids.length > 0 || result.blocked.length > 0) refresh();
51
+ return result;
52
+ },
53
+ (error: unknown) => {
54
+ // A failed command may already have persisted part of the graph.
55
+ refresh();
56
+ throw error;
57
+ },
58
+ )
59
+ .finally(() => pending.delete(mode));
60
+ pending.set(mode, job);
61
+ tail = job.then(
62
+ () => undefined,
63
+ () => undefined,
64
+ );
65
+ return job;
66
+ };
67
+ }
68
+
69
+ /** Recover missed host-stop callbacks without relying on an open browser tab. */
70
+ export function startHostTerminalReconciliation(reconcile: () => Promise<unknown>): () => void {
71
+ let stopped = false;
72
+ let timer: ReturnType<typeof setTimeout>;
73
+ const schedule = (delay: number) => {
74
+ timer = setTimeout(() => {
75
+ void reconcile()
76
+ .catch(() => undefined)
77
+ .finally(() => {
78
+ if (!stopped) schedule(30_000);
79
+ });
80
+ }, delay);
81
+ timer.unref();
82
+ };
83
+ schedule(1_000);
84
+ return () => {
85
+ stopped = true;
86
+ clearTimeout(timer);
87
+ };
88
+ }
@@ -68,19 +68,52 @@ export interface ReconciliationResult {
68
68
  not_reconciled: ReconciliationItem[];
69
69
  }
70
70
 
71
+ export const reconciliationCommandTimeoutMs = 170_000;
72
+
73
+ export class ReconciliationCommandTimeoutError extends Error {
74
+ constructor() {
75
+ super(
76
+ `Open-work reconciliation exceeded ${reconciliationCommandTimeoutMs}ms. Canonical records may contain partial changes; the dashboard projection is being refreshed.`,
77
+ );
78
+ this.name = "ReconciliationCommandTimeoutError";
79
+ }
80
+ }
81
+
71
82
  export async function reconcileOpenWork(
72
83
  filesystem: WorkspaceFileSystem,
73
84
  workspaceRoot: string,
85
+ options: { hostTerminalOnly?: boolean } = {},
74
86
  ): Promise<ReconciliationResult> {
75
87
  if (!(await filesystem.exists(".agents/runtime/bin/zaip.mjs"))) {
76
- return { reconciled_ids: [], blocked: [], not_reconciled: [] };
88
+ throw new Error("ZAIP runtime is unavailable; open work could not be reconciled.");
77
89
  }
78
90
  const cliPath = resolve(workspaceRoot, ".agents", "runtime", "bin", "zaip.mjs");
79
- const { stdout } = await execFileAsync(
80
- process.execPath,
81
- [cliPath, "run", "reconcile", "--open", "--json"],
82
- { cwd: workspaceRoot, windowsHide: true, timeout: 120_000, maxBuffer: 10 * 1_024 * 1_024 },
83
- );
91
+ let stdout: string;
92
+ try {
93
+ ({ stdout } = await execFileAsync(
94
+ process.execPath,
95
+ [
96
+ cliPath,
97
+ "run",
98
+ "reconcile",
99
+ "--open",
100
+ "--json",
101
+ ...(options.hostTerminalOnly ? ["--host-terminal-only"] : []),
102
+ ],
103
+ {
104
+ cwd: workspaceRoot,
105
+ windowsHide: true,
106
+ maxBuffer: 10 * 1_024 * 1_024,
107
+ timeout: reconciliationCommandTimeoutMs,
108
+ killSignal: "SIGTERM",
109
+ },
110
+ ));
111
+ } catch (error) {
112
+ if (typeof error === "object" && error !== null && "killed" in error && error.killed === true) {
113
+ throw new ReconciliationCommandTimeoutError();
114
+ }
115
+ throw error;
116
+ }
84
117
  return reconciliationResult(JSON.parse(stdout) as unknown);
85
118
  }
86
119
 
@@ -94,6 +127,7 @@ export function reconciliationResult(value: unknown): ReconciliationResult {
94
127
  };
95
128
  const records =
96
129
  typeof value === "object" && value !== null ? (value as Record<string, unknown>) : {};
130
+ const terminalStatuses = new Set(["completed", "delivered", "blocked", "cancelled", "failed"]);
97
131
  const reconciliations = Array.isArray(records.reconciliations) ? records.reconciliations : [];
98
132
  for (const candidate of reconciliations) {
99
133
  if (typeof candidate !== "object" || candidate === null) continue;
@@ -107,7 +141,7 @@ export function reconciliationResult(value: unknown): ReconciliationResult {
107
141
  const record = entity as Record<string, unknown>;
108
142
  if (
109
143
  item.status === "closed" &&
110
- ["completed", "blocked", "cancelled", "failed"].includes(record.status as string) &&
144
+ terminalStatuses.has(record.status as string) &&
111
145
  typeof record.id === "string"
112
146
  ) {
113
147
  reconciled.add(record.id);
@@ -119,12 +153,39 @@ export function reconciliationResult(value: unknown): ReconciliationResult {
119
153
  }
120
154
  }
121
155
  }
156
+ // A terminal conversation/root can change canonical telemetry without a
157
+ // Task graph. It still requires a snapshot promotion so Cost Monitor does
158
+ // not keep serving the generation that preceded its Run closure.
159
+ const closedRuns = Array.isArray(records.runs) ? records.runs : [];
160
+ closedRuns.forEach((candidate) => {
161
+ if (typeof candidate !== "object" || candidate === null) return;
162
+ const runId = (candidate as { run?: { run_id?: unknown } }).run?.run_id;
163
+ if (typeof runId === "string") reconciled.add(runId);
164
+ });
122
165
  const orphanedExecutions = Array.isArray(records.orphaned_executions)
123
166
  ? records.orphaned_executions
124
167
  : [];
125
168
  orphanedExecutions.forEach((id) => {
126
169
  remember(blocked, id, "Execution orphaned after the reconciliation grace period.");
127
170
  });
171
+ const orphanedInteractionExecutions = Array.isArray(records.orphaned_interaction_executions)
172
+ ? records.orphaned_interaction_executions
173
+ : [];
174
+ orphanedInteractionExecutions.forEach((id) => {
175
+ remember(blocked, id, "Interaction Execution orphaned after the reconciliation grace period.");
176
+ });
177
+ // Orphan recovery can settle owners without returning a Run reconciliation.
178
+ // Surface every persisted owner, including macro-only `delivered` outcomes,
179
+ // so the button reports the changed entities and refreshes their snapshot.
180
+ for (const key of ["orphaned_owner_entities", "orphaned_execution_entities"]) {
181
+ for (const candidate of Array.isArray(records[key]) ? records[key] : []) {
182
+ if (typeof candidate !== "object" || candidate === null) continue;
183
+ const entity = candidate as Record<string, unknown>;
184
+ if (typeof entity.id !== "string" || !terminalStatuses.has(entity.status as string)) continue;
185
+ reconciled.add(entity.id);
186
+ if (entity.status === "blocked") remember(blocked, entity.id, entity.status_reason);
187
+ }
188
+ }
128
189
  const errors = Array.isArray(records.reconciliation_errors) ? records.reconciliation_errors : [];
129
190
  errors.forEach((candidate) => {
130
191
  if (typeof candidate !== "object" || candidate === null) return;
@@ -247,15 +247,21 @@ describe("dashboard backend", () => {
247
247
  headers: { "x-zaip-session": "test-session" },
248
248
  });
249
249
  expect(response.status, await response.clone().text()).toBe(200);
250
- await expect(response.json()).resolves.toMatchObject({
251
- reconciliation: { reconciled_ids: ["TASK-RECONCILED"] },
252
- });
250
+ const receipt = (await response.json()) as Record<string, unknown>;
251
+ expect(receipt).toMatchObject({ reconciliation: { reconciled_ids: ["TASK-RECONCILED"] } });
252
+ expect(Object.keys(receipt).sort()).toEqual([
253
+ "projection_refresh_completed",
254
+ "reconciliation",
255
+ "snapshot_state",
256
+ ]);
253
257
  expect(reconcile).toHaveBeenCalledOnce();
254
- expect(reconcile).toHaveBeenCalledWith(canonicalFilesystem, workspaceRoot);
258
+ expect(reconcile).toHaveBeenCalledWith(canonicalFilesystem, workspaceRoot, {
259
+ hostTerminalOnly: false,
260
+ });
255
261
  expect(refresh).toHaveBeenCalledOnce();
256
262
  const rejected = await app.request("/api/v2/reconcile-open-work", { method: "POST" });
257
263
  expect(rejected.status).toBe(401);
258
- }, 130_000);
264
+ });
259
265
  it("rebuilds a stale immutable projection without reconciling lifecycle work", async () => {
260
266
  const fixtureRoot = await mkdtemp(join(tmpdir(), "zaip-dashboard-stale-projection-"));
261
267
  const taskIndexPath = join(
@@ -34,6 +34,7 @@ import {
34
34
  readProjectOverview,
35
35
  readProjectSettings,
36
36
  } from "./project-settings.js";
37
+ import { agentPortraitFiles, openWorkIds } from "./server-agent-assets.js";
37
38
  import {
38
39
  type BoardClassification,
39
40
  boardFilters,
@@ -41,6 +42,7 @@ import {
41
42
  boardStage,
42
43
  classificationPage,
43
44
  } from "./server-board.js";
45
+ import { registerBrowserLeaseRoute } from "./server-browser-lease.js";
44
46
  import { registerDailyReadModelRoute } from "./server-daily-read-models-route.js";
45
47
  import {
46
48
  changeEntityPageRows,
@@ -64,11 +66,24 @@ import {
64
66
  } from "./server-projections.js";
65
67
  import { isAllowedDashboardAuthority } from "./server-request-authority.js";
66
68
  import { refreshSqliteSnapshotInWorker } from "./server-snapshot-refresh.js";
69
+ import { readCommittedSnapshotRevision } from "./server-snapshot-revision.js";
70
+ import { startSnapshotSynchronization } from "./server-snapshot-synchronization.js";
67
71
  import { buildSqliteTaskTrace } from "./server-sqlite-task-trace.js";
72
+ import { writeReadModelStartup, writeStartupPhase } from "./server-startup.js";
68
73
  import { queryValues, selectedCurrency } from "./server-summary-helpers.js";
74
+ import {
75
+ createWorkReconciliation,
76
+ reconciliationNeedsRefresh,
77
+ startHostTerminalReconciliation,
78
+ waitForWorkReconciliation,
79
+ } from "./server-work-reconciliation.js";
80
+
81
+ export { reconciliationNeedsRefresh } from "./server-work-reconciliation.js";
82
+
69
83
  import {
70
84
  dashboardIdentity,
71
85
  dashboardWorkspaceKey,
86
+ ReconciliationCommandTimeoutError,
72
87
  type ReconciliationResult,
73
88
  reconcileOpenWork,
74
89
  } from "./server-workspace.js";
@@ -79,6 +94,7 @@ import {
79
94
  } from "./sqlite-read-model.js";
80
95
 
81
96
  export { manifestRun, mergeManifestRuns } from "./manifest-runs.js";
97
+ export { openWorkIds } from "./server-agent-assets.js";
82
98
  export { projectionPeriodFilter } from "./server-period.js";
83
99
  export { projectAllTimeTopTasks } from "./server-period-macro-metric.js";
84
100
  export {
@@ -94,10 +110,6 @@ export {
94
110
  validateSqliteReadModelSchema,
95
111
  } from "./sqlite-read-model.js";
96
112
 
97
- export function reconciliationNeedsRefresh(result: ReconciliationResult): boolean {
98
- return result.reconciled_ids.length > 0 || result.blocked.length > 0;
99
- }
100
-
101
113
  type OverviewMacroMetrics = NonNullable<
102
114
  NonNullable<OperationsProjection["summary"]["overview_metrics"]>["macro_metrics"]
103
115
  >;
@@ -108,47 +120,6 @@ const RUNTIME_KIND = "zaip-dashboard-sqlite-runtime";
108
120
  const SESSION_COOKIE = "zaip-dashboard-sqlite-session";
109
121
  const WORK_PRIORITY_PATH = ".agents/dashboard/work-priority.json";
110
122
  const SNAPSHOT_CACHE_TTL_MS = 5_000;
111
- const agentPortraitFiles: Record<string, string> = {
112
- "agent-orchestrator": "1 - Dave - Product Owner.png",
113
- "agent-builder": "2 - Rafa - Tech Lead.png",
114
- "agent-task-executor": "3 - Leo - Software Engineer.png",
115
- "agent-analyst": "4 - Bia - Product Manager.png",
116
- "agent-finalizer": "5 - Gui - Quality Assurance Lead.png",
117
- "agent-reviewer": "6 - Nanda - Principal Engineer.png",
118
- "agent-integrator": "7 - Dani - Integration Engineer.png",
119
- "agent-auditor": "8 - Ana - Quality Auditor.png",
120
- "agent-text-summarizer": "9 - Lia - Technical Writer.png",
121
- "agent-data-analyst": "10 - Gabi - Data Analyst.png",
122
- "agent-refactoring-specialist": "11 - Tati - Refactoring Specialist.png",
123
- "agent-frontend": "12 - Doug - Frontend Developer.png",
124
- "agent-backend": "13 - Will - Backend Developer.png",
125
- "agent-website-developer": "14 - Jonny - Website Developer.png",
126
- "agent-designer": "15 - Manu - Product Designer.png",
127
- "agent-cloud-engineer": "16 - Dan - Cloud Engineer.png",
128
- "agent-security-auditor": "17 - Luis - Security Auditor.png",
129
- "agent-marketing-analyst": "18 - Lucia - Marketing Analyst.png",
130
- };
131
-
132
- interface ActiveWorkItem {
133
- kind: string;
134
- id: string;
135
- status: string | null;
136
- }
137
-
138
- /** Pure lifecycle helper retained for callers; it never reads a runtime projection. */
139
- export function openWorkIds(items: readonly ActiveWorkItem[]): string[] {
140
- const kinds = new Set(["prd", "spec", "change", "task", "execution"]);
141
- const statuses = new Set(["active", "executing", "verifying", "running", "paused"]);
142
- return items
143
- .filter(
144
- (item) =>
145
- kinds.has(item.kind) &&
146
- (statuses.has(item.status ?? "") || (item.kind === "task" && item.status === "planned")),
147
- )
148
- .map((item) => item.id)
149
- .sort();
150
- }
151
-
152
123
  interface DashboardAppOptions {
153
124
  syncAgentAdapters?: () => Promise<AgentAdapterSyncResult>;
154
125
  filesystem?: WorkspaceFileSystem;
@@ -158,8 +129,12 @@ interface DashboardAppOptions {
158
129
  reconcileOpenWork?: typeof reconcileOpenWork;
159
130
  refreshReadModel?: () => Promise<void>;
160
131
  snapshotState?: "current" | "updating";
132
+ /** Enable filesystem and day rollover synchronization for the serving process. */
133
+ watchSources?: boolean;
161
134
  /** Test seam: health and reconciliation only require the asserted V2 layout. */
162
135
  layout?: WorkspaceLayout;
136
+ /** Only the spawned local runtime may self-terminate for a missing browser lease. */
137
+ launcherOwned?: boolean;
163
138
  }
164
139
 
165
140
  class V2WorkspaceRequiredError extends Error {
@@ -650,14 +625,15 @@ export function createDashboardApp(
650
625
  const app = new Hono();
651
626
  const filesystem = options.filesystem ?? new WorkspaceFileSystem(workspaceRoot);
652
627
  const isSqliteSnapshot = filesystem instanceof SqliteReadModelFileSystem;
653
- // A private SQLite generation is immutable for this app instance. Loading
654
- // its V2 layout repeatedly costs hundreds of milliseconds per navigation;
655
- // canonical filesystems remain deliberately uncached.
656
- const sqliteLayout = isSqliteSnapshot ? requireV2Layout(filesystem) : null;
628
+ // Cache layout within a committed revision and reload after synchronization.
629
+ let sqliteLayout = isSqliteSnapshot ? requireV2Layout(filesystem) : null;
657
630
  const readLayout = async () =>
658
631
  options.layout ?? sqliteLayout ?? (await requireV2Layout(filesystem));
659
632
  const snapshotReadOnly = options.snapshotReadOnly ?? isSqliteSnapshot;
660
- const snapshotState = options.snapshotState ?? "current";
633
+ // A stale seed is safe to serve only until this process promotes its own
634
+ // replacement. Keep the state mutable so health/ready stop advertising an
635
+ // update after the atomic handoff succeeds.
636
+ let snapshotState = options.snapshotState ?? "current";
661
637
  const canonicalFilesystem = options.canonicalFilesystem ?? new WorkspaceFileSystem(workspaceRoot);
662
638
  const reconcileOpen = options.reconcileOpenWork ?? reconcileOpenWork;
663
639
  const refreshReadModel =
@@ -714,16 +690,48 @@ export function createDashboardApp(
714
690
  aggregatePayloads.clear();
715
691
  aggregateInFlight.clear();
716
692
  };
693
+ let observedSnapshotRevision: string | null = null;
717
694
  let projectionRefreshInFlight: Promise<void> | null = null;
695
+ let projectionRefreshRequested = false;
718
696
  const rebuildProjection = async () => {
719
- if (projectionRefreshInFlight !== null) return projectionRefreshInFlight;
720
- projectionRefreshInFlight = refreshReadModel()
721
- .then(() => clearSnapshotCache())
722
- .finally(() => {
723
- projectionRefreshInFlight = null;
724
- });
697
+ if (projectionRefreshInFlight !== null) {
698
+ projectionRefreshRequested = true;
699
+ return projectionRefreshInFlight;
700
+ }
701
+ snapshotState = "updating";
702
+ projectionRefreshInFlight = (async () => {
703
+ do {
704
+ projectionRefreshRequested = false;
705
+ await refreshReadModel();
706
+ if (isSqliteSnapshot) sqliteLayout = requireV2Layout(filesystem);
707
+ clearSnapshotCache();
708
+ } while (projectionRefreshRequested);
709
+ snapshotState = "current";
710
+ })().finally(() => {
711
+ projectionRefreshInFlight = null;
712
+ });
725
713
  return projectionRefreshInFlight;
726
714
  };
715
+ startSnapshotSynchronization({
716
+ workspaceRoot,
717
+ staleSeed: isSqliteSnapshot && snapshotState === "updating",
718
+ watchSources: isSqliteSnapshot && options.watchSources === true,
719
+ refresh: rebuildProjection,
720
+ });
721
+ const reconcileWork = createWorkReconciliation({
722
+ reconcile: (mode) =>
723
+ reconcileOpen(canonicalFilesystem, workspaceRoot, {
724
+ hostTerminalOnly: mode === "host-terminal",
725
+ }),
726
+ refresh: rebuildProjection,
727
+ });
728
+ if (
729
+ isSqliteSnapshot &&
730
+ options.watchSources === true &&
731
+ process.env.ZAIP_DASHBOARD_WATCH_SOURCES !== "0"
732
+ ) {
733
+ startHostTerminalReconciliation(() => reconcileWork("host-terminal"));
734
+ }
727
735
  const readSnapshot = (context: Context) => {
728
736
  const key = snapshotKey(context);
729
737
  const cached = cachedSnapshots.get(key);
@@ -785,6 +793,8 @@ export function createDashboardApp(
785
793
  // available while the dashboard serves a read-only snapshot.
786
794
  const reconcileWrite =
787
795
  context.req.method === "POST" && context.req.path === `${API_PREFIX}/reconcile-open-work`;
796
+ const browserLeaseWrite =
797
+ context.req.method === "GET" && context.req.path === `${API_PREFIX}/browser-lease`;
788
798
  const projectionRefreshWrite =
789
799
  context.req.method === "POST" && context.req.path === `${API_PREFIX}/refresh-projection`;
790
800
  if (
@@ -792,7 +802,8 @@ export function createDashboardApp(
792
802
  context.req.method !== "HEAD" &&
793
803
  !settingsWrite &&
794
804
  !reconcileWrite &&
795
- !projectionRefreshWrite
805
+ !projectionRefreshWrite &&
806
+ !browserLeaseWrite
796
807
  ) {
797
808
  context.header(
798
809
  "Allow",
@@ -824,15 +835,23 @@ export function createDashboardApp(
824
835
  context.req.path.startsWith(`${API_PREFIX}/agent-catalog-assets/`)
825
836
  ? context.req.query("token")
826
837
  : undefined;
838
+ const leaseToken = browserLeaseWrite ? context.req.query("token") : undefined;
839
+ // EventSource cannot carry the session header and localhost cookies span
840
+ // ports. Its query capability is therefore intentionally stricter than
841
+ // ordinary dashboard requests: an old tab must never lease a new runtime.
842
+ if (browserLeaseWrite && leaseToken !== sessionToken)
843
+ return context.json({ error: "Invalid local session" }, 401);
827
844
  if (
828
845
  headerSession !== sessionToken &&
829
846
  cookieSession !== sessionToken &&
830
- portraitToken !== sessionToken
847
+ portraitToken !== sessionToken &&
848
+ leaseToken !== sessionToken
831
849
  ) {
832
850
  return context.json({ error: "Invalid local session" }, 401);
833
851
  }
834
852
  return next();
835
853
  });
854
+ registerBrowserLeaseRoute(app, API_PREFIX, options.launcherOwned === true);
836
855
 
837
856
  app.on(["GET", "HEAD"], `${API_PREFIX}/health`, async (context) => {
838
857
  try {
@@ -871,6 +890,15 @@ export function createDashboardApp(
871
890
  try {
872
891
  const layout = await readLayout();
873
892
  if (isSqliteSnapshot) {
893
+ const revision = readCommittedSnapshotRevision(workspaceRoot);
894
+ if (observedSnapshotRevision !== null && observedSnapshotRevision !== revision) {
895
+ // A worker may commit before its completion callback runs. Refresh
896
+ // the reader and its caches before advertising the new revision.
897
+ filesystem.refresh();
898
+ clearSnapshotCache();
899
+ sqliteLayout = requireV2Layout(filesystem);
900
+ }
901
+ observedSnapshotRevision = revision;
874
902
  const metadata = filesystem.metadata();
875
903
  return headOrJson(context, {
876
904
  kind: RUNTIME_KIND,
@@ -884,6 +912,7 @@ export function createDashboardApp(
884
912
  // /ready must never start a competing projection in the background.
885
913
  day_count: metadata.daily_aggregate_count,
886
914
  snapshot_state: snapshotState,
915
+ snapshot_revision: revision,
887
916
  });
888
917
  }
889
918
  const projection = await buildFirstFoldQuickProjection(filesystem, layout, {
@@ -1220,23 +1249,32 @@ export function createDashboardApp(
1220
1249
  app.post(`${API_PREFIX}/reconcile-open-work`, async (context) => {
1221
1250
  try {
1222
1251
  await readLayout();
1223
- // The served snapshot is immutable by design. Reconciliation must use
1224
- // canonical records. When those records changed, atomically rebuild and
1225
- // reopen the snapshot before the response exposes fresh projections.
1226
- const reconciledIds = await reconcileOpen(canonicalFilesystem, workspaceRoot);
1227
- // A clean reconciliation is the normal startup path. It changed no
1228
- // canonical data, so rebuilding the immutable SQLite snapshot only adds
1229
- // seconds of redundant work before the dashboard can settle.
1230
- if (reconciliationNeedsRefresh(reconciledIds)) {
1231
- await rebuildProjection();
1232
- }
1252
+ // Share the canonical mutation queue with missed host-stop recovery.
1253
+ // Projection rebuilds run in the background and publish their revision.
1254
+ const reconciledIds = await waitForWorkReconciliation(reconcileWork("manual"), () => {
1255
+ void rebuildProjection().catch(() => undefined);
1256
+ });
1233
1257
  return context.json({
1234
- ...(await readSnapshot(context)),
1235
1258
  reconciliation: reconciledIds,
1236
- projection_refresh_completed: reconciliationNeedsRefresh(reconciledIds),
1259
+ snapshot_state: snapshotState,
1260
+ projection_refresh_completed:
1261
+ snapshotState === "current" && projectionRefreshInFlight === null,
1237
1262
  });
1238
1263
  } catch (error) {
1239
1264
  if (error instanceof V2WorkspaceRequiredError) throw error;
1265
+ if (error instanceof ReconciliationCommandTimeoutError) {
1266
+ // SIGTERM can occur after the CLI persisted some records. Start an
1267
+ // atomic refresh without extending the request past the client's
1268
+ // deadline, and make that uncertainty explicit to the caller.
1269
+ return context.json(
1270
+ {
1271
+ error: error.message,
1272
+ reconciliation_may_be_partial: true,
1273
+ projection_refresh_started: true,
1274
+ },
1275
+ 408,
1276
+ );
1277
+ }
1240
1278
  return context.json({ error: error instanceof Error ? error.message : String(error) }, 400);
1241
1279
  }
1242
1280
  });
@@ -1314,7 +1352,18 @@ export function createDashboardApp(
1314
1352
  );
1315
1353
  return headOrJson(
1316
1354
  context,
1317
- await buildManifestCostRunPage(filesystem, layout, context, { from, to }, offset, limit),
1355
+ // The paged Cost adapter reads the lifecycle summary. That canonical
1356
+ // artifact is intentionally not copied into SQLite; use its current
1357
+ // filesystem alongside the immutable SQL facts rather than failing
1358
+ // a filtered page with a synthetic missing-file error.
1359
+ await buildManifestCostRunPage(
1360
+ isSqliteSnapshot ? canonicalFilesystem : filesystem,
1361
+ layout,
1362
+ context,
1363
+ { from, to },
1364
+ offset,
1365
+ limit,
1366
+ ),
1318
1367
  200,
1319
1368
  true,
1320
1369
  );
@@ -1627,42 +1676,23 @@ export const app = new Proxy({} as Hono, {
1627
1676
  },
1628
1677
  });
1629
1678
 
1630
- function startupPhase(phase: string): void {
1631
- process.stderr.write(
1632
- `${JSON.stringify({
1633
- event: "dashboard_sqlite_startup_phase",
1634
- phase,
1635
- elapsed_ms: Math.round(performance.now() - moduleStartedAt),
1636
- })}\n`,
1637
- );
1638
- }
1639
-
1640
1679
  async function main(): Promise<void> {
1641
1680
  const port = Number.parseInt(process.env.ZAIP_SERVER_PORT ?? "4317", 10);
1642
- startupPhase("main_entered");
1681
+ writeStartupPhase(moduleStartedAt, "main_entered");
1643
1682
  const startedAt = performance.now();
1644
- const readModel = await rebuildSqliteReadModel(runtimeWorkspaceRoot);
1645
- // The parent launcher captures stderr even when stdout is intentionally
1646
- // hidden. This makes seed hit/miss and private-copy cost observable without
1647
- // exposing a shared handle or workspace payload.
1648
- process.stderr.write(
1649
- `${JSON.stringify({
1650
- event: "dashboard_sqlite_read_model",
1651
- elapsed_ms: Math.round(performance.now() - startedAt),
1652
- cache: readModel.cache,
1653
- fingerprint_ms: readModel.fingerprint_ms,
1654
- materialize_ms: readModel.materialize_ms,
1655
- source_files: readModel.files,
1656
- })}\n`,
1657
- );
1658
- startupPhase("read_model_ready");
1683
+ const startupOptions = { forceCanonical: false, preferExisting: true };
1684
+ const readModel = await rebuildSqliteReadModel(runtimeWorkspaceRoot, startupOptions);
1685
+ writeReadModelStartup(startedAt, readModel);
1686
+ writeStartupPhase(moduleStartedAt, "read_model_ready");
1659
1687
  const runtimeApp = createDashboardApp(runtimeWorkspaceRoot, sessionToken, undefined, {
1660
1688
  filesystem: new SqliteReadModelFileSystem(runtimeWorkspaceRoot),
1661
1689
  snapshotState: readModel.cache === "stale_seed" ? "updating" : "current",
1690
+ watchSources: true,
1691
+ launcherOwned: true,
1662
1692
  });
1663
- startupPhase("runtime_app_ready");
1693
+ writeStartupPhase(moduleStartedAt, "runtime_app_ready");
1664
1694
  serve({ fetch: runtimeApp.fetch, hostname: HOST, port }, ({ port: boundPort }) => {
1665
- startupPhase("listening");
1695
+ writeStartupPhase(moduleStartedAt, "listening");
1666
1696
  process.stdout.write(
1667
1697
  `ZAIP-AIOS dashboard API: http://${HOST}:${boundPort}${API_PREFIX}/health\n`,
1668
1698
  );