@zaip-ai/cli 1.3.8 → 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (317) hide show
  1. package/README.md +58 -3
  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 +17 -6
  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 +2 -0
  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-plan.d.ts +3 -1
  42. package/dist/services/stack-plan.d.ts.map +1 -1
  43. package/dist/services/stack-plan.js +15 -1
  44. package/dist/services/stack-plan.js.map +1 -1
  45. package/dist/services/stack-terraform.d.ts +13 -0
  46. package/dist/services/stack-terraform.d.ts.map +1 -0
  47. package/dist/services/stack-terraform.js +274 -0
  48. package/dist/services/stack-terraform.js.map +1 -0
  49. package/dist/services/template-manifest.d.ts.map +1 -1
  50. package/dist/services/template-manifest.js +14 -1
  51. package/dist/services/template-manifest.js.map +1 -1
  52. package/dist/types.d.ts +1 -0
  53. package/dist/types.d.ts.map +1 -1
  54. package/package.json +1 -1
  55. package/templates/.agents/.env.example +34 -0
  56. package/templates/{README.md → .agents/README.md} +124 -60
  57. package/templates/.agents/config/agents/catalog.yaml +23 -23
  58. package/templates/.agents/config/clients/codex/agents/zaip-agent-analyst.toml +1 -0
  59. package/templates/.agents/config/clients/codex/agents/zaip-agent-auditor.toml +1 -0
  60. package/templates/.agents/config/clients/codex/agents/zaip-agent-backend.toml +5 -4
  61. package/templates/.agents/config/clients/codex/agents/zaip-agent-brand-strategist.toml +1 -0
  62. package/templates/.agents/config/clients/codex/agents/zaip-agent-builder.toml +1 -0
  63. package/templates/.agents/config/clients/codex/agents/zaip-agent-cloud-engineer.toml +10 -7
  64. package/templates/.agents/config/clients/codex/agents/zaip-agent-data-analyst.toml +1 -0
  65. package/templates/.agents/config/clients/codex/agents/zaip-agent-designer.toml +1 -0
  66. package/templates/.agents/config/clients/codex/agents/zaip-agent-finalizer.toml +1 -0
  67. package/templates/.agents/config/clients/codex/agents/zaip-agent-frontend.toml +5 -4
  68. package/templates/.agents/config/clients/codex/agents/zaip-agent-general-operations.toml +1 -0
  69. package/templates/.agents/config/clients/codex/agents/zaip-agent-governance-compliance.toml +1 -0
  70. package/templates/.agents/config/clients/codex/agents/zaip-agent-growth-strategist.toml +1 -0
  71. package/templates/.agents/config/clients/codex/agents/zaip-agent-integrator.toml +1 -0
  72. package/templates/.agents/config/clients/codex/agents/zaip-agent-marketing-analyst.toml +5 -4
  73. package/templates/.agents/config/clients/codex/agents/zaip-agent-orchestrator.toml +1 -0
  74. package/templates/.agents/config/clients/codex/agents/zaip-agent-refactoring-specialist.toml +1 -0
  75. package/templates/.agents/config/clients/codex/agents/zaip-agent-reviewer.toml +1 -0
  76. package/templates/.agents/config/clients/codex/agents/zaip-agent-sales-specialist.toml +1 -0
  77. package/templates/.agents/config/clients/codex/agents/zaip-agent-security-auditor.toml +1 -0
  78. package/templates/.agents/config/clients/codex/agents/zaip-agent-task-executor.toml +1 -0
  79. package/templates/.agents/config/clients/codex/agents/zaip-agent-website-developer.toml +5 -4
  80. package/templates/.agents/config/clients/codex/agents/zaip-agent-writer.toml +1 -0
  81. package/templates/.agents/config/pricing/models.json +25 -1
  82. package/templates/.agents/config/project.yaml +2 -0
  83. package/templates/.agents/dashboard/.zaip-managed.json +208 -88
  84. package/templates/.agents/dashboard/DATA-DICTIONARY.md +1 -1
  85. package/templates/.agents/dashboard/README.md +58 -19
  86. package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-DwVZOmMV.js → CostCalculationGuidePage-snUCQYTC.js} +1 -1
  87. package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog-VcLx9iOO.js → DashboardHelpDialog-oZVlf-l5.js} +72 -8
  88. package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-CyjMXkm8.js → PricingSettingsDialog-BPtVUGgs.js} +1 -1
  89. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-3Awyaovx.js +3 -0
  90. package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-DFZwIXSC.js → TraceabilityWorkspace-BYjVOHI_.js} +1 -1
  91. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-DbaLbU2q.js +68 -0
  92. package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-DXBOtWNI.js → icons-vendor-TPUT2IUt.js} +1 -1
  93. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-Cmlx8OYH.css +1 -0
  94. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-ZJFXmK7m.js +2 -0
  95. package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-DaNXQPyk.js → react-vendor-Ct48XHGL.js} +1 -1
  96. package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-I4AKqVDX.js → traceability-BDe4_Ubl.js} +1 -1
  97. package/templates/.agents/dashboard/apps/frontend/dist/index.html +6 -6
  98. package/templates/.agents/dashboard/apps/frontend/src/App.tsx +73 -67
  99. package/templates/.agents/dashboard/apps/frontend/src/AppLoadingBoundary.tsx +35 -7
  100. package/templates/.agents/dashboard/apps/frontend/src/AppWorkBoard.ts +55 -0
  101. package/templates/.agents/dashboard/apps/frontend/src/ProjectStackEditor.tsx +11 -0
  102. package/templates/.agents/dashboard/apps/frontend/src/app-loading-boundary.css +98 -0
  103. package/templates/.agents/dashboard/apps/frontend/src/cost-period-refresh.test.ts +5 -5
  104. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api-reconciliation.ts +33 -0
  105. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.test.ts +75 -0
  106. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.ts +7 -8
  107. package/templates/.agents/dashboard/apps/frontend/src/project-settings-model.ts +14 -2
  108. package/templates/.agents/dashboard/apps/frontend/src/project-settings.test.ts +35 -0
  109. package/templates/.agents/dashboard/apps/frontend/src/snapshot-coherence.test.ts +78 -2
  110. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.test.ts +28 -0
  111. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.ts +8 -5
  112. package/templates/.agents/dashboard/apps/frontend/src/use-app-effects.ts +180 -1
  113. package/templates/.agents/dashboard/modules/cost-monitor/src/contracts.ts +7 -4
  114. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorContent.tsx +13 -7
  115. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.tsx +14 -0
  116. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTableDisplay.ts +211 -0
  117. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.test.ts +152 -157
  118. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.tsx +155 -142
  119. package/templates/.agents/dashboard/modules/cost-monitor/src/react/InvocationManifestQuery.test.ts +145 -0
  120. package/templates/.agents/dashboard/modules/cost-monitor/src/react/period-cost-groups.ts +7 -5
  121. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-compact-cost-page.ts +4 -1
  122. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-group-page.ts +4 -1
  123. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-run-page.ts +4 -1
  124. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-projection.ts +4 -4
  125. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.test.ts +79 -1
  126. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.tsx +105 -11
  127. package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +482 -332
  128. package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +430 -285
  129. package/templates/.agents/dashboard/packages/local-runtime/scripts/build-sqlite-runtime.mjs +14 -2
  130. package/templates/.agents/dashboard/packages/local-runtime/scripts/rebuild-sqlite-read-model.mjs +6 -26
  131. package/templates/.agents/dashboard/packages/local-runtime/scripts/validate-sqlite-read-model.mjs +1 -1
  132. package/templates/.agents/dashboard/packages/local-runtime/scripts/verify-persistent-launch.mjs +268 -0
  133. package/templates/.agents/dashboard/packages/local-runtime/server.mjs +482 -332
  134. package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.test.ts +90 -1
  135. package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.ts +112 -22
  136. package/templates/.agents/dashboard/packages/local-runtime/src/server-agent-assets.ts +39 -0
  137. package/templates/.agents/dashboard/packages/local-runtime/src/server-browser-lease.ts +33 -0
  138. package/templates/.agents/dashboard/packages/local-runtime/src/server-reconciliation-refresh.test.ts +148 -11
  139. package/templates/.agents/dashboard/packages/local-runtime/src/server-reconciliation-result.test.ts +68 -0
  140. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-refresh.test.ts +19 -0
  141. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-refresh.ts +39 -43
  142. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-revision.test.ts +42 -0
  143. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-revision.ts +26 -0
  144. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-synchronization.ts +34 -0
  145. package/templates/.agents/dashboard/packages/local-runtime/src/server-startup.ts +30 -0
  146. package/templates/.agents/dashboard/packages/local-runtime/src/server-work-reconciliation.test.ts +89 -0
  147. package/templates/.agents/dashboard/packages/local-runtime/src/server-work-reconciliation.ts +88 -0
  148. package/templates/.agents/dashboard/packages/local-runtime/src/server-workspace.ts +68 -7
  149. package/templates/.agents/dashboard/packages/local-runtime/src/server.test.ts +11 -5
  150. package/templates/.agents/dashboard/packages/local-runtime/src/server.ts +129 -99
  151. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-agent-productivity.test.ts +111 -0
  152. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-agent-productivity.ts +140 -0
  153. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-agent-page.test.ts +127 -4
  154. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-agent-page.ts +145 -5
  155. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-aggregates.ts +156 -23
  156. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-group-projection.ts +18 -9
  157. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-projection.ts +52 -0
  158. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-first-fold-persistence.ts +35 -13
  159. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-force-canonical.test.ts +56 -0
  160. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-incremental-cache.test.ts +29 -0
  161. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-incremental-cache.ts +136 -0
  162. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-incremental-refresh.test.ts +251 -0
  163. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-operations-projection.ts +22 -7
  164. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-cache.test-support.ts +1 -1
  165. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-canonical-parsing.ts +42 -0
  166. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-canonical.ts +793 -0
  167. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider-duration.test.ts +270 -0
  168. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider-fixture.ts +3 -2
  169. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider.test.ts +18 -4
  170. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-source-fingerprint.test.ts +87 -0
  171. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-source-fingerprint.ts +105 -16
  172. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.test.ts +131 -59
  173. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.ts +1000 -1115
  174. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-cache.ts +13 -92
  175. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-claims.test.mjs +79 -0
  176. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.mjs +27 -0
  177. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-lifecycle.mjs +6 -6
  178. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-prerequisites.mjs +1 -1
  179. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-refresh.mjs +2 -10
  180. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-runtime-selection.mjs +36 -0
  181. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-runtime-selection.test.mjs +63 -0
  182. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-server.mjs +142 -0
  183. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.mjs +262 -267
  184. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.test.mjs +18 -29
  185. package/templates/.agents/dashboard/packages/windows-launcher/Program.cs +6 -0
  186. package/templates/.agents/dashboard/zaip-dashboard-windows.exe +0 -0
  187. package/templates/.agents/requirements/autonomous-assurance.md +1 -1
  188. package/templates/.agents/requirements/cloud-hostinger-autonomy.md +47 -0
  189. package/templates/.agents/requirements/comparative-audit.md +1 -1
  190. package/templates/.agents/requirements/context-engine.md +1 -1
  191. package/templates/.agents/requirements/cost-observability.md +1 -1
  192. package/templates/.agents/requirements/dashboard-launch.md +2 -2
  193. package/templates/.agents/requirements/data-export.md +1 -1
  194. package/templates/.agents/requirements/design-system.md +1 -1
  195. package/templates/.agents/requirements/execution-engine.md +1 -1
  196. package/templates/.agents/requirements/kanban-dashboard.md +1 -1
  197. package/templates/.agents/requirements/model-orchestration.md +1 -1
  198. package/templates/.agents/requirements/public-distribution.md +1 -1
  199. package/templates/.agents/requirements/software-architecture.md +1 -1
  200. package/templates/.agents/requirements/strategic-improvements.md +1 -1
  201. package/templates/.agents/requirements/work-lifecycle.md +1 -1
  202. package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
  203. package/templates/.agents/rules/autonomy-decision.md +1 -1
  204. package/templates/.agents/rules/cloud-hostinger-autonomy.md +21 -0
  205. package/templates/.agents/rules/core.md +5 -4
  206. package/templates/.agents/rules/local-sqlite-storage.md +18 -0
  207. package/templates/.agents/rules/meta-mcp.md +21 -0
  208. package/templates/.agents/rules/workflow.md +1 -0
  209. package/templates/.agents/runtime/bin/zaip.mjs +305 -137
  210. package/templates/.agents/runtime/checksums.sha256 +2 -2
  211. package/templates/.agents/runtime/provenance.json +2 -2
  212. package/templates/.agents/skills/zaip-skill-api-to-mcp/SKILL.md +4 -0
  213. package/templates/.agents/skills/zaip-skill-brand-governance/SKILL.md +25 -9
  214. package/templates/.agents/skills/zaip-skill-brand-governance/references/brandbook-framework.md +2 -2
  215. package/templates/.agents/skills/zaip-skill-brand-governance/references/brandbook-standards.md +2 -2
  216. package/templates/.agents/skills/zaip-skill-change/references/observable-evidence.md +15 -0
  217. package/templates/.agents/skills/zaip-skill-dashboard-sqlite/SKILL.md +13 -5
  218. package/templates/.agents/skills/zaip-skill-data-analyst/SKILL.md +10 -5
  219. package/templates/.agents/skills/zaip-skill-execution/SKILL.md +5 -4
  220. package/templates/.agents/skills/zaip-skill-frontend-launcher/SKILL.md +4 -3
  221. package/templates/.agents/skills/zaip-skill-frontend-launcher/references/launcher-contract.md +5 -3
  222. package/templates/.agents/skills/zaip-skill-github-deploy/SKILL.md +5 -5
  223. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/preflight.mjs +78 -13
  224. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/preflight.test.mjs +59 -0
  225. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/yaml-parser.mjs +157 -0
  226. package/templates/.agents/skills/zaip-skill-growth-strategy-revops/SKILL.md +11 -6
  227. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/SKILL.md +17 -0
  228. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/agents/openai.yaml +7 -0
  229. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/references/autonomous-cloud-inputs.example.json +41 -0
  230. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/references/autonomous-runner.md +84 -0
  231. package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/references/operational-contract.md +19 -0
  232. package/templates/.agents/skills/zaip-skill-local-credentials/SKILL.md +43 -0
  233. package/templates/.agents/skills/zaip-skill-local-credentials/agents/openai.yaml +6 -0
  234. package/templates/.agents/skills/zaip-skill-local-credentials/references/dotenv-resolution.md +47 -0
  235. package/templates/.agents/skills/zaip-skill-marketing/SKILL.md +8 -3
  236. package/templates/.agents/skills/zaip-skill-meta-ads/SKILL.md +16 -0
  237. package/templates/.agents/skills/zaip-skill-meta-ads/agents/openai.yaml +4 -0
  238. package/templates/.agents/skills/zaip-skill-meta-ads/references/setup.md +3 -0
  239. package/templates/.agents/skills/zaip-skill-meta-ads-setup/SKILL.md +24 -0
  240. package/templates/.agents/skills/zaip-skill-meta-ads-setup/agents/openai.yaml +4 -0
  241. package/templates/.agents/skills/zaip-skill-meta-ads-setup/references/diagnostics.md +13 -0
  242. package/templates/.agents/skills/zaip-skill-meta-ads-setup/references/official-sources.md +10 -0
  243. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/SKILL.md +12 -0
  244. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/agents/openai.yaml +4 -0
  245. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/references/LICENSE +21 -0
  246. package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/references/upstream.md +77 -0
  247. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/SKILL.md +12 -0
  248. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/agents/openai.yaml +4 -0
  249. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/references/LICENSE +21 -0
  250. package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/references/upstream.md +126 -0
  251. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/SKILL.md +12 -0
  252. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/agents/openai.yaml +4 -0
  253. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/references/LICENSE +21 -0
  254. package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/references/upstream.md +67 -0
  255. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/SKILL.md +12 -0
  256. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/agents/openai.yaml +4 -0
  257. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/references/LICENSE +21 -0
  258. package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/references/upstream.md +69 -0
  259. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/SKILL.md +12 -0
  260. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/agents/openai.yaml +4 -0
  261. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/references/LICENSE +21 -0
  262. package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/references/upstream.md +79 -0
  263. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/SKILL.md +14 -0
  264. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/agents/openai.yaml +4 -0
  265. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/references/LICENSE +21 -0
  266. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/references/upstream.md +144 -0
  267. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/SKILL.md +12 -0
  268. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/agents/openai.yaml +4 -0
  269. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/references/LICENSE +21 -0
  270. package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/references/upstream.md +83 -0
  271. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/SKILL.md +10 -0
  272. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/agents/openai.yaml +4 -0
  273. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/references/LICENSE +21 -0
  274. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/references/setup.md +11 -0
  275. package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/references/upstream.md +55 -0
  276. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/SKILL.md +12 -0
  277. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/agents/openai.yaml +4 -0
  278. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/references/LICENSE +21 -0
  279. package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/references/upstream.md +56 -0
  280. package/templates/.agents/skills/zaip-skill-sales-methodology/SKILL.md +12 -7
  281. package/templates/.agents/skills/zaip-skill-sqlite-read-model/SKILL.md +11 -4
  282. package/templates/.agents/skills/zaip-skill-tool-setup/SKILL.md +6 -4
  283. package/templates/.agents/skills/zaip-skill-tool-setup/references/codex-config.md +4 -2
  284. package/templates/.agents/skills/zaip-skill-tool-setup/references/secrets.md +1 -0
  285. package/templates/.agents/tools/catalog.yaml +6 -0
  286. package/templates/.agents/tools/cloud-runner-dns.mjs +28 -0
  287. package/templates/.agents/tools/cloud-runner-terraform.mjs +212 -0
  288. package/templates/.agents/tools/cloud-runner.integration.test.mjs +239 -0
  289. package/templates/.agents/tools/cloud-runner.mjs +786 -0
  290. package/templates/.agents/tools/cloud-runner.test.mjs +534 -0
  291. package/templates/.agents/tools/cloud-terraform-plan-policy.mjs +262 -0
  292. package/templates/.agents/tools/mcp/development/meta-devtools/README.md +14 -0
  293. package/templates/.agents/tools/mcp/development/meta-devtools/manifest.yaml +28 -0
  294. package/templates/.agents/tools/mcp/infra/cloudflare/manifest.yaml +2 -3
  295. package/templates/.agents/tools/mcp/infra/hostinger/manifest.yaml +1 -3
  296. package/templates/.agents/tools/mcp/marketing/meta-ads/README.md +15 -13
  297. package/templates/.agents/tools/mcp/marketing/meta-ads/manifest.yaml +15 -10
  298. package/templates/.agents/tools/profiles/development.yaml +3 -0
  299. package/templates/infra/terraform/.terraform.lock.hcl +42 -0
  300. package/templates/infra/terraform/README.md +31 -0
  301. package/templates/infra/terraform/backend.tf.json +13 -0
  302. package/templates/infra/terraform/deployment-inputs.example.yaml +22 -0
  303. package/templates/infra/terraform/fixtures/approved-targets.json +32 -0
  304. package/templates/infra/terraform/fixtures/plan-approved.json +67 -0
  305. package/templates/infra/terraform/main.tf +77 -0
  306. package/templates/infra/terraform/outputs.tf +20 -0
  307. package/templates/infra/terraform/terraform.tfvars.example +26 -0
  308. package/templates/infra/terraform/variables.tf +91 -0
  309. package/templates/infra/terraform/versions.tf +18 -0
  310. package/templates/scripts/validate-cloud-hostinger-contract.mjs +89 -0
  311. package/templates/scripts/validate-cloud-terraform-plan.mjs +1 -0
  312. package/templates/template-manifest.json +607 -167
  313. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-BwnJpl02.js +0 -3
  314. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-C09KmlSw.js +0 -68
  315. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-Dyo_wpL4.js +0 -2
  316. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-hPPfVGQv.css +0 -1
  317. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-stale-seed.ts +0 -50
