@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
@@ -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
  );
@@ -0,0 +1,111 @@
1
+ import { DatabaseSync } from "node:sqlite";
2
+ import { describe, expect, it } from "vitest";
3
+ import { agentProductivity, attachAgentProductivity } from "./sqlite-agent-productivity.js";
4
+
5
+ function fixture() {
6
+ const db = new DatabaseSync(":memory:");
7
+ db.exec(`CREATE TABLE zaip_aios_activities (payload_type TEXT,payload_json TEXT,run_id TEXT,zaip_agent TEXT,occurred_day_sp TEXT,phase TEXT,status TEXT,task_id TEXT,agent_invocation_id TEXT,zaip_agent_persona TEXT,zaip_agent_job TEXT);
8
+ INSERT INTO zaip_aios_activities(run_id,zaip_agent,occurred_day_sp) VALUES ('CREATE','dave','2026-09-01'),('CREATE','dave','2026-09-01'),('LATER','leo','2026-09-02'); UPDATE zaip_aios_activities SET task_id='T1' WHERE payload_type IS NULL;`);
9
+ const put = (type: string, value: object) =>
10
+ db
11
+ .prepare("INSERT INTO zaip_aios_activities(payload_type,payload_json) VALUES (?,?)")
12
+ .run(type, JSON.stringify(value));
13
+ put("work_item", {
14
+ entity_kind: "task",
15
+ id: "T1",
16
+ creation_run_id: "CREATE",
17
+ occurred_day_sp: "2026-09-01",
18
+ });
19
+ put("work_item", {
20
+ entity_kind: "prd",
21
+ id: "P1",
22
+ creation_run_id: "CREATE",
23
+ occurred_day_sp: "2026-09-01",
24
+ });
25
+ put("work_item", {
26
+ entity_kind: "spec",
27
+ id: "S1",
28
+ creation_run_id: "CREATE",
29
+ occurred_day_sp: "2026-09-01",
30
+ });
31
+ for (const path of ["a/artifacts/tests/one.md", "b/artifacts/tests/duplicate.md"])
32
+ put("file", {
33
+ path,
34
+ artifact_kind: "validation",
35
+ artifact_id: "TEST1",
36
+ artifact_run_id: "LATER",
37
+ artifact_occurred_day_sp: "2026-09-02",
38
+ });
39
+ put("file", {
40
+ path: "a/artifacts/validations/review.md",
41
+ artifact_kind: "validation",
42
+ artifact_id: "REVIEW",
43
+ artifact_run_id: "LATER",
44
+ artifact_occurred_day_sp: "2026-09-02",
45
+ });
46
+ put("file", {
47
+ path: "a/artifacts/evidences/e.md",
48
+ artifact_kind: "evidence",
49
+ artifact_id: "E1",
50
+ artifact_run_id: "LATER",
51
+ artifact_occurred_day_sp: "2026-09-02",
52
+ });
53
+ return db;
54
+ }
55
+
56
+ describe("agent productivity", () => {
57
+ it("counts a shared Task for each agent without duplicating repeated Runs by that agent", () => {
58
+ const db = fixture();
59
+ try {
60
+ const result = agentProductivity(db, {});
61
+ expect(result.get("dave")).toEqual({
62
+ prd_count: 1,
63
+ spec_count: 1,
64
+ task_count: 1,
65
+ test_count: 0,
66
+ evidence_count: 0,
67
+ });
68
+ expect(result.get("leo")).toEqual({
69
+ prd_count: 0,
70
+ spec_count: 0,
71
+ task_count: 1,
72
+ test_count: 1,
73
+ evidence_count: 1,
74
+ });
75
+ expect(agentProductivity(db, { from: "2026-09-02", to: "2026-09-02" }).has("dave")).toBe(
76
+ false,
77
+ );
78
+ expect(agentProductivity(db, { agent: "leo" }).get("leo")?.task_count).toBe(1);
79
+ } finally {
80
+ db.close();
81
+ }
82
+ });
83
+ it("counts participation without creation metadata and sums memberships in the full-population footer", () => {
84
+ const db = fixture();
85
+ try {
86
+ db.exec(
87
+ `UPDATE zaip_aios_activities SET payload_json=json_remove(payload_json,'$.creation_run_id') WHERE payload_type='work_item' AND json_extract(payload_json,'$.entity_kind')='task'`,
88
+ );
89
+ const page = {
90
+ rows: [{ key: "dave", task_count: 50, total_tokens: 12 }],
91
+ totals: { task_count: 50, total_tokens: 100 },
92
+ };
93
+ attachAgentProductivity(db, {}, page);
94
+ expect(page.rows[0]).toMatchObject({
95
+ task_count: 1,
96
+ tasks_active: 50,
97
+ prd_count: 1,
98
+ total_tokens: 12,
99
+ });
100
+ expect(page.totals).toMatchObject({
101
+ task_count: 2,
102
+ prd_count: 1,
103
+ test_count: 1,
104
+ evidence_count: 1,
105
+ total_tokens: 100,
106
+ });
107
+ } finally {
108
+ db.close();
109
+ }
110
+ });
111
+ });
@@ -0,0 +1,140 @@
1
+ import type { DatabaseSync } from "node:sqlite";
2
+ import type { DateOnlyCostPageInput } from "./sqlite-cost-agent-page.js";
3
+
4
+ const fields = ["prd_count", "spec_count", "task_count", "test_count", "evidence_count"] as const;
5
+ type Counts = Record<(typeof fields)[number], number | null>;
6
+ const empty = (): Counts => ({
7
+ prd_count: 0,
8
+ spec_count: 0,
9
+ task_count: 0,
10
+ test_count: 0,
11
+ evidence_count: 0,
12
+ });
13
+
14
+ /** Tasks count participation per agent; macro creation and artifacts keep their Run attribution. */
15
+ export function agentProductivity(database: DatabaseSync, input: DateOnlyCostPageInput) {
16
+ const clauses = ["a.payload_type IS NULL", "a.run_id IS NOT NULL", "a.zaip_agent IS NOT NULL"];
17
+ const values: string[] = [];
18
+ for (const bound of ["from", "to"] as const) {
19
+ if (!input[bound]) continue;
20
+ clauses.push(`a.occurred_day_sp ${bound === "from" ? ">=" : "<="} ?`);
21
+ values.push(input[bound].slice(0, 10));
22
+ }
23
+ for (const field of ["agent", "phase", "status"] as const) {
24
+ if (!input[field]) continue;
25
+ clauses.push(`a.${field === "agent" ? "zaip_agent" : field}=?`);
26
+ values.push(input[field]);
27
+ }
28
+ if (input.risk) {
29
+ clauses.push(
30
+ "EXISTS (SELECT 1 FROM zaip_aios_activities t WHERE t.payload_type='work_item' AND json_extract(t.payload_json,'$.entity_kind')='task' AND json_extract(t.payload_json,'$.id')=a.task_id AND json_extract(t.payload_json,'$.risk')=?)",
31
+ );
32
+ values.push(input.risk);
33
+ }
34
+ if (input.priority || input.workType) clauses.push("0");
35
+ if (input.origin)
36
+ clauses.push(
37
+ input.origin === "prd"
38
+ ? "a.prd_id IS NOT NULL"
39
+ : input.origin === "spec"
40
+ ? "a.prd_id IS NULL AND a.spec_id IS NOT NULL"
41
+ : input.origin === "task"
42
+ ? "a.prd_id IS NULL AND a.spec_id IS NULL AND a.task_id IS NOT NULL"
43
+ : "a.prd_id IS NULL AND a.spec_id IS NULL AND a.task_id IS NULL",
44
+ );
45
+ const usage: string[] = [];
46
+ for (const field of ["provider", "model"] as const) {
47
+ if (!input[field]) continue;
48
+ usage.push(`COALESCE(u.${field},'unavailable')=?`);
49
+ values.push(input[field]);
50
+ }
51
+ if (usage.length)
52
+ clauses.push(
53
+ `EXISTS (SELECT 1 FROM invocation_usage u WHERE u.agent_invocation_id=a.agent_invocation_id AND ${usage.join(" AND ")})`,
54
+ );
55
+ if (input.query) {
56
+ clauses.push("(a.zaip_agent LIKE ? OR a.zaip_agent_persona LIKE ? OR a.zaip_agent_job LIKE ?)");
57
+ values.push(...Array<string>(3).fill(`%${input.query}%`));
58
+ }
59
+ const owners = database
60
+ .prepare(
61
+ `SELECT a.run_id, MIN(a.zaip_agent) AS agent FROM zaip_aios_activities a WHERE ${clauses.join(" AND ")} GROUP BY a.run_id HAVING COUNT(DISTINCT a.zaip_agent)=1`,
62
+ )
63
+ .all(...values) as Array<{ run_id: string; agent: string }>;
64
+ const agentByRun = new Map(owners.map((row) => [row.run_id, row.agent]));
65
+ const counts = new Map<string, Counts>();
66
+ const seen = new Set<string>();
67
+ const from = input.from?.slice(0, 10) ?? "0000-01-01";
68
+ const to = input.to?.slice(0, 10) ?? "9999-12-31";
69
+ const facts = database
70
+ .prepare(`WITH files AS (
71
+ SELECT json_extract(payload_json,'$.path') path,
72
+ json_extract(payload_json,'$.artifact_kind') artifact_kind,
73
+ json_extract(payload_json,'$.artifact_id') artifact_id,
74
+ json_extract(payload_json,'$.artifact_run_id') artifact_run_id,
75
+ json_extract(payload_json,'$.artifact_occurred_day_sp') artifact_occurred_day_sp
76
+ FROM zaip_aios_activities WHERE payload_type='file'
77
+ )
78
+ SELECT json_extract(payload_json,'$.entity_kind') AS kind,
79
+ json_extract(payload_json,'$.id') id, json_extract(payload_json,'$.creation_run_id') AS run_id,
80
+ json_extract(payload_json,'$.occurred_day_sp') AS day
81
+ FROM zaip_aios_activities WHERE payload_type='work_item'
82
+ AND json_extract(payload_json,'$.entity_kind') IN ('prd','spec')
83
+ AND json_extract(payload_json,'$.creation_run_id') IS NOT NULL
84
+ UNION ALL
85
+ SELECT CASE WHEN artifact_kind='validation' THEN 'test' ELSE 'evidence' END,
86
+ COALESCE(artifact_id,path), artifact_run_id, artifact_occurred_day_sp
87
+ FROM files WHERE artifact_run_id IS NOT NULL AND
88
+ (artifact_kind='evidence' OR (artifact_kind='validation' AND path LIKE '%/artifacts/tests/%'))
89
+ `)
90
+ .all() as Array<{ kind: string; id: string; run_id: string; day: string }>;
91
+ for (const fact of facts) {
92
+ if (!fact.day || fact.day < from || fact.day > to) continue;
93
+ const owner = agentByRun.get(fact.run_id);
94
+ if (!owner) continue;
95
+ const key = `${fact.kind}:${fact.id}`;
96
+ if (seen.has(key)) continue;
97
+ seen.add(key);
98
+ const row = counts.get(owner) ?? empty();
99
+ const field = `${fact.kind}_count` as keyof Counts;
100
+ row[field] = (row[field] ?? 0) + 1;
101
+ counts.set(owner, row);
102
+ }
103
+ // Repeated Runs by one agent do not create extra Tasks. The same Task is
104
+ // counted once for EACH participating agent, so the footer sums memberships.
105
+ const taskCounts = database
106
+ .prepare(`SELECT a.zaip_agent AS agent,
107
+ COUNT(DISTINCT NULLIF(trim(a.task_id),'')) AS task_count
108
+ FROM zaip_aios_activities a WHERE ${clauses.join(" AND ")}
109
+ GROUP BY a.zaip_agent`)
110
+ .all(...values) as Array<{ agent: string; task_count: number }>;
111
+ for (const participation of taskCounts) {
112
+ const row = counts.get(participation.agent) ?? empty();
113
+ row.task_count = participation.task_count;
114
+ counts.set(participation.agent, row);
115
+ }
116
+ return counts;
117
+ }
118
+
119
+ export function attachAgentProductivity(
120
+ database: DatabaseSync,
121
+ input: DateOnlyCostPageInput,
122
+ page: { rows: Array<Record<string, unknown>>; totals?: Record<string, unknown> },
123
+ ) {
124
+ const counts = agentProductivity(database, input);
125
+ for (const row of page.rows) {
126
+ row.tasks_active = row.task_count;
127
+ Object.assign(row, counts.get(String(row.key)) ?? empty());
128
+ row.validation_count = row.test_count;
129
+ }
130
+ if (page.totals) {
131
+ const totals = empty();
132
+ for (const row of counts.values())
133
+ for (const field of fields)
134
+ totals[field] =
135
+ totals[field] === null || row[field] === null ? null : totals[field] + row[field];
136
+ Object.assign(page.totals, totals, {
137
+ validation_count: totals.test_count,
138
+ });
139
+ }
140
+ }