@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.
- package/README.md +58 -3
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +17 -6
- package/dist/commands/init.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/services/adapters.js +4 -4
- package/dist/services/adapters.js.map +1 -1
- package/dist/services/codex-config-template.d.ts +2 -2
- package/dist/services/codex-config-template.d.ts.map +1 -1
- package/dist/services/codex-config-template.js +3 -3
- package/dist/services/codex-config-template.js.map +1 -1
- package/dist/services/codex-rules.d.ts +1 -1
- package/dist/services/codex-rules.d.ts.map +1 -1
- package/dist/services/codex-rules.js +9 -24
- package/dist/services/codex-rules.js.map +1 -1
- package/dist/services/delivery-git-preflight.d.ts +23 -0
- package/dist/services/delivery-git-preflight.d.ts.map +1 -0
- package/dist/services/delivery-git-preflight.js +69 -0
- package/dist/services/delivery-git-preflight.js.map +1 -0
- package/dist/services/doctor.d.ts.map +1 -1
- package/dist/services/doctor.js +13 -1
- package/dist/services/doctor.js.map +1 -1
- package/dist/services/install-agents-template.d.ts.map +1 -1
- package/dist/services/install-agents-template.js +22 -28
- package/dist/services/install-agents-template.js.map +1 -1
- package/dist/services/install-profile.d.ts +8 -0
- package/dist/services/install-profile.d.ts.map +1 -0
- package/dist/services/install-profile.js +56 -0
- package/dist/services/install-profile.js.map +1 -0
- package/dist/services/logger.d.ts.map +1 -1
- package/dist/services/logger.js +6 -5
- package/dist/services/logger.js.map +1 -1
- package/dist/services/stack-git.d.ts +9 -0
- package/dist/services/stack-git.d.ts.map +1 -0
- package/dist/services/stack-git.js +188 -0
- package/dist/services/stack-git.js.map +1 -0
- package/dist/services/stack-plan.d.ts +3 -1
- package/dist/services/stack-plan.d.ts.map +1 -1
- package/dist/services/stack-plan.js +15 -1
- package/dist/services/stack-plan.js.map +1 -1
- package/dist/services/stack-terraform.d.ts +13 -0
- package/dist/services/stack-terraform.d.ts.map +1 -0
- package/dist/services/stack-terraform.js +274 -0
- package/dist/services/stack-terraform.js.map +1 -0
- package/dist/services/template-manifest.d.ts.map +1 -1
- package/dist/services/template-manifest.js +14 -1
- package/dist/services/template-manifest.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/templates/.agents/.env.example +34 -0
- package/templates/{README.md → .agents/README.md} +124 -60
- package/templates/.agents/config/agents/catalog.yaml +23 -23
- package/templates/.agents/config/clients/codex/agents/zaip-agent-analyst.toml +1 -0
- package/templates/.agents/config/clients/codex/agents/zaip-agent-auditor.toml +1 -0
- package/templates/.agents/config/clients/codex/agents/zaip-agent-backend.toml +5 -4
- package/templates/.agents/config/clients/codex/agents/zaip-agent-brand-strategist.toml +1 -0
- package/templates/.agents/config/clients/codex/agents/zaip-agent-builder.toml +1 -0
- package/templates/.agents/config/clients/codex/agents/zaip-agent-cloud-engineer.toml +10 -7
- package/templates/.agents/config/clients/codex/agents/zaip-agent-data-analyst.toml +1 -0
- package/templates/.agents/config/clients/codex/agents/zaip-agent-designer.toml +1 -0
- package/templates/.agents/config/clients/codex/agents/zaip-agent-finalizer.toml +1 -0
- package/templates/.agents/config/clients/codex/agents/zaip-agent-frontend.toml +5 -4
- package/templates/.agents/config/clients/codex/agents/zaip-agent-general-operations.toml +1 -0
- package/templates/.agents/config/clients/codex/agents/zaip-agent-governance-compliance.toml +1 -0
- package/templates/.agents/config/clients/codex/agents/zaip-agent-growth-strategist.toml +1 -0
- package/templates/.agents/config/clients/codex/agents/zaip-agent-integrator.toml +1 -0
- package/templates/.agents/config/clients/codex/agents/zaip-agent-marketing-analyst.toml +5 -4
- package/templates/.agents/config/clients/codex/agents/zaip-agent-orchestrator.toml +1 -0
- package/templates/.agents/config/clients/codex/agents/zaip-agent-refactoring-specialist.toml +1 -0
- package/templates/.agents/config/clients/codex/agents/zaip-agent-reviewer.toml +1 -0
- package/templates/.agents/config/clients/codex/agents/zaip-agent-sales-specialist.toml +1 -0
- package/templates/.agents/config/clients/codex/agents/zaip-agent-security-auditor.toml +1 -0
- package/templates/.agents/config/clients/codex/agents/zaip-agent-task-executor.toml +1 -0
- package/templates/.agents/config/clients/codex/agents/zaip-agent-website-developer.toml +5 -4
- package/templates/.agents/config/clients/codex/agents/zaip-agent-writer.toml +1 -0
- package/templates/.agents/config/pricing/models.json +25 -1
- package/templates/.agents/config/project.yaml +2 -0
- package/templates/.agents/dashboard/.zaip-managed.json +208 -88
- package/templates/.agents/dashboard/DATA-DICTIONARY.md +1 -1
- package/templates/.agents/dashboard/README.md +58 -19
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-DwVZOmMV.js → CostCalculationGuidePage-snUCQYTC.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog-VcLx9iOO.js → DashboardHelpDialog-oZVlf-l5.js} +72 -8
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-CyjMXkm8.js → PricingSettingsDialog-BPtVUGgs.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-3Awyaovx.js +3 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-DFZwIXSC.js → TraceabilityWorkspace-BYjVOHI_.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-DbaLbU2q.js +68 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-DXBOtWNI.js → icons-vendor-TPUT2IUt.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/index-Cmlx8OYH.css +1 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/index-ZJFXmK7m.js +2 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-DaNXQPyk.js → react-vendor-Ct48XHGL.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-I4AKqVDX.js → traceability-BDe4_Ubl.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/index.html +6 -6
- package/templates/.agents/dashboard/apps/frontend/src/App.tsx +73 -67
- package/templates/.agents/dashboard/apps/frontend/src/AppLoadingBoundary.tsx +35 -7
- package/templates/.agents/dashboard/apps/frontend/src/AppWorkBoard.ts +55 -0
- package/templates/.agents/dashboard/apps/frontend/src/ProjectStackEditor.tsx +11 -0
- package/templates/.agents/dashboard/apps/frontend/src/app-loading-boundary.css +98 -0
- package/templates/.agents/dashboard/apps/frontend/src/cost-period-refresh.test.ts +5 -5
- package/templates/.agents/dashboard/apps/frontend/src/dashboard-api-reconciliation.ts +33 -0
- package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.test.ts +75 -0
- package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.ts +7 -8
- package/templates/.agents/dashboard/apps/frontend/src/project-settings-model.ts +14 -2
- package/templates/.agents/dashboard/apps/frontend/src/project-settings.test.ts +35 -0
- package/templates/.agents/dashboard/apps/frontend/src/snapshot-coherence.test.ts +78 -2
- package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.test.ts +28 -0
- package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.ts +8 -5
- package/templates/.agents/dashboard/apps/frontend/src/use-app-effects.ts +180 -1
- package/templates/.agents/dashboard/modules/cost-monitor/src/contracts.ts +7 -4
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorContent.tsx +13 -7
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.tsx +14 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTableDisplay.ts +211 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.test.ts +152 -157
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.tsx +155 -142
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/InvocationManifestQuery.test.ts +145 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/period-cost-groups.ts +7 -5
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-compact-cost-page.ts +4 -1
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-group-page.ts +4 -1
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-run-page.ts +4 -1
- package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-projection.ts +4 -4
- package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.test.ts +79 -1
- package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.tsx +105 -11
- package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +482 -332
- package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +430 -285
- package/templates/.agents/dashboard/packages/local-runtime/scripts/build-sqlite-runtime.mjs +14 -2
- package/templates/.agents/dashboard/packages/local-runtime/scripts/rebuild-sqlite-read-model.mjs +6 -26
- package/templates/.agents/dashboard/packages/local-runtime/scripts/validate-sqlite-read-model.mjs +1 -1
- package/templates/.agents/dashboard/packages/local-runtime/scripts/verify-persistent-launch.mjs +268 -0
- package/templates/.agents/dashboard/packages/local-runtime/server.mjs +482 -332
- package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.test.ts +90 -1
- package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.ts +112 -22
- package/templates/.agents/dashboard/packages/local-runtime/src/server-agent-assets.ts +39 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-browser-lease.ts +33 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-reconciliation-refresh.test.ts +148 -11
- package/templates/.agents/dashboard/packages/local-runtime/src/server-reconciliation-result.test.ts +68 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-refresh.test.ts +19 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-refresh.ts +39 -43
- package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-revision.test.ts +42 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-revision.ts +26 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-synchronization.ts +34 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-startup.ts +30 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-work-reconciliation.test.ts +89 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-work-reconciliation.ts +88 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-workspace.ts +68 -7
- package/templates/.agents/dashboard/packages/local-runtime/src/server.test.ts +11 -5
- package/templates/.agents/dashboard/packages/local-runtime/src/server.ts +129 -99
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-agent-productivity.test.ts +111 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-agent-productivity.ts +140 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-agent-page.test.ts +127 -4
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-agent-page.ts +145 -5
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-aggregates.ts +156 -23
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-group-projection.ts +18 -9
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-projection.ts +52 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-first-fold-persistence.ts +35 -13
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-force-canonical.test.ts +56 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-incremental-cache.test.ts +29 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-incremental-cache.ts +136 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-incremental-refresh.test.ts +251 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-operations-projection.ts +22 -7
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-cache.test-support.ts +1 -1
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-canonical-parsing.ts +42 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-canonical.ts +793 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider-duration.test.ts +270 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider-fixture.ts +3 -2
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider.test.ts +18 -4
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-source-fingerprint.test.ts +87 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-source-fingerprint.ts +105 -16
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.test.ts +131 -59
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.ts +1000 -1115
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-cache.ts +13 -92
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-claims.test.mjs +79 -0
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.mjs +27 -0
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-lifecycle.mjs +6 -6
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-prerequisites.mjs +1 -1
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-refresh.mjs +2 -10
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-runtime-selection.mjs +36 -0
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-runtime-selection.test.mjs +63 -0
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-server.mjs +142 -0
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.mjs +262 -267
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.test.mjs +18 -29
- package/templates/.agents/dashboard/packages/windows-launcher/Program.cs +6 -0
- package/templates/.agents/dashboard/zaip-dashboard-windows.exe +0 -0
- package/templates/.agents/requirements/autonomous-assurance.md +1 -1
- package/templates/.agents/requirements/cloud-hostinger-autonomy.md +47 -0
- package/templates/.agents/requirements/comparative-audit.md +1 -1
- package/templates/.agents/requirements/context-engine.md +1 -1
- package/templates/.agents/requirements/cost-observability.md +1 -1
- package/templates/.agents/requirements/dashboard-launch.md +2 -2
- package/templates/.agents/requirements/data-export.md +1 -1
- package/templates/.agents/requirements/design-system.md +1 -1
- package/templates/.agents/requirements/execution-engine.md +1 -1
- package/templates/.agents/requirements/kanban-dashboard.md +1 -1
- package/templates/.agents/requirements/model-orchestration.md +1 -1
- package/templates/.agents/requirements/public-distribution.md +1 -1
- package/templates/.agents/requirements/software-architecture.md +1 -1
- package/templates/.agents/requirements/strategic-improvements.md +1 -1
- package/templates/.agents/requirements/work-lifecycle.md +1 -1
- package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
- package/templates/.agents/rules/autonomy-decision.md +1 -1
- package/templates/.agents/rules/cloud-hostinger-autonomy.md +21 -0
- package/templates/.agents/rules/core.md +5 -4
- package/templates/.agents/rules/local-sqlite-storage.md +18 -0
- package/templates/.agents/rules/meta-mcp.md +21 -0
- package/templates/.agents/rules/workflow.md +1 -0
- package/templates/.agents/runtime/bin/zaip.mjs +305 -137
- package/templates/.agents/runtime/checksums.sha256 +2 -2
- package/templates/.agents/runtime/provenance.json +2 -2
- package/templates/.agents/skills/zaip-skill-api-to-mcp/SKILL.md +4 -0
- package/templates/.agents/skills/zaip-skill-brand-governance/SKILL.md +25 -9
- package/templates/.agents/skills/zaip-skill-brand-governance/references/brandbook-framework.md +2 -2
- package/templates/.agents/skills/zaip-skill-brand-governance/references/brandbook-standards.md +2 -2
- package/templates/.agents/skills/zaip-skill-change/references/observable-evidence.md +15 -0
- package/templates/.agents/skills/zaip-skill-dashboard-sqlite/SKILL.md +13 -5
- package/templates/.agents/skills/zaip-skill-data-analyst/SKILL.md +10 -5
- package/templates/.agents/skills/zaip-skill-execution/SKILL.md +5 -4
- package/templates/.agents/skills/zaip-skill-frontend-launcher/SKILL.md +4 -3
- package/templates/.agents/skills/zaip-skill-frontend-launcher/references/launcher-contract.md +5 -3
- package/templates/.agents/skills/zaip-skill-github-deploy/SKILL.md +5 -5
- package/templates/.agents/skills/zaip-skill-github-deploy/scripts/preflight.mjs +78 -13
- package/templates/.agents/skills/zaip-skill-github-deploy/scripts/preflight.test.mjs +59 -0
- package/templates/.agents/skills/zaip-skill-github-deploy/scripts/yaml-parser.mjs +157 -0
- package/templates/.agents/skills/zaip-skill-growth-strategy-revops/SKILL.md +11 -6
- package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/SKILL.md +17 -0
- package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/agents/openai.yaml +7 -0
- package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/references/autonomous-cloud-inputs.example.json +41 -0
- package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/references/autonomous-runner.md +84 -0
- package/templates/.agents/skills/zaip-skill-hostinger-cloudflare-terraform/references/operational-contract.md +19 -0
- package/templates/.agents/skills/zaip-skill-local-credentials/SKILL.md +43 -0
- package/templates/.agents/skills/zaip-skill-local-credentials/agents/openai.yaml +6 -0
- package/templates/.agents/skills/zaip-skill-local-credentials/references/dotenv-resolution.md +47 -0
- package/templates/.agents/skills/zaip-skill-marketing/SKILL.md +8 -3
- package/templates/.agents/skills/zaip-skill-meta-ads/SKILL.md +16 -0
- package/templates/.agents/skills/zaip-skill-meta-ads/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-meta-ads/references/setup.md +3 -0
- package/templates/.agents/skills/zaip-skill-meta-ads-setup/SKILL.md +24 -0
- package/templates/.agents/skills/zaip-skill-meta-ads-setup/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-meta-ads-setup/references/diagnostics.md +13 -0
- package/templates/.agents/skills/zaip-skill-meta-ads-setup/references/official-sources.md +10 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/SKILL.md +12 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/references/LICENSE +21 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-api-health/references/upstream.md +77 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/SKILL.md +12 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/references/LICENSE +21 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-api-integration/references/upstream.md +126 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/SKILL.md +12 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/references/LICENSE +21 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-app-health-check/references/upstream.md +67 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/SKILL.md +12 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/references/LICENSE +21 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-app-review-prep/references/upstream.md +69 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/SKILL.md +12 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/references/LICENSE +21 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-compliance-check/references/upstream.md +79 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/SKILL.md +14 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/references/LICENSE +21 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-debug-access-token/references/upstream.md +144 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/SKILL.md +12 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/references/LICENSE +21 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-debug-webhooks/references/upstream.md +83 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/SKILL.md +10 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/references/LICENSE +21 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/references/setup.md +11 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-search-docs/references/upstream.md +55 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/SKILL.md +12 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/references/LICENSE +21 -0
- package/templates/.agents/skills/zaip-skill-meta-devtools-webhook-setup/references/upstream.md +56 -0
- package/templates/.agents/skills/zaip-skill-sales-methodology/SKILL.md +12 -7
- package/templates/.agents/skills/zaip-skill-sqlite-read-model/SKILL.md +11 -4
- package/templates/.agents/skills/zaip-skill-tool-setup/SKILL.md +6 -4
- package/templates/.agents/skills/zaip-skill-tool-setup/references/codex-config.md +4 -2
- package/templates/.agents/skills/zaip-skill-tool-setup/references/secrets.md +1 -0
- package/templates/.agents/tools/catalog.yaml +6 -0
- package/templates/.agents/tools/cloud-runner-dns.mjs +28 -0
- package/templates/.agents/tools/cloud-runner-terraform.mjs +212 -0
- package/templates/.agents/tools/cloud-runner.integration.test.mjs +239 -0
- package/templates/.agents/tools/cloud-runner.mjs +786 -0
- package/templates/.agents/tools/cloud-runner.test.mjs +534 -0
- package/templates/.agents/tools/cloud-terraform-plan-policy.mjs +262 -0
- package/templates/.agents/tools/mcp/development/meta-devtools/README.md +14 -0
- package/templates/.agents/tools/mcp/development/meta-devtools/manifest.yaml +28 -0
- package/templates/.agents/tools/mcp/infra/cloudflare/manifest.yaml +2 -3
- package/templates/.agents/tools/mcp/infra/hostinger/manifest.yaml +1 -3
- package/templates/.agents/tools/mcp/marketing/meta-ads/README.md +15 -13
- package/templates/.agents/tools/mcp/marketing/meta-ads/manifest.yaml +15 -10
- package/templates/.agents/tools/profiles/development.yaml +3 -0
- package/templates/infra/terraform/.terraform.lock.hcl +42 -0
- package/templates/infra/terraform/README.md +31 -0
- package/templates/infra/terraform/backend.tf.json +13 -0
- package/templates/infra/terraform/deployment-inputs.example.yaml +22 -0
- package/templates/infra/terraform/fixtures/approved-targets.json +32 -0
- package/templates/infra/terraform/fixtures/plan-approved.json +67 -0
- package/templates/infra/terraform/main.tf +77 -0
- package/templates/infra/terraform/outputs.tf +20 -0
- package/templates/infra/terraform/terraform.tfvars.example +26 -0
- package/templates/infra/terraform/variables.tf +91 -0
- package/templates/infra/terraform/versions.tf +18 -0
- package/templates/scripts/validate-cloud-hostinger-contract.mjs +89 -0
- package/templates/scripts/validate-cloud-terraform-plan.mjs +1 -0
- package/templates/template-manifest.json +607 -167
- package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-BwnJpl02.js +0 -3
- package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-C09KmlSw.js +0 -68
- package/templates/.agents/dashboard/apps/frontend/dist/assets/index-Dyo_wpL4.js +0 -2
- package/templates/.agents/dashboard/apps/frontend/dist/assets/index-hPPfVGQv.css +0 -1
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-stale-seed.ts +0 -50
package/templates/.agents/dashboard/packages/local-runtime/src/server-reconciliation-result.test.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { WorkspaceFileSystem } from "@zaip-ai/core";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { reconcileOpenWork, reconciliationResult } from "./server-workspace.js";
|
|
4
|
+
|
|
5
|
+
describe("manual reconciliation report", () => {
|
|
6
|
+
it("reports delivered SPECs and PRDs even when no Task or Run changed", () => {
|
|
7
|
+
expect(
|
|
8
|
+
reconciliationResult({
|
|
9
|
+
reconciliations: [
|
|
10
|
+
{
|
|
11
|
+
status: "closed",
|
|
12
|
+
specs: [{ id: "SPEC-DONE", status: "delivered" }],
|
|
13
|
+
prds: [{ id: "PRD-DONE", status: "delivered" }],
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
}),
|
|
17
|
+
).toEqual({ reconciled_ids: ["PRD-DONE", "SPEC-DONE"], blocked: [], not_reconciled: [] });
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("reports the canonical owners of an orphan with their persisted blocking reasons", () => {
|
|
21
|
+
expect(
|
|
22
|
+
reconciliationResult({
|
|
23
|
+
orphaned_executions: ["EXEC-ORPHAN"],
|
|
24
|
+
orphaned_execution_entities: [
|
|
25
|
+
{
|
|
26
|
+
id: "EXEC-ORPHAN",
|
|
27
|
+
status: "blocked",
|
|
28
|
+
status_reason: "Host terminated without delivery evidence.",
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
orphaned_owner_entities: [
|
|
32
|
+
{ id: "TASK-ORPHAN", status: "blocked", status_reason: "Host interrupted." },
|
|
33
|
+
{ id: "SPEC-ORPHAN", status: "blocked", status_reason: "Child attempt stopped." },
|
|
34
|
+
{ id: "PRD-ORPHAN", status: "blocked", status_reason: "Child attempt stopped." },
|
|
35
|
+
],
|
|
36
|
+
unresolved_running_run_ids: ["RUN-LIVE"],
|
|
37
|
+
}),
|
|
38
|
+
).toEqual({
|
|
39
|
+
reconciled_ids: ["EXEC-ORPHAN", "PRD-ORPHAN", "SPEC-ORPHAN", "TASK-ORPHAN"],
|
|
40
|
+
blocked: [
|
|
41
|
+
{ id: "EXEC-ORPHAN", reason: "Host terminated without delivery evidence." },
|
|
42
|
+
{ id: "PRD-ORPHAN", reason: "Child attempt stopped." },
|
|
43
|
+
{ id: "SPEC-ORPHAN", reason: "Child attempt stopped." },
|
|
44
|
+
{ id: "TASK-ORPHAN", reason: "Host interrupted." },
|
|
45
|
+
],
|
|
46
|
+
not_reconciled: [{ id: "RUN-LIVE", reason: "Invocation is still running." }],
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("does not advertise untouched draft, ready or active entities as reconciled", () => {
|
|
51
|
+
expect(
|
|
52
|
+
reconciliationResult({
|
|
53
|
+
orphaned_owner_entities: [
|
|
54
|
+
{ id: "PRD-DRAFT", status: "draft" },
|
|
55
|
+
{ id: "SPEC-READY", status: "ready" },
|
|
56
|
+
{ id: "TASK-LIVE", status: "executing" },
|
|
57
|
+
],
|
|
58
|
+
}),
|
|
59
|
+
).toEqual({ reconciled_ids: [], blocked: [], not_reconciled: [] });
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("reports unavailable reconciliation instead of returning a successful empty result", async () => {
|
|
63
|
+
const filesystem = { exists: vi.fn(async () => false) } as unknown as WorkspaceFileSystem;
|
|
64
|
+
await expect(reconcileOpenWork(filesystem, "test-workspace")).rejects.toThrow(
|
|
65
|
+
"could not be reconciled",
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
});
|
package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-refresh.test.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { attachRefreshWorkerCleanup } from "./server-snapshot-refresh.js";
|
|
4
|
+
|
|
5
|
+
describe("snapshot refresh shutdown", () => {
|
|
6
|
+
it("terminates an active refresh worker on runtime shutdown and removes its listener on completion", () => {
|
|
7
|
+
const processRef = new EventEmitter();
|
|
8
|
+
const calls: string[] = [];
|
|
9
|
+
const cleanup = attachRefreshWorkerCleanup(
|
|
10
|
+
{ kill: (signal) => (calls.push(String(signal)), true) },
|
|
11
|
+
processRef,
|
|
12
|
+
);
|
|
13
|
+
processRef.emit("exit");
|
|
14
|
+
expect(calls).toEqual(["SIGTERM"]);
|
|
15
|
+
cleanup();
|
|
16
|
+
processRef.emit("exit");
|
|
17
|
+
expect(calls).toEqual(["SIGTERM"]);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -1,25 +1,13 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
-
import { randomBytes } from "node:crypto";
|
|
3
2
|
import { existsSync } from "node:fs";
|
|
4
|
-
import {
|
|
5
|
-
import { dirname, resolve } from "node:path";
|
|
3
|
+
import { resolve } from "node:path";
|
|
6
4
|
import {
|
|
7
5
|
type SqliteReadModelFileSystem,
|
|
8
6
|
sqliteReadModelPath,
|
|
9
7
|
validateSqliteReadModelSchema,
|
|
10
8
|
} from "./sqlite-read-model.js";
|
|
11
|
-
import { activateStagedSqliteSnapshot, stageSqliteSnapshot } from "./sqlite-snapshot-install.js";
|
|
12
9
|
|
|
13
|
-
|
|
14
|
-
export async function rebuildSnapshotInWorker(workspaceRoot: string, currentPath: string) {
|
|
15
|
-
const target = resolve(
|
|
16
|
-
dirname(currentPath),
|
|
17
|
-
`refresh-server-${process.pid}-${randomBytes(4).toString("hex")}.sqlite`,
|
|
18
|
-
);
|
|
19
|
-
// A temporary/canonical workspace contains data, not necessarily a second
|
|
20
|
-
// dashboard runtime checkout. The explicit candidates work for a launcher
|
|
21
|
-
// opened at the workspace root and for fixture tests launched from the
|
|
22
|
-
// runtime's source checkout, without relying on import.meta in the CJS build.
|
|
10
|
+
async function rebuildPersistentDatabaseInWorker(workspaceRoot: string, destination: string) {
|
|
23
11
|
const scriptCandidates = [
|
|
24
12
|
resolve(
|
|
25
13
|
workspaceRoot,
|
|
@@ -40,58 +28,66 @@ export async function rebuildSnapshotInWorker(workspaceRoot: string, currentPath
|
|
|
40
28
|
cwd: workspaceRoot,
|
|
41
29
|
env: {
|
|
42
30
|
...process.env,
|
|
43
|
-
// The script prioritizes ZAIP_WORKSPACE over cwd. Do not inherit a
|
|
44
|
-
// launcher's root when this refresh was requested for another
|
|
45
|
-
// canonical workspace (notably an isolated test fixture).
|
|
46
31
|
ZAIP_WORKSPACE: workspaceRoot,
|
|
47
|
-
ZAIP_SQLITE_DB_PATH:
|
|
32
|
+
ZAIP_SQLITE_DB_PATH: destination,
|
|
48
33
|
ZAIP_SQLITE_PREFER_LAST_VALID_SEED: "0",
|
|
49
34
|
ZAIP_DASHBOARD_LAUNCH_PROGRESS: "0",
|
|
50
35
|
},
|
|
51
36
|
stdio: ["ignore", "ignore", "pipe"],
|
|
52
37
|
windowsHide: true,
|
|
53
38
|
});
|
|
39
|
+
// Lease shutdown may end the serving parent while a slow snapshot rebuild
|
|
40
|
+
// is still writing. Terminate the direct child synchronously during parent
|
|
41
|
+
// exit so the next launcher cannot overlap the persistent SQLite writer.
|
|
42
|
+
const cleanupWorker = attachRefreshWorkerCleanup(worker);
|
|
54
43
|
worker.stderr?.setEncoding("utf8");
|
|
55
44
|
worker.stderr?.on("data", (chunk: string) => {
|
|
56
|
-
stderr
|
|
45
|
+
stderr = `${stderr}${chunk}`.slice(-8_000);
|
|
57
46
|
});
|
|
58
|
-
worker.once("error",
|
|
59
|
-
|
|
60
|
-
|
|
47
|
+
worker.once("error", (error) => {
|
|
48
|
+
cleanupWorker();
|
|
49
|
+
rejectWorker(error);
|
|
50
|
+
});
|
|
51
|
+
worker.once("exit", (code) => {
|
|
52
|
+
cleanupWorker();
|
|
53
|
+
return code === 0
|
|
61
54
|
? resolveWorker()
|
|
62
55
|
: rejectWorker(
|
|
63
56
|
new Error(`SQLite refresh worker failed (${code}): ${stderr.trim() || "no stderr"}`),
|
|
64
|
-
)
|
|
65
|
-
);
|
|
57
|
+
);
|
|
58
|
+
});
|
|
66
59
|
});
|
|
67
|
-
return target;
|
|
68
60
|
}
|
|
69
61
|
|
|
70
|
-
export
|
|
71
|
-
|
|
62
|
+
export function attachRefreshWorkerCleanup(
|
|
63
|
+
worker: { kill(signal?: NodeJS.Signals): boolean },
|
|
64
|
+
processRef: {
|
|
65
|
+
once(event: string, listener: () => void): unknown;
|
|
66
|
+
removeListener(event: string, listener: () => void): unknown;
|
|
67
|
+
} = process,
|
|
68
|
+
): () => void {
|
|
69
|
+
const stopWorkerOnParentExit = () => {
|
|
70
|
+
try {
|
|
71
|
+
worker.kill("SIGTERM");
|
|
72
|
+
} catch {
|
|
73
|
+
/* already terminal */
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
processRef.once("exit", stopWorkerOnParentExit);
|
|
77
|
+
return () => processRef.removeListener("exit", stopWorkerOnParentExit);
|
|
72
78
|
}
|
|
73
79
|
|
|
74
80
|
/**
|
|
75
|
-
*
|
|
76
|
-
* the
|
|
77
|
-
*
|
|
81
|
+
* The child process writes the single persistent database in place. WAL keeps
|
|
82
|
+
* the server reader usable until the worker commits; no staged SQLite file is
|
|
83
|
+
* created. On an error the worker rolls back and this reader stays untouched.
|
|
78
84
|
*/
|
|
79
85
|
export async function refreshSqliteSnapshotInWorker(
|
|
80
86
|
workspaceRoot: string,
|
|
81
87
|
filesystem: SqliteReadModelFileSystem,
|
|
82
88
|
) {
|
|
83
89
|
const destination = sqliteReadModelPath(workspaceRoot);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
refreshedPath,
|
|
88
|
-
destination,
|
|
89
|
-
validateSqliteReadModelSchema,
|
|
90
|
-
);
|
|
91
|
-
await filesystem.rebuildAndRefresh(async () => {
|
|
92
|
-
activateStagedSqliteSnapshot(stagedPath, destination);
|
|
93
|
-
});
|
|
94
|
-
} finally {
|
|
95
|
-
await removeRefreshSnapshot(refreshedPath);
|
|
96
|
-
}
|
|
90
|
+
await rebuildPersistentDatabaseInWorker(workspaceRoot, destination);
|
|
91
|
+
validateSqliteReadModelSchema(destination);
|
|
92
|
+
filesystem.refresh();
|
|
97
93
|
}
|
package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-revision.test.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { mkdir, mkdtemp, rm } from "node:fs/promises";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { dirname, join, resolve, sep } from "node:path";
|
|
4
|
+
import { DatabaseSync } from "node:sqlite";
|
|
5
|
+
import { expect, it } from "vitest";
|
|
6
|
+
import { readCommittedSnapshotRevision } from "./server-snapshot-revision.js";
|
|
7
|
+
import { sqliteReadModelPath } from "./sqlite-read-model.js";
|
|
8
|
+
|
|
9
|
+
it("publishes only committed metadata, preserving the revision through a rolled-back write", async () => {
|
|
10
|
+
const base = resolve(tmpdir());
|
|
11
|
+
const root = await mkdtemp(join(base, "zaip-revision-"));
|
|
12
|
+
if (!resolve(root).startsWith(base + sep))
|
|
13
|
+
throw new Error("Fixture cleanup outside temporary directory");
|
|
14
|
+
const databasePath = sqliteReadModelPath(root);
|
|
15
|
+
await mkdir(dirname(databasePath), { recursive: true });
|
|
16
|
+
const writer = new DatabaseSync(databasePath);
|
|
17
|
+
try {
|
|
18
|
+
writer.exec(`
|
|
19
|
+
PRAGMA journal_mode=WAL;
|
|
20
|
+
CREATE TABLE read_model_metadata (key TEXT PRIMARY KEY, value TEXT);
|
|
21
|
+
CREATE VIEW zaip_vw_read_model_metadata AS SELECT * FROM read_model_metadata;
|
|
22
|
+
INSERT INTO read_model_metadata VALUES ('schema_version', '1'), ('source_fingerprint', 'first');
|
|
23
|
+
`);
|
|
24
|
+
const original = readCommittedSnapshotRevision(root);
|
|
25
|
+
expect(original).toMatch(/^[a-f0-9]{64}$/);
|
|
26
|
+
writer.exec(
|
|
27
|
+
"BEGIN IMMEDIATE; UPDATE read_model_metadata SET value='second' WHERE key='source_fingerprint';",
|
|
28
|
+
);
|
|
29
|
+
expect(readCommittedSnapshotRevision(root)).toBe(original);
|
|
30
|
+
writer.exec("ROLLBACK;");
|
|
31
|
+
expect(readCommittedSnapshotRevision(root)).toBe(original);
|
|
32
|
+
writer.exec(
|
|
33
|
+
"BEGIN IMMEDIATE; UPDATE read_model_metadata SET value='second' WHERE key='source_fingerprint'; COMMIT;",
|
|
34
|
+
);
|
|
35
|
+
const committed = readCommittedSnapshotRevision(root);
|
|
36
|
+
expect(committed).not.toBe(original);
|
|
37
|
+
expect(readCommittedSnapshotRevision(root)).toBe(committed);
|
|
38
|
+
} finally {
|
|
39
|
+
writer.close();
|
|
40
|
+
await rm(root, { recursive: true, force: true });
|
|
41
|
+
}
|
|
42
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { DatabaseSync } from "node:sqlite";
|
|
3
|
+
import { sqliteReadModelPath } from "./sqlite-read-model.js";
|
|
4
|
+
|
|
5
|
+
/** Read committed inputs only. Canonical edits and rolled-back writes cannot advance the revision. */
|
|
6
|
+
export function readCommittedSnapshotRevision(workspaceRoot: string): string {
|
|
7
|
+
const database = new DatabaseSync(sqliteReadModelPath(workspaceRoot), { readOnly: true });
|
|
8
|
+
try {
|
|
9
|
+
const rows = database
|
|
10
|
+
.prepare(
|
|
11
|
+
"SELECT key, value FROM zaip_vw_read_model_metadata WHERE key IN ('schema_version', 'source_fingerprint')",
|
|
12
|
+
)
|
|
13
|
+
.all() as Array<{ key: string; value: string }>;
|
|
14
|
+
const metadata = new Map(rows.map((row) => [row.key, row.value]));
|
|
15
|
+
if (!metadata.has("schema_version") || !metadata.has("source_fingerprint")) {
|
|
16
|
+
throw new Error("Committed SQLite snapshot metadata is incomplete");
|
|
17
|
+
}
|
|
18
|
+
return createHash("sha256")
|
|
19
|
+
.update(metadata.get("schema_version") as string)
|
|
20
|
+
.update(":")
|
|
21
|
+
.update(metadata.get("source_fingerprint") as string)
|
|
22
|
+
.digest("hex");
|
|
23
|
+
} finally {
|
|
24
|
+
database.close();
|
|
25
|
+
}
|
|
26
|
+
}
|
package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-synchronization.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { watch } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
export function startSnapshotSynchronization(input: {
|
|
5
|
+
workspaceRoot: string;
|
|
6
|
+
staleSeed: boolean;
|
|
7
|
+
watchSources: boolean;
|
|
8
|
+
refresh: () => Promise<void>;
|
|
9
|
+
}): void {
|
|
10
|
+
if (input.staleSeed) void input.refresh().catch(() => undefined);
|
|
11
|
+
if (!input.watchSources || process.env.ZAIP_DASHBOARD_WATCH_SOURCES === "0") return;
|
|
12
|
+
let pendingRefresh: NodeJS.Timeout | undefined;
|
|
13
|
+
const scheduleRefresh = () => {
|
|
14
|
+
if (pendingRefresh !== undefined) clearTimeout(pendingRefresh);
|
|
15
|
+
pendingRefresh = setTimeout(() => {
|
|
16
|
+
pendingRefresh = undefined;
|
|
17
|
+
void input.refresh().catch(() => undefined);
|
|
18
|
+
}, 300);
|
|
19
|
+
pendingRefresh.unref();
|
|
20
|
+
};
|
|
21
|
+
for (const sourceRoot of [
|
|
22
|
+
join(input.workspaceRoot, ".agents", "records"),
|
|
23
|
+
join(input.workspaceRoot, ".agents", "config"),
|
|
24
|
+
]) {
|
|
25
|
+
try {
|
|
26
|
+
const watcher = watch(sourceRoot, { recursive: true }, scheduleRefresh);
|
|
27
|
+
watcher.unref();
|
|
28
|
+
} catch {
|
|
29
|
+
// Reuse-triggered refresh remains the portable fallback.
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const freshnessTimer = setInterval(scheduleRefresh, 60_000);
|
|
33
|
+
freshnessTimer.unref();
|
|
34
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { rebuildSqliteReadModel } from "./sqlite-read-model.js";
|
|
2
|
+
|
|
3
|
+
export function writeStartupPhase(startedAt: number, phase: string): void {
|
|
4
|
+
process.stderr.write(
|
|
5
|
+
`${JSON.stringify({
|
|
6
|
+
event: "dashboard_sqlite_startup_phase",
|
|
7
|
+
phase,
|
|
8
|
+
elapsed_ms: Math.round(performance.now() - startedAt),
|
|
9
|
+
})}\n`,
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function writeReadModelStartup(
|
|
14
|
+
startedAt: number,
|
|
15
|
+
readModel: Awaited<ReturnType<typeof rebuildSqliteReadModel>>,
|
|
16
|
+
): void {
|
|
17
|
+
// The parent launcher captures stderr even when stdout is intentionally
|
|
18
|
+
// hidden. This makes seed hit/miss and private-copy cost observable without
|
|
19
|
+
// exposing a shared handle or workspace payload.
|
|
20
|
+
process.stderr.write(
|
|
21
|
+
`${JSON.stringify({
|
|
22
|
+
event: "dashboard_sqlite_read_model",
|
|
23
|
+
elapsed_ms: Math.round(performance.now() - startedAt),
|
|
24
|
+
cache: readModel.cache,
|
|
25
|
+
fingerprint_ms: readModel.fingerprint_ms,
|
|
26
|
+
materialize_ms: readModel.materialize_ms,
|
|
27
|
+
source_files: readModel.files,
|
|
28
|
+
})}\n`,
|
|
29
|
+
);
|
|
30
|
+
}
|
package/templates/.agents/dashboard/packages/local-runtime/src/server-work-reconciliation.test.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
createWorkReconciliation,
|
|
4
|
+
startHostTerminalReconciliation,
|
|
5
|
+
waitForWorkReconciliation,
|
|
6
|
+
} from "./server-work-reconciliation.js";
|
|
7
|
+
import { type ReconciliationResult, reconciliationCommandTimeoutMs } from "./server-workspace.js";
|
|
8
|
+
|
|
9
|
+
const clean: ReconciliationResult = { reconciled_ids: [], blocked: [], not_reconciled: [] };
|
|
10
|
+
const changed: ReconciliationResult = {
|
|
11
|
+
...clean,
|
|
12
|
+
reconciled_ids: ["TASK-TERMINAL", "SPEC-DELIVERED", "PRD-DELIVERED"],
|
|
13
|
+
};
|
|
14
|
+
afterEach(() => vi.useRealTimers());
|
|
15
|
+
|
|
16
|
+
describe("canonical host recovery", () => {
|
|
17
|
+
it("serializes manual recovery after host-only work and deduplicates concurrent clicks", async () => {
|
|
18
|
+
let release!: (value: ReconciliationResult) => void;
|
|
19
|
+
const first = new Promise<ReconciliationResult>((resolve) => {
|
|
20
|
+
release = resolve;
|
|
21
|
+
});
|
|
22
|
+
const reconcile = vi.fn().mockReturnValueOnce(first).mockResolvedValue(changed);
|
|
23
|
+
const refresh = vi.fn().mockResolvedValue(undefined);
|
|
24
|
+
const run = createWorkReconciliation({ reconcile, refresh });
|
|
25
|
+
const automatic = run("host-terminal");
|
|
26
|
+
await Promise.resolve();
|
|
27
|
+
const manual = run("manual");
|
|
28
|
+
expect(run("manual")).toBe(manual);
|
|
29
|
+
expect(run("host-terminal")).toBe(manual);
|
|
30
|
+
expect(reconcile.mock.calls).toEqual([["host-terminal"]]);
|
|
31
|
+
release(clean);
|
|
32
|
+
await automatic;
|
|
33
|
+
await expect(manual).resolves.toEqual(changed);
|
|
34
|
+
expect(reconcile.mock.calls).toEqual([["host-terminal"], ["manual"]]);
|
|
35
|
+
expect(refresh).toHaveBeenCalledOnce();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("promotes partial changes after a failure and allows the next attempt", async () => {
|
|
39
|
+
const reconcile = vi
|
|
40
|
+
.fn()
|
|
41
|
+
.mockRejectedValueOnce(new Error("writer interrupted"))
|
|
42
|
+
.mockResolvedValue(clean);
|
|
43
|
+
const refresh = vi.fn().mockResolvedValue(undefined);
|
|
44
|
+
const run = createWorkReconciliation({ reconcile, refresh });
|
|
45
|
+
await expect(run("manual")).rejects.toThrow("writer interrupted");
|
|
46
|
+
await expect(run("manual")).resolves.toEqual(clean);
|
|
47
|
+
expect(refresh).toHaveBeenCalledOnce();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("recovers at startup and retries after failure without overlapping work", async () => {
|
|
51
|
+
vi.useFakeTimers();
|
|
52
|
+
let release!: () => void;
|
|
53
|
+
const reconcile = vi
|
|
54
|
+
.fn()
|
|
55
|
+
.mockImplementationOnce(
|
|
56
|
+
() =>
|
|
57
|
+
new Promise<void>((resolve) => {
|
|
58
|
+
release = resolve;
|
|
59
|
+
}),
|
|
60
|
+
)
|
|
61
|
+
.mockRejectedValueOnce(new Error("busy"))
|
|
62
|
+
.mockResolvedValue(undefined);
|
|
63
|
+
const stop = startHostTerminalReconciliation(reconcile);
|
|
64
|
+
await vi.advanceTimersByTimeAsync(1_000);
|
|
65
|
+
await vi.advanceTimersByTimeAsync(90_000);
|
|
66
|
+
expect(reconcile).toHaveBeenCalledOnce();
|
|
67
|
+
release();
|
|
68
|
+
await vi.advanceTimersByTimeAsync(30_000);
|
|
69
|
+
expect(reconcile).toHaveBeenCalledTimes(2);
|
|
70
|
+
await vi.advanceTimersByTimeAsync(30_000);
|
|
71
|
+
expect(reconcile).toHaveBeenCalledTimes(3);
|
|
72
|
+
stop();
|
|
73
|
+
await vi.advanceTimersByTimeAsync(60_000);
|
|
74
|
+
expect(reconcile).toHaveBeenCalledTimes(3);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("bounds total request time even while the canonical writer is queued", async () => {
|
|
78
|
+
vi.useFakeTimers();
|
|
79
|
+
let release!: (value: ReconciliationResult) => void;
|
|
80
|
+
const pending = new Promise<ReconciliationResult>((resolve) => {
|
|
81
|
+
release = resolve;
|
|
82
|
+
});
|
|
83
|
+
const bounded = expect(waitForWorkReconciliation(pending)).rejects.toThrow("exceeded");
|
|
84
|
+
await vi.advanceTimersByTimeAsync(reconciliationCommandTimeoutMs);
|
|
85
|
+
await bounded;
|
|
86
|
+
release(changed);
|
|
87
|
+
await expect(pending).resolves.toEqual(changed);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ReconciliationCommandTimeoutError,
|
|
3
|
+
type ReconciliationResult,
|
|
4
|
+
reconciliationCommandTimeoutMs,
|
|
5
|
+
} from "./server-workspace.js";
|
|
6
|
+
|
|
7
|
+
export function reconciliationNeedsRefresh(result: ReconciliationResult): boolean {
|
|
8
|
+
return result.reconciled_ids.length > 0 || result.blocked.length > 0;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type ReconciliationMode = "manual" | "host-terminal";
|
|
12
|
+
|
|
13
|
+
/** Include queue time in the HTTP deadline; the serialized job remains retryable. */
|
|
14
|
+
export async function waitForWorkReconciliation(
|
|
15
|
+
work: Promise<ReconciliationResult>,
|
|
16
|
+
onDeadline: () => void = () => undefined,
|
|
17
|
+
): Promise<ReconciliationResult> {
|
|
18
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
19
|
+
try {
|
|
20
|
+
return await Promise.race([
|
|
21
|
+
work,
|
|
22
|
+
new Promise<never>((_resolve, reject) => {
|
|
23
|
+
timer = setTimeout(() => {
|
|
24
|
+
onDeadline();
|
|
25
|
+
reject(new ReconciliationCommandTimeoutError());
|
|
26
|
+
}, reconciliationCommandTimeoutMs);
|
|
27
|
+
timer.unref();
|
|
28
|
+
}),
|
|
29
|
+
]);
|
|
30
|
+
} finally {
|
|
31
|
+
if (timer !== undefined) clearTimeout(timer);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Serialize canonical mutations from the button and the host recovery timer. */
|
|
36
|
+
export function createWorkReconciliation(input: {
|
|
37
|
+
reconcile: (mode: ReconciliationMode) => Promise<ReconciliationResult>;
|
|
38
|
+
refresh: () => Promise<void>;
|
|
39
|
+
}) {
|
|
40
|
+
let tail = Promise.resolve();
|
|
41
|
+
const pending = new Map<ReconciliationMode, Promise<ReconciliationResult>>();
|
|
42
|
+
const refresh = () => void input.refresh().catch(() => undefined);
|
|
43
|
+
return (mode: ReconciliationMode): Promise<ReconciliationResult> => {
|
|
44
|
+
const current = pending.get("manual") ?? pending.get(mode);
|
|
45
|
+
if (current !== undefined) return current;
|
|
46
|
+
const job = tail
|
|
47
|
+
.then(() => input.reconcile(mode))
|
|
48
|
+
.then(
|
|
49
|
+
(result) => {
|
|
50
|
+
if (result.reconciled_ids.length > 0 || result.blocked.length > 0) refresh();
|
|
51
|
+
return result;
|
|
52
|
+
},
|
|
53
|
+
(error: unknown) => {
|
|
54
|
+
// A failed command may already have persisted part of the graph.
|
|
55
|
+
refresh();
|
|
56
|
+
throw error;
|
|
57
|
+
},
|
|
58
|
+
)
|
|
59
|
+
.finally(() => pending.delete(mode));
|
|
60
|
+
pending.set(mode, job);
|
|
61
|
+
tail = job.then(
|
|
62
|
+
() => undefined,
|
|
63
|
+
() => undefined,
|
|
64
|
+
);
|
|
65
|
+
return job;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Recover missed host-stop callbacks without relying on an open browser tab. */
|
|
70
|
+
export function startHostTerminalReconciliation(reconcile: () => Promise<unknown>): () => void {
|
|
71
|
+
let stopped = false;
|
|
72
|
+
let timer: ReturnType<typeof setTimeout>;
|
|
73
|
+
const schedule = (delay: number) => {
|
|
74
|
+
timer = setTimeout(() => {
|
|
75
|
+
void reconcile()
|
|
76
|
+
.catch(() => undefined)
|
|
77
|
+
.finally(() => {
|
|
78
|
+
if (!stopped) schedule(30_000);
|
|
79
|
+
});
|
|
80
|
+
}, delay);
|
|
81
|
+
timer.unref();
|
|
82
|
+
};
|
|
83
|
+
schedule(1_000);
|
|
84
|
+
return () => {
|
|
85
|
+
stopped = true;
|
|
86
|
+
clearTimeout(timer);
|
|
87
|
+
};
|
|
88
|
+
}
|
|
@@ -68,19 +68,52 @@ export interface ReconciliationResult {
|
|
|
68
68
|
not_reconciled: ReconciliationItem[];
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
export const reconciliationCommandTimeoutMs = 170_000;
|
|
72
|
+
|
|
73
|
+
export class ReconciliationCommandTimeoutError extends Error {
|
|
74
|
+
constructor() {
|
|
75
|
+
super(
|
|
76
|
+
`Open-work reconciliation exceeded ${reconciliationCommandTimeoutMs}ms. Canonical records may contain partial changes; the dashboard projection is being refreshed.`,
|
|
77
|
+
);
|
|
78
|
+
this.name = "ReconciliationCommandTimeoutError";
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
71
82
|
export async function reconcileOpenWork(
|
|
72
83
|
filesystem: WorkspaceFileSystem,
|
|
73
84
|
workspaceRoot: string,
|
|
85
|
+
options: { hostTerminalOnly?: boolean } = {},
|
|
74
86
|
): Promise<ReconciliationResult> {
|
|
75
87
|
if (!(await filesystem.exists(".agents/runtime/bin/zaip.mjs"))) {
|
|
76
|
-
|
|
88
|
+
throw new Error("ZAIP runtime is unavailable; open work could not be reconciled.");
|
|
77
89
|
}
|
|
78
90
|
const cliPath = resolve(workspaceRoot, ".agents", "runtime", "bin", "zaip.mjs");
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
91
|
+
let stdout: string;
|
|
92
|
+
try {
|
|
93
|
+
({ stdout } = await execFileAsync(
|
|
94
|
+
process.execPath,
|
|
95
|
+
[
|
|
96
|
+
cliPath,
|
|
97
|
+
"run",
|
|
98
|
+
"reconcile",
|
|
99
|
+
"--open",
|
|
100
|
+
"--json",
|
|
101
|
+
...(options.hostTerminalOnly ? ["--host-terminal-only"] : []),
|
|
102
|
+
],
|
|
103
|
+
{
|
|
104
|
+
cwd: workspaceRoot,
|
|
105
|
+
windowsHide: true,
|
|
106
|
+
maxBuffer: 10 * 1_024 * 1_024,
|
|
107
|
+
timeout: reconciliationCommandTimeoutMs,
|
|
108
|
+
killSignal: "SIGTERM",
|
|
109
|
+
},
|
|
110
|
+
));
|
|
111
|
+
} catch (error) {
|
|
112
|
+
if (typeof error === "object" && error !== null && "killed" in error && error.killed === true) {
|
|
113
|
+
throw new ReconciliationCommandTimeoutError();
|
|
114
|
+
}
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
84
117
|
return reconciliationResult(JSON.parse(stdout) as unknown);
|
|
85
118
|
}
|
|
86
119
|
|
|
@@ -94,6 +127,7 @@ export function reconciliationResult(value: unknown): ReconciliationResult {
|
|
|
94
127
|
};
|
|
95
128
|
const records =
|
|
96
129
|
typeof value === "object" && value !== null ? (value as Record<string, unknown>) : {};
|
|
130
|
+
const terminalStatuses = new Set(["completed", "delivered", "blocked", "cancelled", "failed"]);
|
|
97
131
|
const reconciliations = Array.isArray(records.reconciliations) ? records.reconciliations : [];
|
|
98
132
|
for (const candidate of reconciliations) {
|
|
99
133
|
if (typeof candidate !== "object" || candidate === null) continue;
|
|
@@ -107,7 +141,7 @@ export function reconciliationResult(value: unknown): ReconciliationResult {
|
|
|
107
141
|
const record = entity as Record<string, unknown>;
|
|
108
142
|
if (
|
|
109
143
|
item.status === "closed" &&
|
|
110
|
-
|
|
144
|
+
terminalStatuses.has(record.status as string) &&
|
|
111
145
|
typeof record.id === "string"
|
|
112
146
|
) {
|
|
113
147
|
reconciled.add(record.id);
|
|
@@ -119,12 +153,39 @@ export function reconciliationResult(value: unknown): ReconciliationResult {
|
|
|
119
153
|
}
|
|
120
154
|
}
|
|
121
155
|
}
|
|
156
|
+
// A terminal conversation/root can change canonical telemetry without a
|
|
157
|
+
// Task graph. It still requires a snapshot promotion so Cost Monitor does
|
|
158
|
+
// not keep serving the generation that preceded its Run closure.
|
|
159
|
+
const closedRuns = Array.isArray(records.runs) ? records.runs : [];
|
|
160
|
+
closedRuns.forEach((candidate) => {
|
|
161
|
+
if (typeof candidate !== "object" || candidate === null) return;
|
|
162
|
+
const runId = (candidate as { run?: { run_id?: unknown } }).run?.run_id;
|
|
163
|
+
if (typeof runId === "string") reconciled.add(runId);
|
|
164
|
+
});
|
|
122
165
|
const orphanedExecutions = Array.isArray(records.orphaned_executions)
|
|
123
166
|
? records.orphaned_executions
|
|
124
167
|
: [];
|
|
125
168
|
orphanedExecutions.forEach((id) => {
|
|
126
169
|
remember(blocked, id, "Execution orphaned after the reconciliation grace period.");
|
|
127
170
|
});
|
|
171
|
+
const orphanedInteractionExecutions = Array.isArray(records.orphaned_interaction_executions)
|
|
172
|
+
? records.orphaned_interaction_executions
|
|
173
|
+
: [];
|
|
174
|
+
orphanedInteractionExecutions.forEach((id) => {
|
|
175
|
+
remember(blocked, id, "Interaction Execution orphaned after the reconciliation grace period.");
|
|
176
|
+
});
|
|
177
|
+
// Orphan recovery can settle owners without returning a Run reconciliation.
|
|
178
|
+
// Surface every persisted owner, including macro-only `delivered` outcomes,
|
|
179
|
+
// so the button reports the changed entities and refreshes their snapshot.
|
|
180
|
+
for (const key of ["orphaned_owner_entities", "orphaned_execution_entities"]) {
|
|
181
|
+
for (const candidate of Array.isArray(records[key]) ? records[key] : []) {
|
|
182
|
+
if (typeof candidate !== "object" || candidate === null) continue;
|
|
183
|
+
const entity = candidate as Record<string, unknown>;
|
|
184
|
+
if (typeof entity.id !== "string" || !terminalStatuses.has(entity.status as string)) continue;
|
|
185
|
+
reconciled.add(entity.id);
|
|
186
|
+
if (entity.status === "blocked") remember(blocked, entity.id, entity.status_reason);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
128
189
|
const errors = Array.isArray(records.reconciliation_errors) ? records.reconciliation_errors : [];
|
|
129
190
|
errors.forEach((candidate) => {
|
|
130
191
|
if (typeof candidate !== "object" || candidate === null) return;
|
|
@@ -247,15 +247,21 @@ describe("dashboard backend", () => {
|
|
|
247
247
|
headers: { "x-zaip-session": "test-session" },
|
|
248
248
|
});
|
|
249
249
|
expect(response.status, await response.clone().text()).toBe(200);
|
|
250
|
-
await
|
|
251
|
-
|
|
252
|
-
|
|
250
|
+
const receipt = (await response.json()) as Record<string, unknown>;
|
|
251
|
+
expect(receipt).toMatchObject({ reconciliation: { reconciled_ids: ["TASK-RECONCILED"] } });
|
|
252
|
+
expect(Object.keys(receipt).sort()).toEqual([
|
|
253
|
+
"projection_refresh_completed",
|
|
254
|
+
"reconciliation",
|
|
255
|
+
"snapshot_state",
|
|
256
|
+
]);
|
|
253
257
|
expect(reconcile).toHaveBeenCalledOnce();
|
|
254
|
-
expect(reconcile).toHaveBeenCalledWith(canonicalFilesystem, workspaceRoot
|
|
258
|
+
expect(reconcile).toHaveBeenCalledWith(canonicalFilesystem, workspaceRoot, {
|
|
259
|
+
hostTerminalOnly: false,
|
|
260
|
+
});
|
|
255
261
|
expect(refresh).toHaveBeenCalledOnce();
|
|
256
262
|
const rejected = await app.request("/api/v2/reconcile-open-work", { method: "POST" });
|
|
257
263
|
expect(rejected.status).toBe(401);
|
|
258
|
-
}
|
|
264
|
+
});
|
|
259
265
|
it("rebuilds a stale immutable projection without reconciling lifecycle work", async () => {
|
|
260
266
|
const fixtureRoot = await mkdtemp(join(tmpdir(), "zaip-dashboard-stale-projection-"));
|
|
261
267
|
const taskIndexPath = join(
|