@@ -163,6 +163,54 @@ describe("project settings persistence", () => {
163
163
  expect(architecture).toContain("- TypeScript");
164
164
  });
165
165
 
166
+ it("persists observability and on-demand testing components without requiring local detection", async () => {
167
+ const { filesystem } = await fixtureProject();
168
+ const current = await readProjectSettings(filesystem);
169
+ await filesystem.writeTextAtomic(
170
+ PROJECT_CONFIG_PATH,
171
+ serializeYaml({
172
+ ...current,
173
+ stack: {
174
+ ...current.stack,
175
+ overrides: { ...current.stack.overrides, components: { future_tool: { enabled: true } } },
176
+ },
177
+ }),
178
+ );
179
+ const updated = await persistProjectSettings(filesystem, {
180
+ ...current,
181
+ stack: {
182
+ ...current.stack,
183
+ overrides: {
184
+ components: {
185
+ prometheus: {
186
+ name: "Prometheus",
187
+ version: "3.6.0",
188
+ reference: "docker:prom/prometheus",
189
+ },
190
+ grafana: {
191
+ name: "Grafana",
192
+ version: "",
193
+ reference: "docker:grafana/grafana",
194
+ },
195
+ k6: { name: "k6", version: "1.1.0", reference: "docker:grafana/k6" },
196
+ autocannon: { name: "autocannon", version: "", reference: "npm:autocannon" },
197
+ },
198
+ },
199
+ },
200
+ });
201
+
202
+ expect(updated.stack.overrides.components).toMatchObject({
203
+ prometheus: { name: "Prometheus", version: "3.6.0" },
204
+ grafana: { name: "Grafana", version: "" },
205
+ k6: { name: "k6", version: "1.1.0" },
206
+ autocannon: { name: "autocannon", version: "" },
207
+ future_tool: { enabled: true },
208
+ });
209
+ await expect(filesystem.readText(SOFTWARE_ARCHITECTURE_PATH)).resolves.toContain(
210
+ "| Coleta de métricas | Prometheus | 3.6.0 | docker:prom/prometheus |",
211
+ );
212
+ });
213
+
166
214
  it("rejects invalid settings before touching the project file", async () => {
167
215
  const { configPath, filesystem } = await fixtureProject();
168
216
  const original = await readFile(configPath, "utf8");
@@ -359,7 +407,7 @@ describe("project settings persistence", () => {
359
407
  expect.objectContaining({ id: "skills", value: 2 }),
360
408
  expect.objectContaining({ id: "rules", value: 2 }),
361
409
  expect.objectContaining({ id: "hooks", value: 3 }),
362
- expect.objectContaining({ id: "database", value: "Ativo" }),
410
+ expect.objectContaining({ id: "database", value: "Inativo" }),
363
411
  ],
364
412
  stack_components: [
365
413
  expect.objectContaining({
@@ -375,6 +423,11 @@ describe("project settings persistence", () => {
375
423
  expect.objectContaining({ name: "BullMQ", verification: "unavailable" }),
376
424
  expect.objectContaining({ name: "Hono", verification: "unavailable" }),
377
425
  expect.objectContaining({ name: "React", verification: "unavailable" }),
426
+ expect.objectContaining({ name: "Git", configured_version: "latest" }),
427
+ expect.objectContaining({
428
+ name: "Terraform",
429
+ configured_version: "1.16.1",
430
+ }),
378
431
  ],
379
432
  areas: expect.arrayContaining([
380
433
  expect.objectContaining({
@@ -532,6 +585,7 @@ describe("project settings persistence", () => {
532
585
  version: null,
533
586
  configured_version: "4.12.34",
534
587
  installation: "bundled",
588
+ verification: "unavailable",
535
589
  source: ".agents/dashboard/packages/local-runtime/server.mjs",
536
590
  }),
537
591
  expect.objectContaining({
@@ -539,12 +593,47 @@ describe("project settings persistence", () => {
539
593
  version: null,
540
594
  configured_version: "19.2.7",
541
595
  installation: "bundled",
596
+ verification: "unavailable",
542
597
  source: ".agents/dashboard/apps/frontend/dist",
543
598
  }),
544
599
  ]),
545
600
  );
546
601
  });
547
602
 
603
+ it("reads pnpm workspace manifests and validates their entrypoints", async () => {
604
+ const { filesystem } = await fixtureProject();
605
+ for (const [name, version, directory] of [
606
+ ["hono", "4.12.35", ".agents/dashboard/packages/local-runtime/node_modules/hono"],
607
+ ["react", "19.2.6", ".agents/dashboard/apps/frontend/node_modules/react"],
608
+ ]) {
609
+ await filesystem.writeTextAtomic(
610
+ `${directory}/package.json`,
611
+ JSON.stringify({ name, version, main: "index.js" }),
612
+ );
613
+ await filesystem.writeTextAtomic(`${directory}/index.js`, "export {};");
614
+ const overview = await readProjectOverview(filesystem);
615
+ expect(
616
+ overview.stack_components.find((item) => item.name.toLowerCase() === name),
617
+ ).toMatchObject({
618
+ version,
619
+ source: `${directory}/package.json`,
620
+ verification: "exact",
621
+ installation: "installed",
622
+ });
623
+ await filesystem.writeTextAtomic(
624
+ `${directory}/package.json`,
625
+ JSON.stringify({ name, version, main: "missing.js" }),
626
+ );
627
+ const incomplete = await readProjectOverview(filesystem);
628
+ expect(
629
+ incomplete.stack_components.find((item) => item.name.toLowerCase() === name),
630
+ ).toMatchObject({
631
+ version: null,
632
+ verification: "unavailable",
633
+ });
634
+ }
635
+ });
636
+
548
637
  it("does not accept a local package without its matching identity and entrypoint", async () => {
549
638
  const { filesystem } = await fixtureProject();
550
639
  await mkdir(join(filesystem.root, "node_modules", "hono"), { recursive: true });
@@ -1,3 +1,6 @@
1
+ import { execFile } from "node:child_process";
2
+ import { join } from "node:path";
3
+ import { promisify } from "node:util";
1
4
  import {
2
5
  PROJECT_CONFIG_PATH,
3
6
  type ProjectConfig,
@@ -11,8 +14,9 @@ import {
11
14
  const AGENT_CATALOG_PATH = ".agents/config/agents/catalog.yaml";
12
15
  const CODEX_CONFIG_PATH = ".codex/config.toml";
13
16
  const FRAMEWORK_LOCK_PATH = ".agents/config/framework.lock.yaml";
14
- const SQLITE_READ_MODEL_PATH = ".local/dashboard-sqlite/read-model.sqlite";
17
+ const SQLITE_READ_MODEL_PATH = ".local/sqlite/zaip-aios-dashboard.sqlite";
15
18
  const DASHBOARD_STACK_DEPENDENCIES_PATH = ".agents/dashboard/stack-dependencies.json";
19
+ const exec = promisify(execFile);
16
20
  export const SOFTWARE_ARCHITECTURE_PATH = ".agents/architecture/software-architecture.md";
17
21
 
18
22
  export type ProjectOverview = {
@@ -145,11 +149,26 @@ async function countCodexHookEvents(filesystem: WorkspaceFileSystem): Promise<nu
145
149
  async function readVerifiedStackComponents(
146
150
  filesystem: WorkspaceFileSystem,
147
151
  ): Promise<ProjectOverview["stack_components"]> {
148
- const [project, fallback, bundledRuntime, bundledFrontend] = await Promise.all([
152
+ const [project, fallback, bundledRuntime, bundledFrontend, git, terraform] = await Promise.all([
149
153
  readProjectSettings(filesystem),
150
154
  readDashboardStackDependencies(filesystem),
151
155
  hasNonEmptyFile(filesystem, ".agents/dashboard/packages/local-runtime/server.mjs"),
152
156
  hasBundledDashboardFrontend(filesystem),
157
+ executableComponent(
158
+ filesystem,
159
+ "Git",
160
+ ["--version"],
161
+ "git",
162
+ (output) => /^git version (.+)$/m.exec(output.trim())?.[1] ?? null,
163
+ ),
164
+ executableComponent(filesystem, "Terraform", ["version", "-json"], "terraform", (output) => {
165
+ try {
166
+ const version = JSON.parse(output).terraform_version;
167
+ return typeof version === "string" ? version : null;
168
+ } catch {
169
+ return null;
170
+ }
171
+ }),
153
172
  ]);
154
173
  const configured = configuredStackVersions(project, fallback);
155
174
  const packages = await Promise.all(
@@ -167,34 +186,41 @@ async function readVerifiedStackComponents(
167
186
  packageComponent("BullMQ", "bullmq", configured.get("bullmq"), installed),
168
187
  packageComponent("Hono", "hono", configured.get("hono"), installed, bundledRuntime),
169
188
  packageComponent("React", "react", configured.get("react"), installed, bundledFrontend),
189
+ git,
190
+ terraform,
170
191
  ];
171
192
  }
172
193
 
173
- type InstalledPackage = { name: string; version: string | null };
194
+ type InstalledPackage = { name: string; version: string | null; source: string | null };
174
195
  type DashboardStackDependencies = { node: string | null; packages: Map<string, string> };
175
196
 
176
197
  async function readInstalledPackageVersion(
177
198
  filesystem: WorkspaceFileSystem,
178
199
  name: string,
179
200
  ): Promise<InstalledPackage> {
180
- try {
181
- const value = recordValue(
182
- JSON.parse(await filesystem.readText(`node_modules/${name}/package.json`)),
183
- );
184
- const version = stringValue(value.version);
185
- const entrypoint = packageEntrypoint(value);
186
- if (
187
- value.name !== name ||
188
- version === null ||
189
- !/^\d+\.\d+\.\d+(?:-[\w.-]+)?(?:\+[\w.-]+)?$/.test(version) ||
190
- entrypoint === null
191
- )
192
- return { name, version: null };
193
- await filesystem.readText(`node_modules/${name}/${entrypoint}`);
194
- return { name, version };
195
- } catch {
196
- return { name, version: null };
201
+ const roots = ["node_modules"];
202
+ if (name === "hono") roots.push(".agents/dashboard/packages/local-runtime/node_modules");
203
+ if (name === "react") roots.push(".agents/dashboard/apps/frontend/node_modules");
204
+ for (const root of roots) {
205
+ try {
206
+ const source = `${root}/${name}/package.json`;
207
+ const value = recordValue(JSON.parse(await filesystem.readText(source)));
208
+ const version = stringValue(value.version);
209
+ const entrypoint = packageEntrypoint(value);
210
+ if (
211
+ value.name !== name ||
212
+ version === null ||
213
+ !/^\d+\.\d+\.\d+(?:-[\w.-]+)?(?:\+[\w.-]+)?$/.test(version) ||
214
+ entrypoint === null
215
+ )
216
+ continue;
217
+ await filesystem.readText(`${root}/${name}/${entrypoint}`);
218
+ return { name, version, source };
219
+ } catch {
220
+ // A pnpm workspace can install dependencies in the owning package only.
221
+ }
197
222
  }
223
+ return { name, version: null, source: null };
198
224
  }
199
225
 
200
226
  async function readDashboardStackDependencies(
@@ -257,6 +283,55 @@ async function hasNonEmptyFile(filesystem: WorkspaceFileSystem, path: string): P
257
283
  }
258
284
  }
259
285
 
286
+ async function executableComponent(
287
+ filesystem: WorkspaceFileSystem,
288
+ name: string,
289
+ args: string[],
290
+ command: "git" | "terraform",
291
+ parse: (output: string) => string | null,
292
+ ): Promise<ProjectOverview["stack_components"][number]> {
293
+ const project = await readProjectSettings(filesystem);
294
+ const components = recordValue(recordValue(project.stack.overrides).components);
295
+ const configuredVersion =
296
+ command === "git"
297
+ ? "latest"
298
+ : (stringValue(recordValue(components.backend_infrastructure).version) ?? "1.16.1");
299
+ const safeTerraformVersion = /^\d+\.\d+\.\d+$/.test(configuredVersion)
300
+ ? configuredVersion
301
+ : "1.16.1";
302
+ const local =
303
+ command === "git"
304
+ ? ".local/git/cmd/git.exe"
305
+ : `.local/terraform/v${safeTerraformVersion}-${process.platform}-${process.arch}/${
306
+ process.platform === "win32" ? "terraform.exe" : "terraform"
307
+ }`;
308
+ const localPath = join(filesystem.root, local);
309
+ const executable = (await filesystem.exists(local)) ? localPath : command;
310
+ try {
311
+ const version = parse(
312
+ (await exec(executable, args, { timeout: 10_000, windowsHide: true })).stdout,
313
+ );
314
+ if (version === null) throw new Error("unsupported version output");
315
+ return {
316
+ name,
317
+ version,
318
+ configured_version: configuredVersion,
319
+ source: executable === command ? `Comando do workspace: ${command}` : local,
320
+ verification: "exact",
321
+ installation: executable === command ? "runtime" : "installed",
322
+ };
323
+ } catch {
324
+ return {
325
+ name,
326
+ version: null,
327
+ configured_version: configuredVersion,
328
+ source: null,
329
+ verification: "unavailable",
330
+ installation: "missing",
331
+ };
332
+ }
333
+ }
334
+
260
335
  function runtimeComponent(
261
336
  name: string,
262
337
  version: string | null,
@@ -285,7 +360,7 @@ function packageComponent(
285
360
  name,
286
361
  version: actual,
287
362
  configured_version: configuredVersion ?? null,
288
- source: `node_modules/${packageName}/package.json`,
363
+ source: installed.get(packageName)?.source ?? null,
289
364
  verification: "exact",
290
365
  installation: "installed",
291
366
  };
@@ -298,7 +373,7 @@ function packageComponent(
298
373
  packageName === "hono"
299
374
  ? ".agents/dashboard/packages/local-runtime/server.mjs"
300
375
  : ".agents/dashboard/apps/frontend/dist",
301
- verification: configuredVersion === undefined ? "unavailable" : "constraint",
376
+ verification: "unavailable",
302
377
  installation: "bundled",
303
378
  };
304
379
  return {
@@ -332,6 +407,8 @@ export async function persistProjectSettings(
332
407
  if (requested.tracking.storage_mode !== "tracked_sanitized") {
333
408
  throw new TypeError("This project requires Git-native tracked_sanitized storage.");
334
409
  }
410
+ const currentComponents = recordValue(current.stack.overrides.components);
411
+ const requestedComponents = recordValue(requested.stack.overrides.components);
335
412
  const candidate = parseProjectConfig({
336
413
  ...requested,
337
414
  clients: current.clients,
@@ -342,6 +419,9 @@ export async function persistProjectSettings(
342
419
  overrides: {
343
420
  ...current.stack.overrides,
344
421
  ...requested.stack.overrides,
422
+ ...(Object.keys(currentComponents).length > 0 || Object.keys(requestedComponents).length > 0
423
+ ? { components: { ...currentComponents, ...requestedComponents } }
424
+ : {}),
345
425
  },
346
426
  },
347
427
  });
@@ -371,6 +451,7 @@ const componentLabels: Record<string, string> = {
371
451
  cache_messaging: "Cache e mensageria",
372
452
  async_queue: "Filas e processamento assíncrono",
373
453
  backend_infrastructure: "Infraestrutura backend",
454
+ backend_hosting: "Hospedagem do backend",
374
455
  web_framework: "Framework do Web App",
375
456
  web_language: "Linguagem do Web App",
376
457
  web_build: "Build do Web App",
@@ -380,6 +461,14 @@ const componentLabels: Record<string, string> = {
380
461
  site_islands: "Componentes interativos",
381
462
  site_build: "Build do site",
382
463
  site_hosting: "Hospedagem do site",
464
+ prometheus: "Coleta de métricas",
465
+ grafana: "Dashboards",
466
+ uptime_kuma: "Monitoramento de disponibilidade",
467
+ cadvisor: "Métricas de containers",
468
+ node_exporter: "Métricas de host",
469
+ redis_exporter: "Métricas do Redis",
470
+ k6: "Teste de carga",
471
+ autocannon: "Teste HTTP de carga",
383
472
  };
384
473
 
385
474
  const architectureLabels: Record<string, string> = {
@@ -387,6 +476,7 @@ const architectureLabels: Record<string, string> = {
387
476
  backend_api_and_business_rules: "APIs e regras de negócio",
388
477
  backend_async_processing: "Filas e processamento assíncrono",
389
478
  backend_infrastructure: "Infraestrutura backend",
479
+ backend_hosting: "Hospedagem do backend",
390
480
  };
391
481
 
392
482
  export function renderSoftwareArchitecture(config: ProjectConfig): string {
@@ -0,0 +1,39 @@
1
+ export const agentPortraitFiles: Record<string, string> = {
2
+ "agent-orchestrator": "1 - Dave - Product Owner.png",
3
+ "agent-builder": "2 - Rafa - Tech Lead.png",
4
+ "agent-task-executor": "3 - Leo - Software Engineer.png",
5
+ "agent-analyst": "4 - Bia - Product Manager.png",
6
+ "agent-finalizer": "5 - Gui - Quality Assurance Lead.png",
7
+ "agent-reviewer": "6 - Nanda - Principal Engineer.png",
8
+ "agent-integrator": "7 - Dani - Integration Engineer.png",
9
+ "agent-auditor": "8 - Ana - Quality Auditor.png",
10
+ "agent-text-summarizer": "9 - Lia - Technical Writer.png",
11
+ "agent-data-analyst": "10 - Gabi - Data Analyst.png",
12
+ "agent-refactoring-specialist": "11 - Tati - Refactoring Specialist.png",
13
+ "agent-frontend": "12 - Doug - Frontend Developer.png",
14
+ "agent-backend": "13 - Will - Backend Developer.png",
15
+ "agent-website-developer": "14 - Jonny - Website Developer.png",
16
+ "agent-designer": "15 - Manu - Product Designer.png",
17
+ "agent-cloud-engineer": "16 - Dan - Cloud Engineer.png",
18
+ "agent-security-auditor": "17 - Luis - Security Auditor.png",
19
+ "agent-marketing-analyst": "18 - Lucia - Marketing Analyst.png",
20
+ };
21
+
22
+ interface ActiveWorkItem {
23
+ kind: string;
24
+ id: string;
25
+ status: string | null;
26
+ }
27
+
28
+ export function openWorkIds(items: readonly ActiveWorkItem[]): string[] {
29
+ const kinds = new Set(["prd", "spec", "change", "task", "execution"]);
30
+ const statuses = new Set(["active", "executing", "verifying", "running", "paused"]);
31
+ return items
32
+ .filter(
33
+ (item) =>
34
+ kinds.has(item.kind) &&
35
+ (statuses.has(item.status ?? "") || (item.kind === "task" && item.status === "planned")),
36
+ )
37
+ .map((item) => item.id)
38
+ .sort();
39
+ }
@@ -0,0 +1,33 @@
1
+ import type { Hono } from "hono";
2
+ import { streamSSE } from "hono/streaming";
3
+
4
+ export function registerBrowserLeaseRoute(
5
+ app: Hono,
6
+ apiPrefix: string,
7
+ launcherOwned = false,
8
+ ): void {
9
+ const browserLeases = new Set<object>();
10
+ let browserLeaseDeadline: NodeJS.Timeout | undefined;
11
+ if (launcherOwned) {
12
+ browserLeaseDeadline = setTimeout(() => process.exit(0), 12_000);
13
+ browserLeaseDeadline.unref();
14
+ }
15
+ const releaseBrowserLease = (lease: object) => {
16
+ browserLeases.delete(lease);
17
+ if (!launcherOwned || browserLeases.size !== 0) return;
18
+ if (browserLeaseDeadline !== undefined) clearTimeout(browserLeaseDeadline);
19
+ browserLeaseDeadline = setTimeout(() => process.exit(0), 12_000);
20
+ browserLeaseDeadline.unref();
21
+ };
22
+ app.get(`${apiPrefix}/browser-lease`, (context) => {
23
+ const lease = {};
24
+ if (browserLeaseDeadline !== undefined) clearTimeout(browserLeaseDeadline);
25
+ browserLeaseDeadline = undefined;
26
+ browserLeases.add(lease);
27
+ return streamSSE(context, async (stream) => {
28
+ stream.onAbort(() => releaseBrowserLease(lease));
29
+ await stream.writeSSE({ event: "lease", data: "active" });
30
+ await new Promise(() => {});
31
+ });
32
+ });
33
+ }
@@ -1,12 +1,96 @@
1
- import { resolve } from "node:path";
1
+ import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
2
+ import { join, resolve } from "node:path";
2
3
  import { WorkspaceFileSystem, type WorkspaceLayout } from "@zaip-ai/core";
3
- import { describe, expect, it, vi } from "vitest";
4
+ import { afterEach, describe, expect, it, vi } from "vitest";
4
5
  import { createDashboardApp, reconciliationNeedsRefresh } from "./server.js";
6
+ import { ReconciliationCommandTimeoutError, reconciliationResult } from "./server-workspace.js";
7
+ import { rebuildSqliteReadModel, SqliteReadModelFileSystem } from "./sqlite-read-model.js";
5
8
 
6
9
  const workspaceRoot = resolve(import.meta.dirname, "../../../../..");
7
10
  const v2Layout = { version: 3 } as WorkspaceLayout;
11
+ const fixtureRoots: string[] = [];
12
+ const fixtureBase = join(workspaceRoot, ".tmp", "server-reconciliation-refresh-tests");
13
+
14
+ async function createSqliteFixture(): Promise<string> {
15
+ await mkdir(fixtureBase, { recursive: true });
16
+ const root = await mkdtemp(join(fixtureBase, "fixture-"));
17
+ fixtureRoots.push(root);
18
+ await mkdir(join(root, ".agents", "config"), { recursive: true });
19
+ await Promise.all([
20
+ readFile(join(workspaceRoot, ".agents", "config", "project.yaml"), "utf8").then((content) =>
21
+ writeFile(join(root, ".agents", "config", "project.yaml"), content),
22
+ ),
23
+ readFile(join(workspaceRoot, ".agents", "config", "framework.lock.yaml"), "utf8").then(
24
+ (content) => writeFile(join(root, ".agents", "config", "framework.lock.yaml"), content),
25
+ ),
26
+ ]);
27
+ await rebuildSqliteReadModel(root);
28
+ return root;
29
+ }
30
+
31
+ afterEach(async () => {
32
+ await Promise.all(
33
+ fixtureRoots.splice(0).map((root) => rm(root, { recursive: true, force: true })),
34
+ );
35
+ });
8
36
 
9
37
  describe("reconciliation snapshot refresh", () => {
38
+ it("publishes a stable readiness revision and changes it after a status-only snapshot commit", async () => {
39
+ const fixtureRoot = await createSqliteFixture();
40
+ const taskPath = join(fixtureRoot, ".agents/records/tasks/TASK-REVISION.md");
41
+ await mkdir(join(fixtureRoot, ".agents/records/tasks"), { recursive: true });
42
+ const task = (status: string) => `---
43
+ schema_version: 2
44
+ id: TASK-REVISION
45
+ title: Snapshot status revision
46
+ status: ${status}
47
+ created_at: 2026-09-05T10:00:00.000Z
48
+ ---
49
+ # Task
50
+ `;
51
+ await writeFile(taskPath, task("executing"));
52
+ await rebuildSqliteReadModel(fixtureRoot);
53
+ const filesystem = new SqliteReadModelFileSystem(fixtureRoot);
54
+ const refresh = vi.fn(async () => undefined);
55
+ const app = createDashboardApp(fixtureRoot, "test-session", undefined, {
56
+ snapshotReadOnly: true,
57
+ filesystem,
58
+ refreshReadModel: refresh,
59
+ layout: v2Layout,
60
+ });
61
+ const ready = async () => {
62
+ const response = await app.request("/api/v2/ready", {
63
+ headers: { "x-zaip-session": "test-session" },
64
+ });
65
+ expect(response.status).toBe(200);
66
+ return response.json() as Promise<{
67
+ snapshot_revision: string;
68
+ activity_count: number;
69
+ day_count: number;
70
+ }>;
71
+ };
72
+ try {
73
+ const before = await ready();
74
+ expect(before.snapshot_revision).toMatch(/^[a-f0-9]{64}$/);
75
+ await expect(ready()).resolves.toEqual(before);
76
+ await writeFile(taskPath, task("completed"));
77
+ // Canonical edits do not pretend that the opened snapshot was promoted.
78
+ await expect(ready()).resolves.toEqual(before);
79
+ // Use the production refresh boundary, which releases Windows readers
80
+ // before replacing an older snapshot generation.
81
+ await filesystem.rebuildAndRefresh(() => rebuildSqliteReadModel(fixtureRoot));
82
+ const after = await ready();
83
+ expect(after.snapshot_revision).not.toBe(before.snapshot_revision);
84
+ expect(after.activity_count).toBe(before.activity_count);
85
+ expect(after.day_count).toBe(before.day_count);
86
+ await filesystem.rebuildAndRefresh(() => rebuildSqliteReadModel(fixtureRoot));
87
+ await expect(ready()).resolves.toEqual(after);
88
+ expect(refresh).not.toHaveBeenCalled();
89
+ } finally {
90
+ filesystem.close();
91
+ }
92
+ });
93
+
10
94
  it("refreshes only when reconciliation changed canonical work", () => {
11
95
  expect(
12
96
  reconciliationNeedsRefresh({ reconciled_ids: [], blocked: [], not_reconciled: [] }),
@@ -25,9 +109,35 @@ describe("reconciliation snapshot refresh", () => {
25
109
  not_reconciled: [],
26
110
  }),
27
111
  ).toBe(true);
112
+ expect(
113
+ reconciliationNeedsRefresh(
114
+ reconciliationResult({
115
+ reconciliations: [
116
+ {
117
+ status: "closed",
118
+ specs: [{ id: "SPEC-DELIVERED", status: "delivered" }],
119
+ prds: [{ id: "PRD-DELIVERED", status: "delivered" }],
120
+ },
121
+ ],
122
+ }),
123
+ ),
124
+ ).toBe(true);
28
125
  });
29
126
 
30
- it("serves health while a changed reconciliation refreshes in a worker", async () => {
127
+ it("reports a closed Run as a snapshot-changing reconciliation without a Task graph", () => {
128
+ expect(
129
+ reconciliationNeedsRefresh(
130
+ reconciliationResult({
131
+ runs: [{ run: { run_id: "RUN-CONVERSATION-CLOSED" } }],
132
+ reconciliations: [],
133
+ }),
134
+ ),
135
+ ).toBe(true);
136
+ });
137
+
138
+ it("returns a compact receipt while a changed reconciliation refreshes in a worker", async () => {
139
+ const fixtureRoot = await createSqliteFixture();
140
+ const filesystem = new SqliteReadModelFileSystem(fixtureRoot);
31
141
  let rejectRefresh: ((error: Error) => void) | undefined;
32
142
  const refresh = vi.fn(
33
143
  () =>
@@ -35,9 +145,10 @@ describe("reconciliation snapshot refresh", () => {
35
145
  rejectRefresh = reject;
36
146
  }),
37
147
  );
38
- const app = createDashboardApp(workspaceRoot, "test-session", undefined, {
148
+ const app = createDashboardApp(fixtureRoot, "test-session", undefined, {
39
149
  snapshotReadOnly: true,
40
- canonicalFilesystem: new WorkspaceFileSystem(workspaceRoot),
150
+ filesystem,
151
+ canonicalFilesystem: new WorkspaceFileSystem(fixtureRoot),
41
152
  reconcileOpenWork: async () => ({
42
153
  reconciled_ids: ["TASK-CHANGED"],
43
154
  blocked: [],
@@ -50,17 +161,43 @@ describe("reconciliation snapshot refresh", () => {
50
161
  method: "POST",
51
162
  headers: { "x-zaip-session": "test-session" },
52
163
  });
164
+ const response = await reconciliation;
165
+ expect(response.status, await response.clone().text()).toBe(200);
166
+ await expect(response.json()).resolves.toMatchObject({
167
+ snapshot_state: "updating",
168
+ projection_refresh_completed: false,
169
+ reconciliation: { reconciled_ids: ["TASK-CHANGED"] },
170
+ });
53
171
  await vi.waitFor(() => expect(refresh).toHaveBeenCalledOnce());
54
172
  await expect(
55
173
  app.request("/api/v2/health", { headers: { "x-zaip-session": "test-session" } }),
56
- ).resolves.toMatchObject({
57
- status: 200,
58
- });
174
+ ).resolves.toMatchObject({ status: 200 });
59
175
  rejectRefresh?.(new Error("simulated worker refresh failure"));
60
- const response = await reconciliation;
61
- expect(response.status).toBe(400);
176
+ await vi.waitFor(() => expect(refresh).toHaveBeenCalledOnce());
177
+ filesystem.close();
178
+ });
179
+
180
+ it("returns a finite partial-reconciliation error and refreshes the projection", async () => {
181
+ const refresh = vi.fn(async () => undefined);
182
+ const app = createDashboardApp(workspaceRoot, "test-session", undefined, {
183
+ snapshotReadOnly: true,
184
+ canonicalFilesystem: new WorkspaceFileSystem(workspaceRoot),
185
+ reconcileOpenWork: async () => {
186
+ throw new ReconciliationCommandTimeoutError();
187
+ },
188
+ refreshReadModel: refresh,
189
+ layout: v2Layout,
190
+ });
191
+
192
+ const response = await app.request("/api/v2/reconcile-open-work", {
193
+ method: "POST",
194
+ headers: { "x-zaip-session": "test-session" },
195
+ });
196
+ expect(response.status).toBe(408);
62
197
  await expect(response.json()).resolves.toMatchObject({
63
- error: "simulated worker refresh failure",
198
+ reconciliation_may_be_partial: true,
199
+ projection_refresh_started: true,
64
200
  });
201
+ await vi.waitFor(() => expect(refresh).toHaveBeenCalledOnce());
65
202
  });
66
203
  });